Dark Mode Blueprint
Theme toggle supporting light, dark, and system-preference modes with CSS custom properties, user preference persistence, smooth transitions, and flash-of-wrong-theme prevention.
| Feature | dark-mode |
| Category | UI |
| Version | 1.0.0 |
| Tags | dark-mode, theming, css-custom-properties, prefers-color-scheme, user-preferences, color-scheme |
| YAML Source | View on GitHub |
| JSON API | dark-mode.json |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
theme | select | Yes | Theme | Validations: required |
user_preference | select | No | User Preference | |
system_preference | select | No | System Preference | |
resolved_theme | select | Yes | Resolved Theme | |
transition_duration_ms | number | No | Transition Duration (ms) | Validations: min, max |
component_override | json | No | Per-Component Theme Override | |
css_variables | json | No | CSS Custom Properties Map |
Rules
- respect_system_preference:
- description: When the user has not explicitly chosen a theme (preference is “system”), the resolved theme must match the operating system’s prefers-color-scheme media query value.
- no_flash_on_load:
- description: Theme must be resolved and applied before the first paint. Use an inline script in the document head that reads persisted preference from storage and sets the theme class/attribute on the html element synchronously.
- transition_duration:
- description: Color transitions between themes must use a 200ms duration on background-color, color, and border-color properties. Transitions must not apply on initial page load to prevent visual flicker.
- persist_user_choice:
- description: When a user explicitly selects a theme, persist the choice to local storage (or equivalent) so it survives page reloads and new sessions.
- system_preference_listener:
- description: Register a matchMedia listener for prefers-color-scheme changes. When the user’s preference is “system” and the OS theme changes, update the resolved theme in real time without page reload.
- per_component_overrides:
- description: Individual components may override the global theme via a data attribute or prop. Overridden components must scope their CSS custom properties to avoid leaking into child components.
- css_custom_properties_only:
- description: All theme-dependent colors, shadows, and borders must be defined as CSS custom properties. No hardcoded color values in component styles.
Outcomes
User_selects_theme (Priority: 1)
Given:
- user explicitly selects a theme (light or dark)
Then:
- set_field target:
user_preference— Store chosen theme as user preference - set_field target:
resolved_theme— Apply the chosen theme immediately - set_field target:
theme— Update theme field to the explicit choice - emit_event event:
theme.changed
Result: Theme changes immediately and preference is persisted to storage
System_preference_applied (Priority: 2)
Given:
user_preference(db) eqsystem- operating system reports a preferred color scheme via prefers-color-scheme
Then:
- set_field target:
system_preference— Capture OS color scheme value - set_field target:
resolved_theme— Resolve theme to match OS preference - emit_event event:
theme.changed
Result: Theme follows the operating system preference automatically
System_preference_changes_live (Priority: 3)
Given:
user_preference(db) eqsystem- operating system color scheme changes while page is open
Then:
- set_field target:
system_preference— Update cached system preference - set_field target:
resolved_theme— Resolve theme to new OS preference - emit_event event:
theme.system_preference_changed
Result: Theme updates in real time when OS color scheme changes
Page_loads_without_flash (Priority: 4)
Given:
- page is loading for the first time or after navigation
- persisted theme preference exists in storage
Then:
- set_field target:
resolved_theme— Theme applied synchronously before first paint via inline script
Result: Page renders with correct theme from the first frame with no flash
Component_overrides_theme (Priority: 5) — Error: THEME_OVERRIDE_SCOPE_LEAK
Given:
- a component specifies a theme override different from the global theme
Then:
- set_field target:
component_override— Component renders with its own scoped CSS custom properties
Result: Component displays in its overridden theme while rest of page uses global theme
No_persisted_preference (Priority: 6)
Given:
user_preference(db) not_exists- page loads for the first time
Then:
- set_field target:
themevalue:system— Default to system preference mode - set_field target:
resolved_theme— Resolve theme from OS prefers-color-scheme
Result: First-time visitors see the theme matching their OS preference
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
THEME_STORAGE_UNAVAILABLE | 500 | Unable to persist theme preference. Local storage may be disabled. | No |
THEME_INVALID_VALUE | 400 | Theme value must be one of: light, dark, or system. | No |
THEME_OVERRIDE_SCOPE_LEAK | 500 | Component theme override is leaking CSS custom properties to child elements. | No |
Events
| Event | Description | Payload |
|---|---|---|
theme.changed | Theme was changed by user action or system preference | theme, resolved_theme, source |
theme.system_preference_changed | Operating system color scheme changed while user preference is set to system | system_preference, resolved_theme |
theme.preference_persisted | User theme preference was saved to persistent storage | user_preference |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| theme-configuration | required | Dark mode relies on the theme configuration system for CSS custom property definitions |
| accessibility | recommended | Both light and dark themes must independently meet WCAG AA contrast ratios |
| responsive-layout | optional | Theme toggle UI may need responsive positioning across breakpoints |
AGI Readiness
Goals
Reliable Dark Mode
Theme toggle supporting light, dark, and system-preference modes with CSS custom properties, user preference persistence, smooth transitions, and flash-of-wrong-theme prevention.
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| success_rate | >= 99% | Successful operations divided by total attempts |
| error_rate | < 1% | Failed operations divided by total attempts |
Autonomy
Level: semi_autonomous
Escalation Triggers:
error_rate > 5
Tradeoffs
| Prefer | Over | Reason |
|---|---|---|
| accessibility | aesthetics | UI must be usable by all users including those with disabilities |
Coordination
Protocol: request_response
Consumes:
| Capability | From | Fallback |
|---|---|---|
theme_configuration | theme-configuration | degrade |
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| user_selects_theme | autonomous | - | - |
| system_preference_applied | autonomous | - | - |
| system_preference_changes_live | supervised | - | - |
| page_loads_without_flash | autonomous | - | - |
| component_overrides_theme | supervised | - | - |
| no_persisted_preference | autonomous | - | - |