# Phase 17: UI Optimization & Polish - Context

**Gathered:** 2026-06-17
**Status:** Ready for planning

<domain>
## Phase Boundary

A **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: fixed` BottomTabBar 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 `BrandSlot` seam (`apps/pwa/src/components/BrandSlot.tsx`), and produce a **complete favicon / PWA-icon set** to replace the placeholder stubs in `apps/pwa/public/` (`icon-192.png` 699 B, `icon-512.png`, `apple-touch-icon.png` 617 B — all generated stubs; there is currently **no `favicon.ico`/`favicon.svg`** and index.html links only the apple-touch icon).
- **C — Theme-token groundwork.** Restructure `apps/pwa/src/styles/tokens.css` from its single light `:root` into a **themeable semantic-token layer** (swappable by `data-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**.
</domain>

<decisions>
## Implementation Decisions

### A — 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.ts` assertions (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 content `padding-bottom`, OR shrink the `100dvh` column by the bar height) vs. a single shared `--bottom-chrome-h` token consumed by both the FAB offset and the content padding (single source of truth). Researcher also decides whether to add a permanent overlap assertion to `layout.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 the `vite-plugin-pwa` manifest `icons[]` in `apps/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), and `apple-touch-icon.png` (180×180). Update `index.html` (add the missing `<link rel="icon">`s; `theme-color` currently `#4A90D9`) and the `vite.config.ts` manifest to reference them.
- **D-05:** Drive the logo through the existing `BrandSlot` seam contract — swap the placeholder `<div>` for an `<img>` and override the `--brand-logo-*` tokens — **without changing `LoginPage` layout** (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.css` into a themeable layer (semantic tokens resolvable per theme via `data-theme`/`prefers-color-scheme`) — **groundwork only** (option 3a). **Do NOT** author dark palette values, wire `prefers-color-scheme` to 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 in `tokens.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/logout` works (200, clears cookie — BL-02 verified live) and `fetchLocalLogout()` exists at `apps/pwa/src/api/client.ts:127`, but **no component calls it** (zero logout buttons in `apps/pwa/src`). Add a logout control (likely in `SettingsSheet.tsx` and/or `AppNav`) 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.tsx` and 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.spec` sweep (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 — its `files:` list (`App.tsx`, `BottomTabBar.tsx`, `CalendarShell.tsx`) is the fix surface.
</decisions>

<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 no `padding-bottom` for 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-color` meta (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 `:root` token 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
- **`BrandSlot` seam** (`apps/pwa/src/components/BrandSlot.tsx`): purpose-built in Phase 19 to absorb the real logo with zero `LoginPage` layout 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-pwa` manifest** (`vite.config.ts`): the icon set is declared in one place; replacing stubs = update files in `public/` + the manifest `icons[]` + `index.html` links.

### Established Patterns
- **Phone/desktop split at 767/768px** via `matchMedia` in `App.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-h` token" option would make these three agree via one source of truth.
- Logo image ↔ `--brand-logo-*` tokens (`tokens.css`) ↔ `BrandSlot` ↔ `LoginPage`.
- Icon files (`public/`) ↔ `index.html` links ↔ `vite.config.ts` PWA manifest — all three must reference the same regenerated asset set.
</code_context>

<specifics>
## Specific Ideas

**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).
</specifics>

<deferred>
## Deferred Ideas

- **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.
</deferred>

---

*Phase: 17-ui-optimization-polish*
*Context gathered: 2026-06-17*
