A single CSS custom-property theme contract with explicit light, dark, and system-resolved modes, persisted user choice, and no hard-coded color literals in product components.
Package status: AI-assisted reference package. Static package validation has passed; implementation scenarios remain not run and human domain review is required before stable adoption.
Decision
Resolve appearance through one semantic token layer exposed as CSS custom properties on a root theme attribute. Support light, dark, and system modes; persist explicit user choice; set color-scheme to match the resolved mode; and forbid product components from hard-coding color literals outside the token contract.
Scope
- Theme mode enumeration, resolution order, and persistence for web shells.
- Mapping design-token releases to runtime custom properties for light and dark palettes.
- First-paint flash prevention and live reaction to OS preference changes when in system mode.
- Interaction with reduced-motion preferences for any optional theme transition.
Outside this block
- Token package versioning and registry distribution.
- Native mobile theming outside a WebView or shared token export.
- High-contrast or forced-colors accessibility modes (may complement but are not defined here).
Contract
- Exactly three user-selectable modes exist:
light,dark, andsystem; no undocumented mode strings are honored. - Resolved mode is written to a dedicated attribute or class before first meaningful paint when persistence or system preference is known.
- Explicit user choice overrides OS preference until the user changes it again or clears storage.
- All product component foreground, background, border, and focus colors reference semantic custom properties prefixed per the adoption profile; raw hex or rgb literals in component CSS are rejected in CI.
- The document
color-schememeta or CSS property matches the resolved mode so native form controls and scrollbars follow theme. - When preference is
system, a media-query listener updates resolved mode on OS changes without requiring navigation. - Invalid stored values fall back to the profile default once, log a single diagnostic, and rewrite storage to the default.
- The application ships an embedded minimal baseline token set for both resolved modes. On first run, when neither the pinned package nor a valid cache is available, the baseline supplies readable semantic colors without component literals.
Implementation guidance
- Inject an inline script or server-rendered attribute in
<head>to set resolved theme before stylesheet load when flash guard is enabled. - Import token values from the pinned token package release; do not fork divergent dark palettes in application code.
- Expose a small settings control that writes preference, updates DOM attributes, and dispatches a documented event for embedded widgets.
- Pair with the reduced-motion block: theme swap duration is zero when reduced motion is requested.
Failure handling and safeguards
- If token package load fails, use a validated last cached custom-property block; when no cache exists or it is invalid, use the embedded minimal light/dark baseline rather than component literals or an undefined palette.
- If persistence is unavailable (private mode), honor session choice but do not claim cross-session continuity.
- If embedded third-party widgets cannot read the contract, isolate them in a documented neutral frame rather than breaking global tokens.
- If contrast validation fails for a resolved pair, block release of that token version rather than shipping an unreadable dark palette.
Verification and operations
- Measure flash-of-incorrect-theme incidents via early paint beacons in staging.
- Track distribution of stored preferences and resolved modes to detect unexpected skew.
- Run automated contrast checks on representative routes in both resolved modes each release.
- Validate cached and embedded baseline token sets before release, including the no-cache first-run path.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - Teams must map semantic token names to their design system and keep web components free of literal colors.