Foundation (phase 01, verified): - FastAPI app: /api/health, /api/suggestions, /api/chat (placeholder), static frontend served locally (no CDN) - Postgres 17 + pgvector via db/Containerfile + compose.yaml (podman compose up -d db), Alembic initial migration (documents, chunks with vector(768), query_log) - LLM client targeting https://aipi.reeseapps.com/v1 (turbo/embed); scripts/llm_probe.py verified models + 768-dim embeddings live - Conditional debugpy: imported only when DEBUGPY=1 (attach on demand, :5678); logging config for clean single-line logs - Frontend shell: mobile-first chat + Sources pages, tokens, a11y baselines - Tests: 24 unit+integration (99% coverage on app/), ruff + pyright clean, Playwright smoke E2E (3 tests) against a deterministic mock LLM - Planning: .agent/PLAN.md (architecture + LOCKED decisions), AGENTS.md, 6 user stories, 7 phase files (one story / one phase / one Playwright suite each)
3.5 KiB
3.5 KiB
Story: Responsive, Polished, Accessible UI
Phase: 07_story_responsive_polish.md · E2E: tests/e2e/test_responsive_polish.py
Narrative
As a user on any device — phone at the coffee shop, laptop at the desk — I want the chat to be comfortable to read and drive: no pinched layout, no tiny tap targets, no contrast failures, no wasted whitespace — so asking Brain feels effortless everywhere.
- Given any viewport from 360px to 1600px+
- When I use the chat and the Sources page
- Then the layout follows the PLAN §7 standards (containers, chat column, full-width table), all interactive elements are reachable by keyboard, and every color pair meets WCAG 2.1 AA.
Acceptance criteria
- Layout: container 72rem centered with side padding; chat column capped at 46rem centered; Sources table uses full container width with horizontal scroll below 640px (never a squeezed single hairline column).
- Mobile (375px): header condenses, composer reachable above the home
indicator (
safe-area-inset-bottom), chips scroll horizontally, bubbles ≤92% width, no horizontal page overflow (documentscrollWidth == clientWidth). - A11y sweep: landmarks present on both pages (
header/nav/main/ footer); skip link works (focus#main); all inputs have labels (visible or programmatically associated); all icon-only buttons havearia-label;:focus-visibleoutline on every control (Tab through). - Contrast: automated check of the key pairs (ink/surface, ink-soft/surface, white/brand, chip-ink/chip-bg, deflection pairs) ≥4.5:1 (test computes from computed styles; PLAN §7.2 table is the baseline).
- No-CDN re-verification on both pages (no
http(s)://src/href except same-origin/…). - Reduced motion: with
prefers-reduced-motion, typing dots and spinner do not animate (computedanimation: noneor duration ≥2s). - Long words/paths (e.g. a 60-char file path) wrap or ellipsize without breaking the bubble (overflow-wrap anywhere).
UI Visualization & Structure
- This phase is the visual audit + fix pass: it does not add features, it enforces PLAN §7 end-to-end on chat + sources.
- Desktop 1440px screenshot pass: header 64px, chat centered with balanced margins, sources table edge-to-edge within the container.
- Tablet 768px: chat column uses most of the width (≤46rem cap), no mid-column dead zones; stat cards 3-across.
- Phone 375px: one-column flow, 44px+ targets, thumb-zone composer.
- Any deviation found → fix in
frontend/assets/styles.css(tokens first), re-verify with the E2E below.
Playwright Mapping Rule
Test Scenario → tests/e2e/test_responsive_polish.py:
test_no_horizontal_overflow_at_viewports— for 360/375/768/1280/1600:document.documentElement.scrollWidth <= clientWidthon both pages.test_chat_column_capped_and_centered— at 1600px,.chat-shellwidth ≤ 46rem (736px) + 2% and horizontally centered (±2%).test_sources_table_full_width— at 1280px,.table-wrapwidth ≥ 80% of.containerwidth.test_a11y_landmarks_and_labels— both pages: landmarks present, skip link target#mainfocusable,#message-inputhas an associated label, no<img>/icon buttons without accessible name.test_contrast_pairs_pass_aa— computed-color contrast assertions for the PLAN §7.2 pairs (helper computes WCAG relative luminance).test_reduced_motion_respected— emulatereducedMotion: 'reduce'; typing dots have no running animation (or ≥2s duration).