Files
familysync/.planning/phases/17-ui-optimization-polish/17-VERIFICATION.md
T

13 KiB
Raw Blame History

phase, verified, status, score, behavior_unverified, overrides_applied, human_verification
phase verified status score behavior_unverified overrides_applied human_verification
17-ui-optimization-polish 2026-06-18T00:00:00Z human_needed 10/10 decisions delivered in code (1 WARNING-class resize limitation, 4 device-only checks) 0 0
test expected why_human
On a phone (≤767px, e.g. iPhone 14 / Pixel 7) load /calendar and /lists. Confirm the New Event FAB sits fully above the BottomTabBar and the color-legend chips are not clipped behind the bar. FAB floats above the tab bar (not on the Admin tab); legend chips fully visible; CI overlap guard (layout.spec.ts) corroborates geometry on iphone/pixel profiles. Pixel-accurate fixed-chrome overlap + safe-area-inset rendering on a real notched device cannot be fully judged from CSS source; CI guard runs headless WebKit/Chromium only.
test expected why_human
Resize a desktop browser window narrower than 768px (or rotate a tablet/foldable across the 767px breakpoint) with the Settings sheet, a credential sheet, or the admin reset sheet OPEN. Sheet should switch between centered-modal (desktop) and bottom-sheet (phone) geometry. NOTE: 17-REVIEW WR-01 documents that the `phone` snapshot is computed once per render with no matchMedia listener, so the sheet keeps stale geometry until an unrelated re-render. Confirm severity for this household's actual devices. Resize-crossing-breakpoint re-render behavior is a runtime interaction; the static branches are correct at mount but do not re-evaluate. Operator decides if this WARNING blocks (real phones are always phones; impact is tablets/foldables/desktop-resize).
test expected why_human
Install the PWA to a device home screen and inspect the app icon (especially Android adaptive/maskable rendering and iOS apple-touch-icon). Maskable icon (icon-maskable-512.png) shows the family-house logo inside the safe zone with no clipping; favicon shows in browser tab; apple-touch-icon shows on iOS home screen. Maskable safe-zone correctness and home-screen icon rendering are device/installer-specific (iOS-Safari standalone is device-only per CLAUDE.md); cannot be driven headless.
test expected why_human
Sign in (local auth, non-dev-bypass) and open Settings; tap 'Sign out'. Session is cleared and the app routes to /login; signing back in works. (Endpoint + client wiring verified in code; live round-trip confirms cookie clearing end-to-end.) Full logout round-trip (cookie cleared server-side + redirect) is a runtime/auth flow; code path is verified but live confirmation is prudent.

Phase 17: UI Optimization & Polish Verification Report

Phase Goal: A visual-identity & polish pass for the PWA spanning three workstreams — (A) phone-layout polish (no fixed-chrome overlap), (B) branding assets (real logo + complete icon set), (C) theme-token groundwork — plus (D) UAT-surfaced UI fixes (logout, admin toasts, sheet centering, admin nav rework). Verified: 2026-06-18 Status: human_needed Re-verification: No — initial verification

Goal Achievement

Decisions D-01..D-10 stand in for REQ-IDs (no Success Criteria array in ROADMAP; goal is prose + decisions). All 10 decisions have corresponding, substantive, wired code in the codebase. The status is human_needed (not passed) because device-only checks (phone overlap on real hardware, PWA icon install, logout round-trip) and one WARNING-class resize limitation (17-REVIEW WR-01) require operator confirmation.

Observable Truths (by Decision)

# Decision / Truth Status Evidence
D-01 Phone FAB no longer overlaps BottomTabBar; legend not occluded ✓ VERIFIED FAB bottom: calc(var(--bottom-chrome-h) + var(--space-6)) (CalendarShell.tsx:470); phone paddingBottom: var(--bottom-chrome-h) (App.tsx:164); --bottom-chrome-h: calc(56px + env(safe-area-inset-bottom,0px)) (tokens.css:69). Matches ROADMAP fix sketch exactly.
D-02 Permanent overlap regression guard in CI ✓ VERIFIED layout.spec.ts:209-231 "New Event FAB does not overlap BottomTabBar" — asserts fabBox.bottom ≤ navBox.top, phone-only (iphone/pixel), skipped on desktop. Shared-token approach chosen (the planner's default lean).
D-03 Real FamilySync logo (warm/rounded/at-home) committed as SVG; operator-approved ✓ VERIFIED logo.svg (warm peach gradient bg, rounded family-house mark, 512 viewBox). Accent #e8915a + --brand-logo-border-radius: 0 documented as operator-approved (tokens.css:41,106).
D-04 Complete 7-asset icon set; proper separate maskable ✓ VERIFIED All 7 files present in public/ (logo.svg, favicon.svg, favicon.ico, icon-192/512, icon-maskable-512, apple-touch-icon) at real sizes (not stubs). Maskable is its own 8627B file, distinct from icon-512 (12056B). Manifest references it with purpose: 'maskable' (vite.config.ts:41).
D-05 Logo wired into BrandSlot, decorative, no LoginPage layout change ✓ VERIFIED BrandSlot.tsx swaps placeholder div for <img src="/logo.svg" alt="" aria-hidden="true">; <h1>FamilySync</h1> retained as page title; tokens drive size/radius. Seam contract honored.
D-06 tokens.css restructured to themeable layer; Schedule-X overrides intact; no new hardcoded colors ⚠️ PARTIAL Combined :root, [data-theme="light"] selector (tokens.css:16-17); --sx-color-* overrides remain inside the block after the theme-default import (tokens.css:127+). Groundwork-only (no dark palette/toggle) per scope. Caveat: sheet-centering work (17-05/06) added hardcoded px literals (12px, 480px, boxShadow rgba) in component files — but these match pre-existing patterns and the color invariant (hex in tokens.css) holds.
D-07 Reachable Sign out clears session + routes to /login; best-effort on API failure ✓ VERIFIED SettingsSheet.tsx:522-554 always-visible "Sign out" control → handleSignOut (137-146): try fetchLocalLogout(), catch swallows error, then unconditional navigate('/login').
D-08 Admin create-member + reset-password success toasts, ~3s auto-dismiss ✓ VERIFIED AdminPage.tsx: toast state + 3000ms setTimeout auto-dismiss (62-69); "Member added." (261), "Password reset." (1090); role="status" live region (1030).
D-09 Dialogs/sheets centered on desktop, bottom-sheet on phone ⚠️ PARTIAL SettingsSheet (204-233), CredentialSheet (178+), AdminPage reset sheet (1419-1443) all have phone (bottom:0) vs desktop (translate(-50%,-50%), maxWidth 480px) branches. Caveat: branch is a one-shot render snapshot (17-REVIEW WR-01) — does not re-evaluate on resize across 767px. Correct for real phones; stale on tablet/foldable/desktop-resize.
D-10 Two-tab ARIA strip with roving tabindex + ArrowLeft/Right keyboard nav ✓ VERIFIED AdminPage.tsx: role="tablist" (321), role="tab"+aria-selected+roving tabIndex 0/-1 (331-358), handleTabKeyDown ArrowRight/Left with focus management (205-220), role="tabpanel" ×2. CI-tested in admin.spec.ts (ArrowRight/Left switch + default selection).

Score: 10/10 decisions delivered in code (8 fully VERIFIED, 2 PARTIAL with documented WARNING-class caveats). 0 behavior-unverified, 0 FAILED, 0 BLOCKER.

Required Artifacts

Artifact Expected Status Details
apps/pwa/src/styles/tokens.css Themeable layer + --bottom-chrome-h ✓ VERIFIED Combined selector + token defined; sx overrides intact
apps/pwa/public/logo.svg + 6 icons Brand mark + complete set ✓ VERIFIED All 7 real files; maskable separate
apps/pwa/pwa-assets.config.ts + package.json generator config + script ✓ VERIFIED Both exist; reads logo.svg
apps/pwa/e2e/layout.spec.ts FAB↔BottomTabBar overlap guard ✓ VERIFIED D-01 regression test present
apps/pwa/src/components/BrandSlot.tsx logo img swapped in ✓ VERIFIED Decorative img, h1 retained
apps/pwa/index.html favicon links + theme-color ✓ VERIFIED favicon.svg + .ico + apple-touch + #e8915a
apps/pwa/src/components/SettingsSheet.tsx Sign out + centering ✓ VERIFIED handleSignOut + desktop branch
apps/pwa/src/components/CredentialSheet.tsx centering branch ✓ VERIFIED phone/desktop branch present
apps/pwa/src/routes/AdminPage.tsx toasts + tablist + reset centering ✓ VERIFIED All present
apps/pwa/e2e/admin.spec.ts tab ARIA + keyboard + toast tests ✓ VERIFIED 9 tab assertions + toast structure test
From To Via Status Details
tokens.css @schedule-x/theme-default sx overrides after import ✓ WIRED tool-verified
pwa-assets.config.ts public/logo.svg images: ['public/logo.svg'] ✓ WIRED tool-verified
CalendarShell.tsx tokens.css FAB reads var(--bottom-chrome-h) ✓ WIRED Manually verified (CalendarShell.tsx:470) — gsd-tools reported false-negative due to over-escaped regex var\\(--bottom-chrome-h\\); pattern is present and correct.
App.tsx tokens.css phone paddingBottom reads token ✓ WIRED Manually verified (App.tsx:164) — same false-negative; pattern present.
vite.config.ts icon-maskable-512.png manifest purpose: maskable ✓ WIRED tool-verified
BrandSlot.tsx public/logo.svg img src /logo.svg ✓ WIRED tool-verified
SettingsSheet.tsx api/client.ts handleSignOut calls fetchLocalLogout ✓ WIRED tool-verified
AdminPage.tsx SyncStateToast.tsx reuse toast visual pattern ✓ WIRED (pattern) gsd-tools reported "target not referenced" — the plan via says reuse the visual pattern, not import. Toast uses role="status" + 3s auto-dismiss as specified (AdminPage.tsx:1030,66-69). Intent satisfied.

Behavioral Spot-Checks

Behavior Command Result Status
Overlap guard test exists grep "New Event FAB does not overlap BottomTabBar" layout.spec.ts 1 match ✓ PASS
Admin tab tests exist grep "getByRole('tab'" admin.spec.ts 9 matches ✓ PASS
Tab keyboard switching exercised in CI ArrowRight/ArrowLeft → aria-selected assertions present (admin.spec.ts:134-151) ✓ PASS (behavior-dependent truth has CI coverage)
All 7 branding assets present + real sizes ls public/ + file logo.svg, favicon.svg/.ico, icon-192/512, maskable-512, apple-touch all >900B PNG/SVG ✓ PASS

Production build / typecheck / eslint (0 warnings) / 266 unit tests already passing per phase context — relied upon, not re-run.

Requirements Coverage

No REQ-IDs; D-01..D-10 serve as requirements. Coverage: D-01/D-02→17-03 ✓, D-03/D-04→17-02 ✓, D-04/D-05→17-04 ✓, D-06→17-01 ✓(⚠), D-07/D-09→17-05 ✓(⚠), D-08/D-09/D-10→17-06 ✓. All decisions mapped to a plan and delivered.

Anti-Patterns Found

File Line Pattern Severity Impact
SettingsSheet/CredentialSheet/AdminPage sheet branches Hardcoded px (12px, 480px, boxShadow rgba) in component files Info Layout primitives, not colors; matches pre-existing patterns; color invariant (hex→tokens.css) intact
SettingsSheet.tsx various phone = matchMedia(...) one-shot, no listener ⚠️ Warning 17-REVIEW WR-01 — stale sheet/FAB geometry on resize across 767px (tablet/foldable/desktop-resize). Not a blocker for real phones.
AdminPage / dialogs Hardcoded z-index literals (300/301/302/303) Info 17-REVIEW WR — duplicated magic numbers, no current layering bug

No TBD/FIXME/XXX debt markers found in phase-modified files. No BLOCKER-class issues (17-REVIEW: 0 critical).

Human Verification Required

See frontmatter human_verification — 4 items: (1) phone overlap on real device, (2) resize-across-breakpoint sheet geometry [WR-01 severity call], (3) PWA maskable/home-screen icon install, (4) logout round-trip. Items 1 and 3 are genuinely device-only (per CLAUDE.md iOS-Safari/install exception). Items 1, 2, 4 could alternatively be spot-checked via playwright-cli on Chromium if desired.

Gaps Summary

No goal-blocking gaps. Every decision D-01..D-10 is delivered with substantive, wired code, and the two automated CI guards the phase promised (FAB overlap in layout.spec.ts, admin tab ARIA/keyboard in admin.spec.ts) exist and assert real geometry/state transitions. The phase achieves its goal in the codebase.

Two WARNING-class caveats (both pre-documented in 17-REVIEW, both WARNING not BLOCKER) and four human/device confirmations keep the verdict at human_needed rather than passed:

  • WR-01 (resize snapshot) is the one substantive behavioral caveat — sheet/FAB geometry does not re-evaluate when the viewport crosses 767px after mount. For the two-person household's actual phones this is invisible (a phone is always a phone); it only manifests on tablet rotation or desktop-window narrowing. Operator should confirm this is acceptable for the milestone or fold the useIsPhone() fix (already sketched in 17-REVIEW) into a follow-up.
  • The hardcoded-px additions in sheet branches are a minor invariant softening (D-06 is primarily a color invariant, which holds), recorded as Info.

Verified: 2026-06-18 Verifier: Claude (gsd-verifier)