14 KiB
Phase 17: UI Optimization & Polish - Context
Gathered: 2026-06-17 Status: Ready for planning
## Phase BoundaryA visual-identity & polish pass for the PWA, spanning four bounded workstreams (A–C from this discussion; D added from Phase 19 UAT, see Decisions §D):
- A — Phone-layout polish. Fix the long-standing phone (≤767px) fixed-chrome overlap where the
position: fixedBottomTabBar covers the New Event FAB (FAB lands on the Admin tab) and occludes the bottom of the calendar + the colour-legend chips, then sweep other small-viewport spacing / tap-target / overflow issues. CSS/layout only, no behaviour change. - B — Branding assets. Generate a real FamilySync logo and drop it into the already-built
BrandSlotseam (apps/pwa/src/components/BrandSlot.tsx), and produce a complete favicon / PWA-icon set to replace the placeholder stubs inapps/pwa/public/(icon-192.png699 B,icon-512.png,apple-touch-icon.png617 B — all generated stubs; there is currently nofavicon.ico/favicon.svgand index.html links only the apple-touch icon). - C — Theme-token groundwork. Restructure
apps/pwa/src/styles/tokens.cssfrom its single light:rootinto a themeable semantic-token layer (swappable bydata-theme/prefers-color-scheme). Light stays the only shipped theme — this is enabling groundwork only. - D — UAT-surfaced UI fixes (from Phase 19 live UAT). Wire a logout control to the existing
fetchLocalLogout()(no backend), add success feedback to admin create/reset-password flows, fix dialog/popup bottom-center positioning, and rework the clunky admin navigation. See Decisions §D (D-07…D-10).
Out of scope (explicitly deferred this discussion, 2026-06-17):
- Shipped dark theme + light/dark/system toggle → backlog 999.20 (Phase 17's token groundwork is the enabling seam).
- Broader "modern styling" visual refresh (contemporary restyle of login/calendar/event-form/lists/admin) → backlog 999.21, flagged for a future milestone — a redesign track, not a polish phase.
This stays a focused polish + branding + groundwork pass, not a redesign.
## Implementation DecisionsA — Phone-layout polish
- D-01: Fix the seed defect (BottomTabBar overlapping the FAB + colour legend) AND run a bounded small-viewport sweep — the Phase 7
layout.spec.tsassertions (tap targets ≥44px, no horizontal overflow, critical elements in-viewport, accessible names) are the checklist; fix what they flag across phone routes. Bounded and checklist-driven, not a free-form audit. - D-02: Fix technique and regression-guard mechanism are the researcher's call (deferred from discussion). Inputs the researcher must weigh: the ROADMAP/todo fix sketch (lift FAB to
bottom: calc(56px + env(safe-area-inset-bottom,0px) + var(--space-6))+ matching contentpadding-bottom, OR shrink the100dvhcolumn by the bar height) vs. a single shared--bottom-chrome-htoken consumed by both the FAB offset and the content padding (single source of truth). Researcher also decides whether to add a permanent overlap assertion tolayout.spec.ts(FAB/legend must not intersect the BottomTabBar rect on phone profiles) vs. playwright-cli manual verification only. Default lean if evidence is neutral: shared token + add the CI assertion (hardest to regress), but this is the researcher's decision to make on the merits.
B — Branding assets
- D-03: Logo + full icon set are AI-generated in-phase (option 2a). Claude generates the logo and the complete icon/favicon set from the brief below, wires them in (BrandSlot
<img>,apps/pwa/public/files,index.html<link>s, and thevite-plugin-pwamanifesticons[]inapps/pwa/vite.config.ts), and the user approves the result before it's final. No external designer / user-supplied art. - D-04: Deliver a complete icon set, not just a logo:
favicon.ico+favicon.svg,icon-192.png,icon-512.png, a proper maskable 512 (the current manifest reuses the non-maskable 512 as maskable — a real maskable needs safe-zone padding), andapple-touch-icon.png(180×180). Updateindex.html(add the missing<link rel="icon">s;theme-colorcurrently#4A90D9) and thevite.config.tsmanifest to reference them. - D-05: Drive the logo through the existing
BrandSlotseam contract — swap the placeholder<div>for an<img>and override the--brand-logo-*tokens — without changingLoginPagelayout (the seam was built in Phase 19 precisely to isolate this). Keep the accessibility shape:<h1>carries the app name, logo image is decorative (alt=""/aria-hidden), no layout shift.
C — Theme-token groundwork
- D-06: Restructure
tokens.cssinto a themeable layer (semantic tokens resolvable per theme viadata-theme/prefers-color-scheme) — groundwork only (option 3a). Do NOT author dark palette values, wireprefers-color-schemeto actually flip, or add a toggle this phase. Light remains the sole shipped theme. Keep the existing invariant: no hard-coded hex/px in component files — all values stay intokens.css. The restructure must leave the Schedule-X--sx-color-*overrides (bottom of tokens.css) working unchanged.
D — UAT-surfaced UI findings (from Phase 19 live UAT, 2026-06-17)
Surfaced by the operator during the Phase 19 local-auth UAT and routed here by operator decision — these are UI concerns, not Phase 19 auth blockers. Phase 19 ships functionally complete; Phase 17 owns the UI. (Recorded in .planning/phases/19-local-auth-no-oidc-mode/19-UAT.md as F-01…F-04.)
- D-07 (F-02) — Wire a logout control into the UI. Logout is fully plumbed but unreachable: the endpoint
POST/GET /api/auth/local/logoutworks (200, clears cookie — BL-02 verified live) andfetchLocalLogout()exists atapps/pwa/src/api/client.ts:127, but no component calls it (zero logout buttons inapps/pwa/src). Add a logout control (likely inSettingsSheet.tsxand/orAppNav) that calls the existing client function + redirects to/login. No backend work — UI wiring only. (Slightly beyond pure "polish" — it's a small new control; planner should size it.) - D-08 (F-01) — Add success feedback to admin local-account actions. Admin create-member and reset-password both succeed (verified at DB/login level in UAT) but show no success toast/confirmation, leaving the operator unsure it worked. Add success feedback to those admin flows (
AdminPage.tsx). - D-09 (F-03) — Fix dialog/popup positioning. Popups/sheets render bottom-center instead of properly centered. Fits the fixed-chrome/sheet-positioning sweep already in workstream A; likely the same dialog/sheet CSS (
SettingsSheet.tsxand shared dialog styles). Verify across phone + desktop via playwright-cli. - D-10 (F-04) — Rework the clunky admin navigation. Admin UI navigation reads as clunky and needs a rework. Larger UX item than the others — planner should decide whether it fits this phase's "polish" budget or warrants its own slice. (
AdminPage.tsx, admin nav/tab surface.)
Claude's Discretion
- D-02 (fix technique + regression guard) is explicitly delegated to the researcher/planner.
- Exact small-viewport issues surfaced by the
layout.specsweep (D-01) — fix as found, within the no-behaviour-change boundary. - Logo visual execution within the brand brief (see Specific Ideas) — subject to user approval at the checkpoint.
Folded Todos
2026-06-13-pwa-phone-bottombar-overlap.md(area: pwa-ui,resolves_phase: 17) — the phase's seed defect. Phone-layout fixed BottomTabBar overlaps the New Event FAB + colour legend at ≤767px. Reproduced 2026-06-13 via playwright-cli at 390×844 (FAB over Admin tab; "Dev User" legend clipped) vs 1280×800 (no overlap). Long-standing (BottomTabBar dates to Phase 04), not a Phase 10 regression. This is workstream A's anchor — itsfiles:list (App.tsx,BottomTabBar.tsx,CalendarShell.tsx) is the fix surface.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Phase scope & seed defect
.planning/ROADMAP.md§"Phase 17: UI Optimization & Polish" — goal, three-workstream scope, scope boundary, seed-defect detail + CSS fix sketch..planning/todos/pending/2026-06-13-pwa-phone-bottombar-overlap.md— the folded seed-defect todo (repro, fix sketch, affected files)..planning/ROADMAP.md§"Phase 999.20" / §"Phase 999.21" — the deferred dark-mode and styling-refresh backlog items (what is explicitly NOT in this phase).
A — Phone layout (fix surface)
apps/pwa/src/App.tsx—window.matchMedia('(max-width: 767px)')phone breakpoint +contentStyle(currently reserves nopadding-bottomfor the fixed bar).apps/pwa/src/components/BottomTabBar.tsx—position: fixed; height: calc(56px + env(safe-area-inset-bottom)); z-index: 200.apps/pwa/src/components/CalendarShell.tsx— the "New Event" FAB (position: fixed; bottom: var(--space-6); right: var(--space-6)).apps/pwa/e2e/layout.spec.ts— Phase 7 quality-bar assertions (UI-SPEC Rules 1–4: ≥44px tap targets, no overflow, in-viewport, accessible names); the sweep checklist AND the home for any new overlap regression assertion.apps/pwa/playwright.config.ts—iphone(iPhone 14/WebKit 390×844),pixel(Pixel 7/Chromium 412×915),desktop(Desktop Chrome 1280×720) profiles to verify across.
B — Branding
apps/pwa/src/components/BrandSlot.tsx— the Phase-17 branding seam; header documents the--brand-logo-*token contract and the swap-div-for-<img>plan..planning/phases/19-local-auth-no-oidc-mode/19-UI-SPEC.md§Brand Slot — the seam's design contract (referenced by BrandSlot.tsx).apps/pwa/public/—apple-touch-icon.png,icon-192.png,icon-512.png(placeholder stubs to replace).apps/pwa/index.html— current<link rel="apple-touch-icon">+theme-colormeta (no<link rel="icon">yet).apps/pwa/vite.config.ts§VitePWA({ manifest: { icons: [...] } })(lines ~29–41) — PWA manifest icon list to update (incl. the improper maskable reuse).
C — Theming
apps/pwa/src/styles/tokens.css— the single light:roottoken system to restructure into a themeable layer (note the Schedule-X--sx-color-*overrides at the bottom and the--brand-logo-*defaults). </canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
BrandSlotseam (apps/pwa/src/components/BrandSlot.tsx): purpose-built in Phase 19 to absorb the real logo with zeroLoginPagelayout change — swap the placeholder div for an<img>and set--brand-logo-*. The branding workstream is wiring, not new architecture.layout.spec.ts+ 3 Playwright profiles (Phase 7/14): a ready, CI-wired structural quality bar (tap targets / overflow / in-viewport / a11y names) — doubles as the sweep checklist (D-01) and the natural home for a regression guard (D-02).- CSS custom-property token system (
tokens.css): everything is already a variable with "no hard-coded hex/px in components" enforced — the themeable-layer restructure (D-06) builds on an already-favourable structure rather than fighting inline values. vite-plugin-pwamanifest (vite.config.ts): the icon set is declared in one place; replacing stubs = update files inpublic/+ the manifesticons[]+index.htmllinks.
Established Patterns
- Phone/desktop split at 767/768px via
matchMediainApp.tsx; phone uses top AppNav (PhoneNav<header>) + fixed BottomTabBar (sole<nav aria-label="Main navigation">), desktop uses a sidebar with no bottom bar. Fixes must be phone-scoped and not touch desktop geometry. - Safe-area awareness: BottomTabBar already uses
env(safe-area-inset-bottom); any FAB-lift / content-padding fix must compose with the same inset (notched-device correctness). - Accessibility invariants the layout fix must preserve: single nav landmark on mobile, ≥44px tap targets, no horizontal overflow (the very assertions in
layout.spec.ts).
Integration Points
- FAB offset (
CalendarShell.tsx) ↔ bar height (BottomTabBar.tsx) ↔ content padding (App.tsx) — D-02's "shared--bottom-chrome-htoken" option would make these three agree via one source of truth. - Logo image ↔
--brand-logo-*tokens (tokens.css) ↔BrandSlot↔LoginPage. - Icon files (
public/) ↔index.htmllinks ↔vite.config.tsPWA manifest — all three must reference the same regenerated asset set. </code_context>
Logo / brand brief (user, 2026-06-17):
- FamilySync is the brand — family-oriented, the name is the identity.
- Caricature-family vibes — a warm, characterful family feel (not a cold/corporate geometric mark).
- Warm tones.
- Rounded corners / rounded shapes.
- Overall it should "make you feel comfortable and at home."
This brief drives the AI-generated logo + icon set (D-03/D-04). Present generated options at a user-approval checkpoint before finalizing; the warm/rounded/at-home direction is the acceptance lens. Note the current theme-color is #4A90D9 (cool blue) and --color-shared-family is rose #f25c7a — the warm-tone brief may motivate revisiting the brand/theme accent during the token-groundwork work (keep within light-theme scope).
- Shipped dark theme + light/dark/system toggle — captured as backlog 999.20 (PWA dark mode / theming). Phase 17 ships the enabling token groundwork only.
- Modern visual styling refresh (contemporary restyle across high-visibility surfaces) — captured as backlog 999.21, flagged for a future milestone (redesign risk; own track).
Reviewed Todos (not folded)
2026-06-10-gitea-ci-regression-and-docker-publish.md— surfaced as a weak keyword match (score 0.6) but is CI/tooling work already shipped in Phase 8/14; unrelated to this UI phase. Not folded.
Phase: 17-ui-optimization-polish Context gathered: 2026-06-17