## Context The site already supports theme selection via a floating notch and persists the user's choice using `localStorage` (key: `site.theme`). Theme selection affects the root document via `html[data-theme]` and is initialized before first paint via an inline head script. Two gaps remain: - Returning users should reliably see their last-selected theme even in environments where `localStorage` is unavailable. - We need analytics to measure theme switcher usage and preferred themes. The site uses Umami for analytics. Most interactions are tracked via `data-umami-event*` attributes, and runtime-only events use `window.umami.track(...)`. ## Goals / Non-Goals **Goals:** - Persist theme selection across visits with a robust fallback: `localStorage` primary, client-side cookie fallback. - Apply the stored theme before first paint when possible. - Emit a deterministic Umami event when a user changes theme via the theme notch. **Non-Goals:** - Server-side rendering of theme choice (the site is statically built; no request-time HTML variation). - Tracking theme selection on initial page load (only user-initiated changes). - Adding new UI beyond the existing theme notch. ## Decisions 1) Persistence mechanism and precedence - **Decision**: Read theme preference in this order: 1. `localStorage` (`site.theme`) 2. Cookie (`site_theme`) 3. Environment signals (forced-colors, prefers-color-scheme) - **Rationale**: `localStorage` is already in use and provides a stable primary store. Cookies provide a resilient fallback when storage access is blocked or throws. - **Alternatives considered**: - Cookie-only: simpler but unnecessary regression from existing behavior. - URL param: not persistent and adds user-visible noise. 2) Cookie format and attributes - **Decision**: Store `site_theme=` with `Max-Age=31536000`, `Path=/`, `SameSite=Lax`. Set `Secure` when running under HTTPS. - **Rationale**: First-party cookie with long TTL provides continuity across visits. The cookie is readable from the inline head script for pre-paint initialization. 3) Analytics event shape - **Decision**: Emit a custom Umami event via `window.umami.track("theme_switch", data)` only on user-initiated changes. - **Event properties**: - `target_id`: `theme.switch.` - `placement`: `theme_notch` - `theme`: new theme value (`dark` | `light` | `high-contrast`) - `prev_theme`: previous theme value (same enum) when known - **Rationale**: A dedicated event name makes reporting straightforward (no need to filter general `click`). Using `target_id`/`placement` keeps it compatible with the site's interaction taxonomy. - **Alternatives considered**: - Reuse `click` event: consistent, but mixes preference changes into general click reporting. 4) Avoid tracking initial theme restoration - **Decision**: Do not emit `theme_switch` from the head theme-init script. - **Rationale**: We want to measure explicit user interaction with the notch, not implicit restoration. ## Risks / Trade-offs - Cookie and storage may both be blocked in restrictive environments → fallback to environment signals; no persistence. - Umami may be disabled/unconfigured or not loaded at event time → guard with `typeof window.umami !== "undefined"` and keep behavior non-fatal. - Using cookies introduces another persistence layer → must keep `localStorage` and cookie consistent on successful theme changes.