Phase 17: UI Optimization & Polish #24

Merged
luckberg merged 61 commits from gsd/phase-17-ui-optimization-polish into main 2026-06-18 16:24:54 -04:00
3 changed files with 227 additions and 4 deletions
Showing only changes of commit 3784762817 - Show all commits
+5 -4
View File
@@ -35,7 +35,7 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem
- [x] **Phase 14: Desktop E2E Coverage** - Add a Desktop Chrome Playwright profile + make the mobile-authored specs desktop-safe so the Phase 8 regression gate validates desktop, not just mobile (completed 2026-06-12)
- [x] **Phase 15: Doc-Only CI Skip + Markdown Lint** - Aggregate-gate the slow api/harness CI jobs so doc-only PRs to main merge without running them (no branch-protection deadlock), and add markdownlint to `fast-checks` so docs get a fast format+lint gate (promoted from backlog 999.17) (completed 2026-06-12)
- [x] **Phase 16: CI Dependency Audit, Security Checks & Image Hygiene** - Extend Gitea CI with outdated-dependency reporting + vulnerability audit + a baseline of additional security checks, and enforce the dev/prod image boundary so no dev-bypass, secret, or family data ships in published images (absorbs backlog 999.17); independent of the admin chain (completed 2026-06-13)
- [ ] **Phase 17: UI Optimization & Polish** - Responsive/layout polish pass for the PWA — fix the long-standing phone-layout overlap where the fixed BottomTabBar covers the New Event FAB and the calendar colour legend, and sweep other small-viewport spacing/tap-target issues surfaced in use (CSS/layout only, no behaviour change)
- [ ] **Phase 17: UI Optimization & Polish** - Phone-layout polish + branding + theme groundwork: fix the long-standing phone-layout overlap where the fixed BottomTabBar covers the New Event FAB and the colour legend (+ small-viewport sweep), finish the branding assets (real FamilySync logo into the BrandSlot seam + a complete favicon/PWA-icon set replacing the placeholder stubs), and restructure tokens.css into a themeable token layer (light-only groundwork for future dark mode). Shipped dark theme → backlog 999.20; broader styling refresh → backlog 999.21 (future milestone)
## Phase Details
@@ -394,10 +394,11 @@ Plans:
### Phase 17: UI Optimization & Polish
**Goal**: A responsive/layout polish pass for the PWA so the phone (≤767px) layout has no fixed-chrome overlap and small-viewport spacing reads cleanly — starting with the long-standing BottomTabBar overlap that hides the New Event FAB and the calendar colour legend.
**Goal**: A visual-identity & polish pass for the PWA spanning three workstreams: **(A) phone-layout polish** so the phone (≤767px) layout has no fixed-chrome overlap and small-viewport spacing reads cleanly — starting with the long-standing BottomTabBar overlap that hides the New Event FAB and the colour legend, plus a small-viewport sweep; **(B) branding assets** — generate a real FamilySync logo into the existing `BrandSlot` seam (`apps/pwa/src/components/BrandSlot.tsx`) and a complete favicon/PWA-icon set replacing the placeholder stubs in `apps/pwa/public/`; **(C) theme-token groundwork** — restructure `apps/pwa/src/styles/tokens.css` into a themeable semantic-token layer (swappable by `data-theme`/`prefers-color-scheme`), light staying the only shipped theme, so a future dark theme is cheap.
**Mode:** standard
**Depends on**: Nothing structural (CSS/layout only). Best sequenced after Phase 10 merges (the BottomTabBar gained an Admin tab and the new SetupBanner adds top pressure on phone), but otherwise independent of the admin chain.
**Requirements**: TBD (UI/UX polish — define/promote in discuss-phase)
**Depends on**: Nothing structural (CSS/layout + assets only). Best sequenced after Phase 10 merges (the BottomTabBar gained an Admin tab and the new SetupBanner adds top pressure on phone), but otherwise independent of the admin chain.
**Requirements**: TBD (UI/UX polish + branding — define/promote in plan-phase; decisions captured in 17-CONTEXT.md)
**Scope boundary (set in `/gsd-discuss-phase 17`, 2026-06-17):** Workstream C ships token groundwork **only** — no dark palette, no theme toggle (→ backlog **999.20**). A broader "modern styling" visual refresh is **out of scope** and routed to backlog **999.21** (future milestone). Keep Phase 17 a focused polish + branding + groundwork pass, not a redesign.
**Seed defect — phone-layout bottom-bar overlap (documented 2026-06-13; long-standing, NOT introduced by Phase 10 — the BottomTabBar dates to Phase 04):**
@@ -0,0 +1,120 @@
# 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 three bounded workstreams:
- **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.
**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.
### 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 14: ≥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 ~2941) — 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*
@@ -0,0 +1,102 @@
# Phase 17: UI Optimization & Polish - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-06-17
**Phase:** 17-ui-optimization-polish
**Areas discussed:** Scope breadth, Fix technique, Regression guard, Phase shape, Branding asset ownership, Dark-mode depth, Styling-refresh boundary, Logo/brand direction
---
## Phase shape / sequencing
Initial framing was a CSS-only layout-polish phase. Mid-discussion the user expanded scope to also include branding/logo assets (started but unfinished, incl. favicon), groundwork for themes (dark mode), and a modern styling refresh. This pushed past the ROADMAP's original "CSS/layout only, not a redesign" boundary, so scope was re-negotiated.
| Option | Description | Selected |
|--------|-------------|----------|
| One phase, four workstreams (A layout + B branding + C dark mode + D styling) | Keep everything in Phase 17 | |
| Split — keep layout + branding + token groundwork; defer dark theme & styling | Bound the phase, route the rest to backlog | ✓ |
| One phase but styling "light" | A+B+C full, D incidental only | |
**User's choice:** "spin dark mode and styling into /gsd-capture --backlog. We do the rest in the phase." → Phase 17 = layout polish + branding + theme-token groundwork. Shipped dark theme → backlog 999.20; styling refresh → backlog 999.21 (future milestone).
**Notes:** ROADMAP Phase 17 goal/scope updated to match; two backlog items created and committed (916fb34).
## A — Scope breadth (layout)
| Option | Description | Selected |
|--------|-------------|----------|
| Seed defect + targeted sweep | Fix overlap, then run Phase 7 layout.spec checklist across phone routes | ✓ (implied by keeping the layout workstream) |
| Seed defect only | Fix just the bottom-bar overlap | |
| Broad small-viewport audit | Free-form audit of every phone screen | |
**User's choice:** Layout workstream retained as the bounded, checklist-driven sweep (D-01).
**Notes:** Phase 7 `layout.spec.ts` assertions are the checklist.
## A — Fix technique & regression guard
| Option | Description | Selected |
|--------|-------------|----------|
| Shared bar-height token + padding / inline calc / shrink column | CSS technique for the overlap | (researcher decides) |
| Add overlap assertions to layout.spec / manual playwright-cli / both | Regression guard | (researcher decides) |
**User's choice:** "5 - have the researcher decide."
**Notes:** Delegated to research/planning (D-02). Default lean noted: shared `--bottom-chrome-h` token + CI assertion, but researcher decides on the merits.
## B — Branding asset ownership
| Option | Description | Selected |
|--------|-------------|----------|
| AI-generate in-phase | Claude generates logo + full icon set, wires in, user approves | ✓ |
| User supplies final art | User provides logo, Claude derives icon set | |
| Generate placeholders now, real art later | Stopgap improved mark via the seam | |
**User's choice:** "2a" — AI-generate in-phase.
**Notes:** No existing logo draft found beyond the "FS" placeholder + stub icons. Full icon set incl. proper maskable + favicon.ico/svg (D-03/D-04/D-05).
## C — Dark-mode depth
| Option | Description | Selected |
|--------|-------------|----------|
| Groundwork only | Themeable token restructure, light stays default; dark flippable later | ✓ |
| Ship working dark mode | Finished dark theme via prefers-color-scheme | |
| Ship dark mode + in-app toggle | Plus persisted light/dark/system toggle | |
**User's choice:** "3a" — groundwork only. Combined with "spin dark mode into backlog," the *token restructure* stays in-phase; the *shipped dark theme + toggle* go to backlog 999.20 (D-06).
**Notes:** Light remains the sole shipped theme this phase.
## D — Styling-refresh boundary
| Option | Description | Selected |
|--------|-------------|----------|
| In-system polish | Modernize within existing design system | |
| Component-level refresh | Rework high-visibility surfaces | |
| Broader visual overhaul | Open-ended modern restyle | (deferred) |
**User's choice:** "Dont do 4 - that goes into future milestone." → routed entirely to backlog 999.21.
**Notes:** Redesign risk; own track in a future milestone.
## Logo / brand direction
**User's choice (free-text):** "FamilySync is the brand — family orientated. I like caricature family kind of vibes, warm tones, rounded corner sort of thing. Something that makes you feel comfortable and at home."
**Notes:** Captured verbatim into CONTEXT Specific Ideas as the acceptance lens for generated logo/icon options. Current theme-color `#4A90D9` (cool blue) may be revisited toward warm tones within light-theme scope.
---
## Claude's Discretion
- Fix technique + regression-guard mechanism for the layout overlap (delegated to researcher — D-02).
- Specific small-viewport issues surfaced by the `layout.spec` sweep (fix as found, no behaviour change).
- Logo visual execution within the brand brief, subject to user approval at a checkpoint.
## Deferred Ideas
- Shipped dark theme + light/dark/system toggle → backlog **999.20**.
- Modern visual styling refresh → backlog **999.21** (future milestone).
- (Reviewed, not folded) `2026-06-10-gitea-ci-regression-and-docker-publish.md` — weak keyword match, already-shipped CI work, unrelated to this UI phase.
---
## Process note — interactive question tool blocked
The `AskUserQuestion` tool returned "Permission denied by hook" during this discussion. Investigation: no configured PreToolUse hook or permission rule matches `AskUserQuestion` (user-settings matchers are only `Write|Edit`/`Bash`/`MultiEdit`; `defaultMode: bypassPermissions`; no managed settings). The block correlates with an active background subagent (the Phase 19 `--fix --auto` fixer) running concurrently — interactive questions are suppressed while a background agent is live. Discussion proceeded via the plain-text numbered-list fallback.