---
phase: "17"
plan: "01"
subsystem: pwa/styles
tags: [css-tokens, theme-groundwork, workstream-c, d-06]
dependency_graph:
  requires: []
  provides:
    - apps/pwa/src/styles/tokens.css — combined :root,[data-theme="light"] selector
    - --bottom-chrome-h token (consumed by Workstreams A and D plans)
  affects:
    - apps/pwa/src/styles/tokens.css (selector restructure, new token)
tech_stack:
  added: []
  patterns:
    - "CSS combined selector :root,[data-theme=light] for future dark-theme override"
    - "--bottom-chrome-h: calc(56px + env(safe-area-inset-bottom, 0px)) layout-chrome token"
key_files:
  created: []
  modified:
    - apps/pwa/src/styles/tokens.css
decisions:
  - "D-06: combined :root,[data-theme=light] selector is purely structural — all token values unchanged; enables future data-theme=dark override without any component-file changes"
  - "Dark-theme stub is a comment-only block — no live [data-theme=dark] rule; Phase 999.20 owns that work"
  - "--bottom-chrome-h placed in spacing-scale section (alongside --space-* tokens) as its conceptual peer"
  - "All --sx-color-* overrides remain inside the combined rule block (do not split); prevents Schedule-X cascade break (Pitfall 3)"
metrics:
  duration: "3m 23s"
  completed_date: "2026-06-18"
  tasks_completed: 2
  tasks_total: 2
  files_changed: 1
status: complete
---

# Phase 17 Plan 01: Token Groundwork — Summary

Restructured `apps/pwa/src/styles/tokens.css` from a single `:root {}` block to a combined `:root, [data-theme="light"]` selector (D-06 theme-token groundwork), and added the `--bottom-chrome-h` layout-chrome token as the single source of truth for BottomTabBar effective height.

## What Was Built

**One-liner:** CSS selector restructure to `[data-theme="light"]-capable pattern + `--bottom-chrome-h` token for BottomTabBar height, zero value changes.

### tokens.css structural change

- The opening `:root {` selector changed to `:root,\n[data-theme="light"] {`
- All 12 `--sx-color-*` Schedule-X overrides remain inside the same combined rule block (cascade order unchanged)
- All `--brand-logo-*` tokens remain inside the combined rule block (verbatim)
- New token added to spacing scale: `--bottom-chrome-h: calc(56px + env(safe-area-inset-bottom, 0px))`
- Dark theme stub added as `/* ... */` comment below the closing brace (no live rule)

### No component/route file changes

This plan is groundwork only. No component, route, or App file was touched. Workstream A (Plan 17-02) and Workstream D (Plans 17-04, 17-05) consume `--bottom-chrome-h` from this foundation.

## Verification Results

### Task 1 acceptance criteria

| Criterion | Result |
|-----------|--------|
| `[data-theme="light"]` selector present | PASS |
| `--bottom-chrome-h` present with exact value `calc(56px + env(safe-area-inset-bottom, 0px))` | PASS |
| `--sx-color-*` count still 12 (unchanged) | PASS (12 occurrences) |
| No live `[data-theme="dark"]` rule (commented stub only) | PASS — line is inside `/* ... */` block comment |
| `pnpm --filter @familysync/pwa build` exits 0 | PASS |

### Task 2 verification

| Check | Result |
|-------|--------|
| Grep invariant — no NEW hard-coded hex/px in component/route files | PASS — zero literals introduced by this plan (selector-only change, no component files touched) |
| playwright-cli `/calendar` Schedule-X color sweep | PASS — calendar renders with FamilySync custom colors (member-0 blue #4a90d9 for today-circle + nav active state; shared-family rose #f25c7a for seeded event chip). Not reverting to Schedule-X default blue/green. Screenshot captured. |
| `layout.spec.ts` pixel profile | PASS — 15 passed, 1 skipped (desktop-only, expected); 0 failures |

## Deviations from Plan

None. Plan executed exactly as written. The single selector change in tokens.css was the only modification; all values are verbatim from the original file.

## Known Stubs

None introduced by this plan.

## Noted Observations (Task 2 — Out of Scope)

The grep invariant sweep of `apps/pwa/src/components/` and `apps/pwa/src/routes/` found pre-existing hard-coded literals in multiple files (not introduced by this plan):

- `SyncStateToast.tsx` — hardcoded `#50C878` color (line 105) and `rgba(0,0,0,0.12)` box shadow
- `ListCard.tsx` — hardcoded `56px` min-height (line 68), `4px` border-radius (line 127), `44px`/`44px` touch targets (lines 166–167)
- `EmptyState.tsx` — hardcoded `280px` max-width
- Other component files — similar pre-existing literals

These are pre-existing at commit eb0db8b and are explicitly out of scope for Plan 17-01 (selector-only change). They do not affect the plan's deliverable. Noted here per Task 2 instructions.

## Threat Surface Scan

No new trust boundaries, network endpoints, auth paths, or schema changes. This is a static CSS file selector change — no runtime data flow, no new dependencies. Threat model confirmed: T-17-01-01 accepted (CSS custom properties carry no executable content; no new threat above LOW).

## Self-Check: PASSED

| Item | Status |
|------|--------|
| `apps/pwa/src/styles/tokens.css` exists | FOUND |
| `17-01-SUMMARY.md` exists | FOUND |
| Task 1 commit `c2f89bd` in git log | FOUND |
