diff --git a/apps/web/app/src/app/activation/activation.html b/apps/web/app/src/app/activation/activation.html index 73e8e0b..fe367c4 100644 --- a/apps/web/app/src/app/activation/activation.html +++ b/apps/web/app/src/app/activation/activation.html @@ -1,6 +1,8 @@
-

v1.0.2_stable

+

+ v1.0.2_stable +

Configure your API key, local workspace settings, and project seed prompt before entering the first projects @@ -69,7 +71,7 @@

{{ apiKey.label }}

@if (plaintextTokenFor(apiKey)) { -

+

{{ plaintextTokenFor(apiKey) }}

} @else { @@ -130,8 +132,10 @@ [attr.aria-selected]="selectedConfigTab() === 'themis'" aria-controls="config-tabpanel" class="cursor-pointer border-r border-zinc-950/10 px-5 py-3 font-mono text-xs text-zinc-500 focus:outline-none dark:border-white/10 dark:text-zinc-400" - [class.bg-white dark:bg-zinc-950]="selectedConfigTab() === 'themis'" - [class.text-accent]="selectedConfigTab() === 'themis'" + [class.bg-white]="selectedConfigTab() === 'themis'" + [class.dark:bg-zinc-950]="selectedConfigTab() === 'themis'" + [class.text-blue-600]="selectedConfigTab() === 'themis'" + [class.dark:text-blue-500]="selectedConfigTab() === 'themis'" (click)="selectConfigTab('themis')" > ~/.config/themis/core.json @@ -143,8 +147,10 @@ [attr.aria-selected]="selectedConfigTab() === 'opencode'" aria-controls="config-tabpanel" class="cursor-pointer border-r border-zinc-950/10 px-5 py-3 font-mono text-xs text-zinc-500 focus:outline-none dark:border-white/10 dark:text-zinc-400" - [class.bg-white dark:bg-zinc-950]="selectedConfigTab() === 'opencode'" - [class.text-accent]="selectedConfigTab() === 'opencode'" + [class.bg-white]="selectedConfigTab() === 'opencode'" + [class.dark:bg-zinc-950]="selectedConfigTab() === 'opencode'" + [class.text-blue-600]="selectedConfigTab() === 'opencode'" + [class.dark:text-blue-500]="selectedConfigTab() === 'opencode'" (click)="selectConfigTab('opencode')" > ~/.config/opencode/opencode.json @@ -156,8 +162,10 @@ [attr.aria-selected]="selectedConfigTab() === 'env'" aria-controls="config-tabpanel" class="cursor-pointer border-r border-zinc-950/10 px-5 py-3 font-mono text-xs text-zinc-500 focus:outline-none dark:border-white/10 dark:text-zinc-400" - [class.bg-white dark:bg-zinc-950]="selectedConfigTab() === 'env'" - [class.text-accent]="selectedConfigTab() === 'env'" + [class.bg-white]="selectedConfigTab() === 'env'" + [class.dark:bg-zinc-950]="selectedConfigTab() === 'env'" + [class.text-blue-600]="selectedConfigTab() === 'env'" + [class.dark:text-blue-500]="selectedConfigTab() === 'env'" (click)="selectConfigTab('env')" > .env.production diff --git a/apps/web/app/src/app/auth/forgotten-password/forgotten-password.html b/apps/web/app/src/app/auth/forgotten-password/forgotten-password.html index eb9e860..670b683 100644 --- a/apps/web/app/src/app/auth/forgotten-password/forgotten-password.html +++ b/apps/web/app/src/app/auth/forgotten-password/forgotten-password.html @@ -4,7 +4,7 @@

Recovery link sent @@ -32,7 +32,7 @@

Recover password @@ -71,6 +71,7 @@

Password updated @@ -28,7 +28,7 @@

Reset your password @@ -74,6 +74,7 @@

Sign in @@ -75,7 +75,13 @@ Remember this device. - Sign in diff --git a/apps/web/app/src/app/auth/sign-up/sign-up.html b/apps/web/app/src/app/auth/sign-up/sign-up.html index d62b161..129d67d 100644 --- a/apps/web/app/src/app/auth/sign-up/sign-up.html +++ b/apps/web/app/src/app/auth/sign-up/sign-up.html @@ -10,7 +10,7 @@

Create your account @@ -77,7 +77,13 @@ } - Create account diff --git a/apps/web/app/src/app/auth/verify-device/verify-device.html b/apps/web/app/src/app/auth/verify-device/verify-device.html index 7ffa541..3304da9 100644 --- a/apps/web/app/src/app/auth/verify-device/verify-device.html +++ b/apps/web/app/src/app/auth/verify-device/verify-device.html @@ -10,7 +10,7 @@

Verify device diff --git a/apps/web/app/src/app/auth/verify-email/verify-email.html b/apps/web/app/src/app/auth/verify-email/verify-email.html index a989f79..2675e3b 100644 --- a/apps/web/app/src/app/auth/verify-email/verify-email.html +++ b/apps/web/app/src/app/auth/verify-email/verify-email.html @@ -10,7 +10,7 @@

Verify email diff --git a/apps/web/app/src/app/shared/layout/logo/logo.html b/apps/web/app/src/app/shared/layout/logo/logo.html index 16a810d..d2f0961 100644 --- a/apps/web/app/src/app/shared/layout/logo/logo.html +++ b/apps/web/app/src/app/shared/layout/logo/logo.html @@ -29,7 +29,7 @@ } @if (variant() === 'wordmark' || variant() === 'mark-name') { - Themis + Themis } @if (variant() === 'wordmark') { diff --git a/apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts b/apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts index 5c7a412..9fd6d6c 100644 --- a/apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts +++ b/apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts @@ -18,7 +18,7 @@ export class AuthCard { readonly classes = computed(() => uiClass( - 'mx-auto w-full max-w-[27.5rem] rounded-[var(--radius-panel)] border border-zinc-950/10 dark:border-white/10 px-5 py-6 shadow-sm sm:px-8 sm:py-8 md:px-10 md:py-10', + 'mx-auto w-full max-w-[27.5rem] rounded-[var(--radius-panel)] border border-zinc-950/10 dark:border-white/10 px-6 py-6 shadow-sm sm:px-8 sm:py-8 md:px-10 md:py-10', this.tone() === 'raised' ? 'bg-zinc-100 dark:bg-zinc-800' : 'bg-zinc-50 dark:bg-zinc-900', ), ); diff --git a/apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html b/apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html index 501466d..97a81ac 100644 --- a/apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html +++ b/apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html @@ -1,12 +1,12 @@
-
+
diff --git a/apps/web/app/version.json b/apps/web/app/version.json index a15e91f..08df5a2 100644 --- a/apps/web/app/version.json +++ b/apps/web/app/version.json @@ -1,3 +1,3 @@ { - "version": "1.2.0" + "version": "1.5.0" } diff --git a/docs/constitution/roadmap.md b/docs/constitution/roadmap.md index 1f56485..2fdfed4 100644 --- a/docs/constitution/roadmap.md +++ b/docs/constitution/roadmap.md @@ -84,3 +84,18 @@ Replace the vendored Open Design prototypes and the inherited design-system skil - See spec: [`docs/specs/2026-06-26-ui-designer-app/`](./specs/2026-06-26-ui-designer-app/) - Branch: `feat/OC/ui-designer-app` - Version target: `1.4.0` + +## Post-Refactor UI Review + +Audit and polish the surfaces left inconsistent by the Catalyst utility-first refactor series (Catalyst Angular foundation, pure tokens alignment, site utility-first migration, UI designer app). The review follows the `web-design-reviewer` workflow: capture a baseline screenshot grid and an auth flow recording, audit visual drift at the source, apply focused fixes, re-capture, and ship the recordings as evidence. Concrete items in scope: replace the non-canonical `font-display` utility with `font-heading` across the auth routes, brand wordmark, and recipes doc; collapse duplicate background utilities in the `app-auth-layout` sticky header; replace the magic `min-h-[calc(100vh-64px)]` with `min-h-dvh`; tighten the `app-auth-card` mobile padding to a 24px outer floor; add `data-od-id` chrome hooks for visual e2e suites; add `scripts/capture-ui-snapshots.cjs` to drive the snapshot matrix; regenerate the auth flow recordings. No new tokens, no new primitives, no redesign. Out of scope for this spec: a `DESIGN.md` manuscript realignment (already documented as follow-up in the site spec), automated visual regression in CI, and any backend changes. + +- See spec: [`docs/specs/2026-06-27-post-refactor-ui-review/`](./specs/2026-06-27-post-refactor-ui-review/) +- Branch: `feat/OC/post-refactor-ui-review` +- Version target: `1.5.0` + +Slice plan: + +- [ ] PR1: replace `font-display` with `font-heading` in the auth routes, the brand wordmark, and `recipes.md`. +- [ ] PR2: tighten the `app-auth-layout` sticky header (drop duplicate utilities, responsive height, sticky on mobile). +- [ ] PR3: tighten `app-auth-card` mobile padding to `px-6 py-6` floor and add `data-od-id="submit"` to every auth route's primary CTA. +- [ ] PR4: add `scripts/capture-ui-snapshots.cjs`, regenerate `media/auth-flow-videos/*.webm`, bump version to `1.5.0`, update the roadmap. diff --git a/docs/specs/2026-06-27-post-refactor-ui-review/plan.md b/docs/specs/2026-06-27-post-refactor-ui-review/plan.md new file mode 100644 index 0000000..e891d73 --- /dev/null +++ b/docs/specs/2026-06-27-post-refactor-ui-review/plan.md @@ -0,0 +1,121 @@ +# Post-Refactor UI Review — Implementation Plan + +The work is split into four PRs. Each PR is independently reviewable and verifiable. The capture scripts + recordings (PR4) can land last so the visual evidence matches the source state. + +## PR1 — Auth heading utility alignment + +Replace `font-display` with `font-heading` across the auth surface, the brand wordmark, and the recipes doc. This is the smallest possible PR that fixes the most visible contract drift. + +### Tasks + +1. Edit `apps/web/app/src/app/auth/sign-in/sign-in.html`: replace `font-display` with `font-heading` on the `

` (line 13). +2. Same edit in: + - `apps/web/app/src/app/auth/sign-up/sign-up.html` (line 13) + - `apps/web/app/src/app/auth/verify-email/verify-email.html` (line 13) + - `apps/web/app/src/app/auth/verify-device/verify-device.html` (line 13) + - `apps/web/app/src/app/auth/forgotten-password/forgotten-password.html` (lines 7 and 35) + - `apps/web/app/src/app/auth/reset-password/reset-password.html` (lines 6 and 31) +3. Edit `apps/web/app/src/app/shared/layout/logo/logo.html`: replace `font-display` with `font-heading` on the wordmark (line 32). +4. Edit `docs/design-system/recipes.md`: update the three `font-display` references in the auth-shell recipe (lines 20, 26, 43) to `font-heading`. +5. Run `rg "font-display" apps/web app docs/design-system` to confirm zero matches. +6. Run `pnpm nx run app:lint`, `pnpm nx run app:typecheck`, `pnpm nx run app:vite:test`. + +### Acceptance + +- `rg "font-display" apps/web app docs/design-system` returns zero matches. +- `pnpm nx run app:lint`, `pnpm nx run app:typecheck`, `pnpm nx run app:vite:test` all pass. +- The existing auth e2e specs (`apps/web/app-e2e/src/auth/*`) stay green. + +## PR2 — `app-auth-layout` shell tightening + +Drop the duplicate background utilities in the sticky header, replace the magic `min-h-[calc(100vh-64px)]` with `min-h-dvh`, and add the missing `data-od-id="brand"` hook. + +### Tasks + +1. Edit `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html`: + - Drop `sm:bg-white` from the sticky header class list (collapse `bg-white sm:bg-white` to `bg-white`). + - Collapse `dark:bg-zinc-950 dark:bg-zinc-950/85` to `dark:bg-zinc-950/85` (keep the translucent backdrop; drop the opaque duplicate). + - Replace `relative border-b ... sm:relative sm:sticky sm:top-0 sm:z-20 ...` with `sticky top-0 z-20 border-b ...` so the sticky behavior is on at every breakpoint (currently the layout is only sticky at `sm+`, leaving the chrome to scroll away on mobile which is jarring). + - Add `data-od-id="brand"` to the brand link. + - Replace `font-display` with `font-heading` on the brand link. + - Replace `min-h-[calc(100vh-64px)]` with `min-h-dvh` on `
`. +2. Run `rg "sm:bg-white|dark:bg-zinc-950 dark:bg-zinc-950|min-h-\[calc\(100vh-64px\)\]" apps/web/app/src/app/shared/ui/layout` to confirm zero matches. +3. Run `pnpm nx run app:lint`, `pnpm nx run app:typecheck`, `pnpm nx run app:vite:test`. +4. Run the existing site-e2e and app-e2e suites that touch the auth routes (or, if the gateway is not available, run unit tests only and note the manual visual check in the PR description). + +### Acceptance + +- Static guards pass (zero matches). +- Unit tests pass. +- Manual visual check at 375px and 1280px confirms the auth card still centers vertically and the sticky header remains visible after scroll. + +## PR3 — Auth card mobile padding + submit hook + +Tighten the `app-auth-card` mobile padding so the smallest viewports (360px) keep a safe gutter, and add a stable `data-od-id="submit"` hook to each auth route's primary button. + +### Tasks + +1. Edit `apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts`: change the padding scale from `px-5 py-6 sm:px-8 sm:py-8 md:px-10 md:py-10` to `px-6 py-6 sm:px-8 sm:py-8 md:px-10 md:py-10`. The `px-5` floor becomes `px-6` to match the design-system mobile-first baseline. +2. Edit `apps/web/app/src/app/auth/sign-in/sign-in.html`: add `data-od-id="submit"` to the submit `` (alongside the existing `data-slot="submit"`). +3. Same edit in: + - `apps/web/app/src/app/auth/sign-up/sign-up.html` (line 80) + - `apps/web/app/src/app/auth/forgotten-password/forgotten-password.html` (line 73) + - `apps/web/app/src/app/auth/reset-password/reset-password.html` (two buttons: Verify code on line 76 and Update password on line 120) +4. Run the lint + typecheck + test targets again. +5. Run the existing auth e2e specs. + +### Acceptance + +- Static guard: `rg "data-od-id=\"submit\"" apps/web/app/src/app/auth` returns the expected count (sign-in: 1, sign-up: 1, verify-email: 0, verify-device: 0, forgotten-password: 1, reset-password: 2). +- Unit tests + auth e2e specs pass. + +## PR4 — Capture scripts + recordings + +Add the structured snapshot script, update the auth-flow README, and regenerate the auth flow recordings. This PR depends on PR1 + PR2 + PR3 being merged. + +### Tasks + +1. Author `scripts/capture-ui-snapshots.cjs`: + - Site routes: `/en/`, `/es/`, `/docs/` at viewports `375`, `768`, `1280`, light + dark. Output to `media/ui-snapshots/site---.png`. + - Auth routes: `/app/en/sign-in`, `/app/en/sign-up`, `/app/en/forgotten-password`, `/app/en/verify-email`, `/app/en/verify-device`, `/app/en/reset-password`. Reuse the viewport matrix from `scripts/snapshot-auth-phase10.cjs` (360/390/520/768/1280). Output to `media/ui-snapshots/auth---.png`. + - The script detects whether the gateway at `${BASE_URL:-http://localhost:8082}` (or `http://localhost:8081` for auth) is reachable. If unreachable, it logs a clear message and skips that half; it does not crash the other half. + - The script uses `waitUntil: 'domcontentloaded'` plus a 400ms settle timeout so SSR-rendered HTML is captured correctly. +2. Author `media/ui-snapshots/README.md` describing the directory layout and the script. +3. Update `media/auth-flow-videos/README.md` to mention the new snapshot script as the companion artifact. +4. Regenerate `media/auth-flow-videos/auth-flow-iphone-13-mini.webm` and `media/auth-flow-videos/auth-flow-hd-1920x1080.webm` via `node scripts/capture-auth-flow.cjs` once the gateway is up. +5. Bump `apps/web/app/version.json` from `1.2.0` to `1.5.0`. +6. Edit `docs/constitution/roadmap.md`: add a "## Post-Refactor UI Review" entry pointing at `docs/specs/2026-06-27-post-refactor-ui-review/`. + +### Acceptance + +- `scripts/capture-ui-snapshots.cjs` runs without uncaught errors and produces the expected files in `media/ui-snapshots/`. +- `media/ui-snapshots/README.md` documents the artifact. +- `media/auth-flow-videos/auth-flow-iphone-13-mini.webm` and `media/auth-flow-videos/auth-flow-hd-1920x1080.webm` are present and playable. +- `apps/web/app/version.json` is `1.5.0`. +- `docs/constitution/roadmap.md` lists the new phase. + +## Cross-PR Verification + +After all four PRs land: + +```bash +pnpm nx run-many -t lint,typecheck --projects=app,site,ui-designer +pnpm nx run app:vite:test +pnpm nx run app:build --skip-nx-cache +pnpm nx run site:build --skip-nx-cache +pnpm nx run ui-designer:build --skip-nx-cache + +# Visual evidence +pnpm nx serve site & +sleep 3 +node scripts/capture-ui-snapshots.cjs + +# Gateway boot (skip if Redis is not reachable in this environment) +pnpm exec nx run-many -t build --projects server,realtime,worker,api,app,site --configuration production +node dist/apps/web/server/main.js & +sleep 6 +node scripts/capture-ui-snapshots.cjs # auth half +node scripts/capture-auth-flow.cjs # recordings +``` + +Manual review: walk the audit checklist in `sdd.md`, tick each row when the corresponding screenshot matches the expected post-fix state. Record any remaining issues as P3 follow-ups for the next spec. diff --git a/docs/specs/2026-06-27-post-refactor-ui-review/requirements.md b/docs/specs/2026-06-27-post-refactor-ui-review/requirements.md new file mode 100644 index 0000000..450501a --- /dev/null +++ b/docs/specs/2026-06-27-post-refactor-ui-review/requirements.md @@ -0,0 +1,108 @@ +# Post-Refactor UI Review — Requirements + +## Functional Requirements + +### FR-1 — Heading utility alignment + +Every heading in the auth surface and the brand wordmark uses the canonical `font-heading` utility. The non-canonical `font-display` alias does not appear in source. + +- **Where:** `apps/web/app/src/app/auth/**/*.html`, `apps/web/app/src/app/shared/layout/logo/logo.html`, `docs/design-system/recipes.md`. +- **Acceptance:** `rg "font-display" apps/web app docs/design-system` returns zero matches. +- **Verification:** visual diff of sign-in/sign-up/verify-email/verify-device/forgotten-password/reset-password at 375px and 1280px confirms the heading family matches the ui-designer seed prototype (`apps/web/ui-designer/src/prototypes/app-auth-shell.html`). + +### FR-2 — `app-auth-layout` sticky header + +The auth shell sticky header carries exactly one background utility per mode. The duplicate `bg-white sm:bg-white` and `dark:bg-zinc-950 dark:bg-zinc-950/85` collapse to `bg-white/85` and `dark:bg-zinc-950/85`. + +- **Where:** `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html`. +- **Acceptance:** `rg "sm:bg-white|dark:bg-zinc-950 dark:bg-zinc-950" apps/web/app/src/app/shared/ui/layout` returns zero matches. +- **Verification:** rendered sticky header has a translucent backdrop blur in both light and dark modes; the opaque value does not flash in. + +### FR-3 — `app-auth-layout` responsive height + +The auth shell main area uses `min-h-dvh` instead of a magic `min-h-[calc(100vh-64px)]` so the card centers regardless of header height. + +- **Where:** `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html`. +- **Acceptance:** `rg "min-h-\[calc\(100vh-64px\)\]" apps/web/app/src/app/shared/ui/layout` returns zero matches. +- **Verification:** auth card centers vertically at 360px, 375px, 768px, and 1280px; no clipping when the language menu opens at desktop. + +### FR-4 — Auth card mobile padding + +The auth card padding scale starts at `px-6 py-6` on the smallest viewports. The `px-5` floor is replaced with `px-6`. + +- **Where:** `apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts`. +- **Acceptance:** the `px-5` literal does not appear in `auth-card.ts`; the scale reads `px-6 py-6 sm:px-8 sm:py-8 md:px-10 md:py-10`. +- **Verification:** rendered card at 360px viewport keeps a 24px outer gutter; no clipping of the heading or the submit button. + +### FR-5 — Visual e2e chrome hooks + +Every auth route exposes `data-od-id="submit"` on its primary submit button. The brand link exposes `data-od-id="brand"`. + +- **Where:** `apps/web/app/src/app/auth/sign-in/sign-in.html`, `apps/web/app/src/app/auth/sign-up/sign-up.html`, `apps/web/app/src/app/auth/forgotten-password/forgotten-password.html`, `apps/web/app/src/app/auth/reset-password/reset-password.html` (two buttons), `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html`. +- **Acceptance:** `rg "data-od-id=\"submit\"" apps/web/app/src/app/auth` returns 5 matches (sign-in: 1, sign-up: 1, forgotten-password: 1, reset-password: 2). `rg "data-od-id=\"brand\"" apps/web/app/src/app/shared/ui/layout` returns 1 match. +- **Verification:** snapshot script locates every submit button without falling back to `getByRole`. + +### FR-6 — Capture scripts and recordings + +A `scripts/capture-ui-snapshots.cjs` script exists and produces a structured snapshot grid for the site routes (`/en/`, `/es/`, `/docs/`) and the auth routes. The existing `scripts/capture-auth-flow.cjs` script regenerates the auth flow recordings. + +- **Where:** `scripts/capture-ui-snapshots.cjs`, `media/ui-snapshots/`, `media/auth-flow-videos/`. +- **Acceptance:** the snapshot script writes one PNG per (route x viewport x theme) to `media/ui-snapshots/`. The auth flow recordings are present at `media/auth-flow-videos/auth-flow-iphone-13-mini.webm` and `media/auth-flow-videos/auth-flow-hd-1920x1080.webm`. +- **Verification:** the script handles a missing gateway gracefully (skips the auth half with a clear log). + +### FR-7 — Documentation accuracy + +The auth shell recipe in `docs/design-system/recipes.md` matches the rendered Angular app. + +- **Where:** `docs/design-system/recipes.md`. +- **Acceptance:** the recipe uses `font-heading`, `min-h-dvh`, `data-od-id="brand"`, `data-od-id="submit"`, and the `px-6 py-6` mobile padding floor. +- **Verification:** line-by-line diff of the recipe against `apps/web/app/src/app/auth/sign-in/sign-in.html` and `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html` confirms parity. + +### FR-8 — Roadmap entry + +`docs/constitution/roadmap.md` lists this spec under a new "Post-Refactor UI Review" entry. + +- **Where:** `docs/constitution/roadmap.md`. +- **Acceptance:** the section exists and points at `docs/specs/2026-06-27-post-refactor-ui-review/`. +- **Verification:** `rg "Post-Refactor UI Review" docs/constitution/roadmap.md` returns one match. + +## Non-Functional Requirements + +### NFR-1 — Accessibility + +- WCAG AA contrast holds for every text/background pair after the fixes: + - `zinc-950` on `white/85` (light header) ≥ 4.5:1 for body text. + - `zinc-50` on `zinc-950/85` (dark header) ≥ 4.5:1 for body text. + - `zinc-950` on `zinc-50` (light card surface) ≥ 4.5:1. + - `zinc-50` on `zinc-900` (dark card surface) ≥ 4.5:1. +- Every interactive element keeps its focus ring (the `ui-focus-ring` utility still resolves to `var(--color-blue-500)` in dark mode and `var(--color-blue-600)` in light mode). +- Touch targets remain at least 44px (`ui-touch-target` is unchanged). +- `prefers-reduced-motion` continues to be honored through the existing `@media (prefers-reduced-motion: reduce)` block in `styles.base.css`. + +### NFR-2 — Mobile-first + +- All spacing scales start at the smallest viewport and grow at `sm:` / `md:` / `lg:`. +- The auth card padding starts at `px-6 py-6` (24px gutter) on the smallest viewport, matching the design-system baseline. +- The sticky header is sticky at every breakpoint (not just `sm+`), so the chrome does not disappear on mobile after scrolling. + +### NFR-3 — Performance + +- No new CSS bundle weight. The duplicate utilities in the sticky header collapse to a single utility each; the rendered class list is shorter. +- No new JS dependencies. Playwright is already a workspace dev dependency. + +### NFR-4 — Internationalization + +- No new copy. The existing i18n markers in the auth templates are unchanged. +- `pnpm nx run app:extract-i18n` continues to produce the same `messages.es.xlf` (no new strings, no removed strings). + +### NFR-5 — Tenant isolation + +- Not applicable. This spec touches presentation layer only. + +## Out of Scope + +- No redesign of any auth route copy, layout, or section order. +- No new design tokens, no new primitives, no new shared/ui components. +- No changes to the backend, the API, or the worker. +- No introduction of automated visual regression tests in CI. +- No migration of `DESIGN.md` (documented as out of scope in the previous site spec). diff --git a/docs/specs/2026-06-27-post-refactor-ui-review/sdd.md b/docs/specs/2026-06-27-post-refactor-ui-review/sdd.md new file mode 100644 index 0000000..b4d2250 --- /dev/null +++ b/docs/specs/2026-06-27-post-refactor-ui-review/sdd.md @@ -0,0 +1,263 @@ +# Post-Refactor UI Review — Software Design Document + +## Decision + +Run a **design polish + mobile-first UX/UI review** pass on top of the recent utility-first and Catalyst refactor (`2026-06-22-catalyst-angular-ui-foundation/`, `2026-06-23-catalyst-pure-tokens-alignment/`, `2026-06-26-site-utility-first-migration/`, `2026-06-26-ui-designer-app/`). The review follows the [`web-design-reviewer`](../../../.opencode/skills/web-design-reviewer/SKILL.md) workflow: capture a baseline of screenshots and an auth flow recording, audit visual issues at the source, apply focused fixes, re-capture, and ship the recordings as evidence. + +The scope is intentionally narrow: + +1. Replace the non-canonical `font-display` utility across the auth routes with the canonical `font-heading` utility declared in `styles.base.css` and documented in `tokens.md` / `recipes.md`. +2. Remove duplicate background utilities in the `app-auth-layout` sticky header and tighten the `min-h-[calc(100vh-64px)]` so the auth card clears the header at every viewport. +3. Tighten the `app-auth-card` mobile padding so the smallest viewports (360px) keep a safe 16px outer gutter. +4. Add a `data-od-id` hook to the brand link and to each auth route's submit button so visual e2e suites can target the chrome without depending on `getByRole`. +5. Add `scripts/capture-ui-snapshots.cjs` to capture a structured before/after snapshot grid for the site (`/en/`, `/es/`, `/docs/`) and the auth routes at the Phase 10 viewport matrix (360/390/520/768/1280) x light/dark. +6. Regenerate the existing `media/auth-flow-videos/auth-flow-{iphone-13-mini,hd-1920x1080}.webm` recordings through the unchanged `scripts/capture-auth-flow.cjs` flow. + +No new tokens, no new primitives, no redesign of routes or sections. The fix is structural alignment with the already-shipped token foundation. + +## Why now + +The previous refactor series standardized the token layer and the visual primitives but left a few surfaces inconsistent with the documented contract: + +| Surface | Current state | Documented contract (tokens.md / recipes.md) | +| --------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| 10 `font-display` usages (auth + brand) | `font-display` (non-canonical alias, no Tailwind utility) | `font-heading` (canonical, wired through `--font-heading`) | +| `app-auth-layout` sticky header | `bg-white sm:bg-white`, `dark:bg-zinc-950 dark:bg-zinc-950/85` (duplicates) | one background per mode, gradient-free | +| `app-auth-layout` main height | `min-h-[calc(100vh-64px)]` (assumes fixed 64px header) | responsive header height via flex layout | +| `app-auth-card` mobile padding | `px-5 py-6` on 360px viewports (15px gutters inside the 16px page gutter) | at least `p-4` page gutter, `px-6 py-6` card minimum | +| `data-od-id` chrome hooks | only on `auth-shell`, `auth-card`, `pending-email`; missing on brand + submit btn | brand, lang, theme, submit, footer | +| Auth flow + UI snapshots | manual, ad-hoc, no shared script for site | shared `scripts/capture-*.cjs` artifact | + +The site and the ui-designer prototype already follow the canonical contract. The Angular app's auth surface drifted because the migration series updated tokens and primitives first and left the route templates untouched. This spec closes the drift with the smallest correct change set. + +## Goals + +1. Every text style that the design system documents as a heading utility uses the canonical name (`font-heading`). `font-display` no longer appears in `apps/web/app/src/**` or `apps/web/ui-designer/src/**`. +2. The `app-auth-layout` sticky header has exactly one background utility per mode (no duplicates, no gradient). +3. The `app-auth-layout` main area clears the responsive header height at every viewport without a magic constant. +4. The `app-auth-card` keeps a 16px outer gutter at the 360px viewport (matches the design system mobile-first baseline). +5. Every auth route exposes a stable `data-od-id` hook on the submit button and the brand link, in addition to the existing `auth-shell` and `auth-card` hooks. +6. `scripts/capture-ui-snapshots.cjs` exists and produces a structured snapshot grid for the site and the auth routes (one image per route x viewport x theme). +7. `media/auth-flow-videos/auth-flow-iphone-13-mini.webm` and `media/auth-flow-videos/auth-flow-hd-1920x1080.webm` are regenerated and load correctly. + +## Non-Goals + +1. No redesign of any auth route copy, layout, or section order. +2. No new design tokens, no new primitives, no new shared/ui components. +3. No changes to the backend, the API, or the worker. +4. No introduction of new e2e specs. The site smoke spec and the existing auth route specs stay unchanged. +5. No introduction of automated visual regression tests. The snapshot grid is a manual artifact, committed under `media/ui-snapshots/`, not asserted in CI. +6. No migration of the `DESIGN.md` manuscript. The brand-level manuscript is documented as out of scope in `2026-06-26-site-utility-first-migration/sdd.md`. +7. No new dependency. Playwright is already a workspace dev dependency. + +## Audit Findings (Baseline) + +The audit walked the same six surfaces the screenshot script will capture. Findings are grouped by severity. Only the `P1` and `P2` items are in scope for this spec; `P3` items are documented as follow-up. + +### P1 — Visual contract drift + +| ID | Surface | Issue | +| ---- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P1-1 | `apps/web/app/src/app/auth/sign-in/sign-in.html:13` | `font-display` on the page `

`. The canonical heading utility is `font-heading` per `tokens.md` and `recipes.md`. | +| P1-2 | `apps/web/app/src/app/auth/sign-up/sign-up.html:13` | Same as P1-1. | +| P1-3 | `apps/web/app/src/app/auth/verify-email/verify-email.html:13` | Same as P1-1. | +| P1-4 | `apps/web/app/src/app/auth/verify-device/verify-device.html:13` | Same as P1-1. | +| P1-5 | `apps/web/app/src/app/auth/forgotten-password/forgotten-password.html:7` | Same as P1-1, plus `:35` (the success state also has its own `

`). | +| P1-6 | `apps/web/app/src/app/auth/reset-password/reset-password.html:6,31` | Same as P1-1 (two `

` instances for the success state and the form state). | +| P1-7 | `apps/web/app/src/app/shared/layout/logo/logo.html:32` | `font-display` on the wordmark. Brand uses the same heading family everywhere else. | +| P1-8 | `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html:3` | Sticky header carries `bg-white sm:bg-white` and `dark:bg-zinc-950 dark:bg-zinc-950/85`. Duplicate utilities resolve to the same value and inflate the generated class list; the `bg-white` (without opacity) is the intentional choice for the chrome. | +| P1-9 | `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html:23` | `min-h-[calc(100vh-64px)]` hard-codes a 64px header height. On mobile (375px) the header collapses to `py-2.5` (about 52px), so the main area leaves a visible gap; on desktop with the language menu open it can grow taller than 64px and clip the card. | + +### P2 — Mobile polish + +| ID | Surface | Issue | +| ---- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P2-1 | `apps/web/app/src/app/shared/ui/layout/auth-card/auth-card.ts:21-22` | On a 360px viewport the page gutter is `px-4` (16px) and the card starts at `px-5 py-6` (20px). Tight on small phones; tighten to `px-6` floor on the smallest viewports. | +| P2-2 | `apps/web/app/src/app/shared/ui/layout/auth-layout/auth-layout.html:6` | The brand link has `routerLink="/"` but the `app-logo` mark-name already carries the brand mark. The `routerLink` is harmless but the `data-od-id="brand"` hook is missing. | +| P2-3 | `apps/web/app/src/app/auth/sign-in/sign-in.html:78-80` | `app-button` submit is the only primary CTA without a `data-od-id` hook. Visual e2e snapshots cannot target it from the chrome. | +| P2-4 | `apps/web/app/src/app/auth/sign-up/sign-up.html:80-82` | Same as P2-3. | +| P2-5 | `apps/web/app/src/app/auth/forgotten-password/forgotten-password.html:73-80` | Same as P2-3. | +| P2-6 | `apps/web/app/src/app/auth/reset-password/reset-password.html:76-83,120-127` | Same as P2-3 (two submit buttons: Verify code + Update password). | +| P2-7 | `docs/design-system/recipes.md:20,26,43` | The auth-shell recipe in `recipes.md` uses `font-display`. Update to `font-heading` so the example matches the Angular app after the fix. | +| P2-8 | `apps/web/site/src/components/landing-page.astro:145` | Sticky `