## Context The site currently uses a single dark theme defined via CSS variables in `site/public/styles/global.css` (e.g., `--bg0`, `--bg1`, `--fg`, `--muted`, `--stroke`, `--accent`). There is no existing theme selection mechanism (no `data-theme` attribute, no persisted preference). The site shell uses a sticky `.site-header` and a per-page `.subnav` row (when present). A theme switcher notch must be fixed-positioned such that it does not overlap the header and leaves enough space for the subnav region. ## Goals / Non-Goals **Goals:** - Provide three themes: `dark`, `light`, `high-contrast`. - Allow switching themes via a floating notch on the right side of the screen positioned below the primary nav bar and not overlapping the subnav area. - Make switching feel premium: - hover animation on the notch - smooth theme transitions (without an abrupt flash) - Ensure accessibility: - keyboard operable - visible focus - respects `prefers-reduced-motion` - high contrast theme is meaningfully higher contrast, not just a color swap - Persist the user's selection across page loads. **Non-Goals:** - Rebuild the entire visual system or rewrite all CSS to a design-token framework. - PWA theming (manifest/theme-color) beyond what is required to implement UI themes. - Adding user accounts or server-side persistence of theme preference. ## Decisions ### 1. Theme selection mechanism: `data-theme` on `` Use `document.documentElement.dataset.theme = "dark" | "light" | "high-contrast"`. Rationale: - Works cleanly with CSS variables. - Scopes theme styles across the entire page without specificity fights. Alternatives considered: - Adding theme classes to `body` (works, but html-scoped variables are simpler for form control theming). ### 2. Token strategy: override existing CSS variables per theme Keep the existing variable names and provide theme-specific overrides: - `:root` remains the default (dark) - `html[data-theme="light"] { ... }` - `html[data-theme="high-contrast"] { ... }` Rationale: - Minimizes churn in existing CSS. - Enables incremental migration of any remaining hard-coded colors to tokens. ### 3. Default theme resolution order On first load, resolve the active theme in this order: 1) stored user preference (`localStorage.theme`) 2) forced colors / high-contrast OS mode (if detected) -> `high-contrast` 3) system color scheme -> `light` if `prefers-color-scheme: light`, else `dark` Rationale: - User choice wins. - If the user is in a forced/high-contrast environment, defaulting to high-contrast aligns with accessibility intent. ### 4. Prevent flash of wrong theme with a tiny head script Insert a small inline script in the document `
` that sets `data-theme` before first paint. Rationale: - Avoids "flash" where the page renders in dark before switching to light/high-contrast. Trade-off: - Inline scripts can constrain future CSP hardening; keep script small and self-contained. ### 5. Smooth transitions without animating on every page load Use a transient attribute/class (e.g., `data-theme-transition="on"`) only during user-initiated theme changes. Implementation shape: - When switching: set `data-theme-transition="on"`, update `data-theme`, then remove after ~250ms. - CSS applies transitions for color/background/border/shadow only when the attribute is present. Rationale: - Avoids "everything animates" feeling during initial load. - Avoids subtle jank on navigation. ### 6. Notch UI: fixed-position, expands on hover/focus Implement the switcher as a fixed-position control at the right edge: - Default collapsed: a small vertical tab. - On `:hover` and `:focus-within`: expands into a small panel exposing the three theme options. Accessibility decisions: - Use a real `