From 916fb34f17b2b6b80c5cf2c0ff912bba0c1edf24 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 17 Jun 2026 20:34:02 -0400 Subject: [PATCH 01/54] docs: add backlog items 999.20 (dark mode/theming) + 999.21 (styling refresh) --- .planning/ROADMAP.md | 26 +++++++++++++++++++ .../999.20-pwa-dark-mode-theming/.gitkeep | 0 .../.gitkeep | 0 3 files changed, 26 insertions(+) create mode 100644 .planning/phases/999.20-pwa-dark-mode-theming/.gitkeep create mode 100644 .planning/phases/999.21-pwa-modern-styling-refresh/.gitkeep diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 9bcd5c2..8776c63 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -654,6 +654,32 @@ Plans: - [ ] TBD (promote with /gsd-review-backlog when ready) +### Phase 999.20: PWA dark mode / theming — ship a full dark theme + light/dark/system switch (BACKLOG) + +**Goal:** [Captured for future planning] Ship a complete dark theme for the PWA plus a light/dark/system theme switch. **Phase 17 lays the token-architecture groundwork** — it restructures `apps/pwa/src/styles/tokens.css` from a single light `:root` into a themeable semantic-token layer that can be swapped via `data-theme` / `prefers-color-scheme`, with light staying the default and only-shipped theme. This backlog item is the follow-through that consumes that seam: author the actual dark palette values (including the Schedule-X `--sx-color-*` calendar overrides at the bottom of tokens.css), wire `prefers-color-scheme`, add a persisted in-app toggle in the /admin or Settings surface (light / dark / system), and verify both themes render cleanly across every route (calendar, lists, admin, settings sheet, login) via `playwright-cli` + the Phase 7 `layout.spec` profiles. + +**Context:** Deferred out of Phase 17 (2026-06-17) during `/gsd-discuss-phase 17` to keep that phase scoped to phone-layout polish + branding assets. Phase 17's token restructure is the explicit enabling groundwork, so this should be cheap to pick up afterward. Related: Phase 17 (UI Optimization & Polish — the groundwork), 999.21 (modern styling refresh). Tags: pwa, theming, dark-mode, tokens, accessibility, settings, prefers-color-scheme. + +**Requirements:** TBD +**Plans:** 0 plans + +Plans: + +- [ ] TBD (promote with /gsd-review-backlog when ready) + +### Phase 999.21: PWA modern visual styling refresh — contemporary look across the app (BACKLOG) + +**Goal:** [Captured for future planning] A broader "more modern, visually appealing" styling pass across the PWA — beyond the bounded in-system polish of Phase 17. Candidate scope: a contemporary refresh of high-visibility surfaces (login, calendar shell, event form, lists, admin), revisiting elevation/shadows, radii, spacing rhythm, typography scale, and control states, potentially reworking specific component layouts. Explicitly **flagged for a future milestone**, not v1.1 — it is a visual-overhaul track with real redesign risk and should be scoped/sequenced on its own rather than bolted onto a polish phase. Best sequenced after the Phase 17 token groundwork and 999.20 (dark mode) so the refresh is theme-aware from the start. + +**Context:** Deferred out of Phase 17 (2026-06-17) during `/gsd-discuss-phase 17`. The user scoped Phase 17 to layout polish + branding (logo/favicon/icon assets) + theme-token groundwork, and routed the open-ended styling refresh here for a future milestone to avoid an unbounded redesign inside a polish phase. Related: Phase 17 (the polish baseline), 999.20 (dark mode / theming). Tags: pwa, ui, styling, redesign, design-system, future-milestone. + +**Requirements:** TBD +**Plans:** 0 plans + +Plans: + +- [ ] TBD (promote with /gsd-review-backlog when ready) + ### Phase 18: Auto timezone detection and ability to change timezone **Goal:** Make the household timezone an explicit, stored, user-changeable setting — auto-detected from the browser at first run, changeable from the role-gated /admin Settings — and route the server-side all-day "9 AM local" reminder computation through it (replacing the implicit `process.env.TZ` fallback), without touching the already-correct browser-local display/timed-write path. diff --git a/.planning/phases/999.20-pwa-dark-mode-theming/.gitkeep b/.planning/phases/999.20-pwa-dark-mode-theming/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.planning/phases/999.21-pwa-modern-styling-refresh/.gitkeep b/.planning/phases/999.21-pwa-modern-styling-refresh/.gitkeep new file mode 100644 index 0000000..e69de29 -- 2.54.0 From 3784762817c64d1c65fcdd8ea5aa3a9562825b32 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 17 Jun 2026 20:37:02 -0400 Subject: [PATCH 02/54] docs(17): capture phase context (layout polish + branding + theme groundwork; dark mode/styling deferred) --- .planning/ROADMAP.md | 9 +- .../17-ui-optimization-polish/17-CONTEXT.md | 120 ++++++++++++++++++ .../17-DISCUSSION-LOG.md | 102 +++++++++++++++ 3 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/17-ui-optimization-polish/17-CONTEXT.md create mode 100644 .planning/phases/17-ui-optimization-polish/17-DISCUSSION-LOG.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 8776c63..75b146b 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -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):** diff --git a/.planning/phases/17-ui-optimization-polish/17-CONTEXT.md b/.planning/phases/17-ui-optimization-polish/17-CONTEXT.md new file mode 100644 index 0000000..731a87a --- /dev/null +++ b/.planning/phases/17-ui-optimization-polish/17-CONTEXT.md @@ -0,0 +1,120 @@ +# Phase 17: UI Optimization & Polish - Context + +**Gathered:** 2026-06-17 +**Status:** Ready for planning + + +## 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**. + + + +## 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 ``, `apps/pwa/public/` files, `index.html` ``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 ``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 `
` for an `` 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: `

` 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. + + + +## 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-`` 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 `` + `theme-color` meta (no `` 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). + + + +## 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 `` 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 `
`) + fixed BottomTabBar (sole `