## Purpose Define site-wide theme support (dark, light, high-contrast) using CSS tokens and an application mechanism that can switch across the entire UI. ## Requirements ### Requirement: Site themes The site MUST support three themes: - `dark` - `light` - `high-contrast` Themes MUST be applied by setting a `data-theme` attribute on the root document element (``). #### Scenario: Dark theme active - **WHEN** `data-theme="dark"` is set on `` - **THEN** the site's background, text, and component styling reflect the dark palette #### Scenario: Light theme active - **WHEN** `data-theme="light"` is set on `` - **THEN** the site's background, text, and component styling reflect the light palette #### Scenario: High contrast theme active - **WHEN** `data-theme="high-contrast"` is set on `` - **THEN** the site uses a high-contrast palette with a clearly visible focus ring and high-contrast borders ### Requirement: Theme tokens meet contrast intent For each supported theme (`dark`, `light`, `high-contrast`), the theme token pairs used for primary text and primary surfaces MUST meet WCAG 2.2 AA contrast intent. At minimum: - primary body text on the primary background MUST be high-contrast - link text on the primary background MUST be distinguishable and meet contrast intent - secondary labels on the primary background MUST remain readable #### Scenario: Dark theme text is readable - **WHEN** `data-theme="dark"` is active - **THEN** primary text remains readable against primary surfaces without low-contrast combinations #### Scenario: Dark theme links are readable - **WHEN** `data-theme="dark"` is active - **THEN** links in common surfaces are readable against their background ### Requirement: Theme persistence The site MUST persist the user's theme selection so it is retained across page loads and navigations. Persistence MUST be stored locally in the browser (e.g., localStorage). #### Scenario: Theme persists across reload - **WHEN** the user selects `light` theme and reloads the page - **THEN** the `light` theme remains active ### Requirement: Theme persistence works across visits with fallback The site MUST persist the user's theme selection across visits so returning users see the last-selected theme. The site MUST use client-side persistence and MUST support a fallback mechanism: - Primary: `localStorage` - Fallback: a client-side cookie The effective theme selection order MUST be: 1) Stored theme in `localStorage` (if available) 2) Stored theme in a cookie (if localStorage is unavailable) 3) Default selection using environment signals #### Scenario: LocalStorage persists across a later visit - **WHEN** a user selects `light` theme and later returns to the site in the same browser - **THEN** the site initializes in `light` theme before first paint #### Scenario: Cookie fallback is used when localStorage is unavailable - **WHEN** the browser environment blocks `localStorage` access and the user selects `dark` theme - **THEN** the theme is persisted using a client-side cookie and is restored on the next visit #### Scenario: No persistence available falls back to defaults - **WHEN** both `localStorage` and cookie persistence are unavailable - **THEN** the site falls back to default theme selection using environment signals ### Requirement: Default theme selection If the user has not explicitly selected a theme, the site MUST choose a default theme using environment signals. Default selection order: 1) If forced colors / high-contrast mode is active, default to `high-contrast` 2) Else if the system prefers light color scheme, default to `light` 3) Else default to `dark` #### Scenario: No stored preference uses system settings - **WHEN** the user has no stored theme preference - **THEN** the site selects a default theme based on forced-colors and prefers-color-scheme ### Requirement: Theme switching transition Theme changes initiated by the user MUST transition smoothly. The transition MUST be disabled or substantially reduced when `prefers-reduced-motion: reduce` is set. #### Scenario: Smooth transition on switch - **WHEN** the user switches from `dark` to `light` theme - **THEN** theme-affecting properties transition smoothly instead of abruptly switching #### Scenario: Reduced motion disables theme animation - **WHEN** `prefers-reduced-motion: reduce` is set and the user switches theme - **THEN** the theme change occurs without noticeable animation