Architecture: Three-Layer Engine
Ultimate Dark Mode applies dark mode through three layers, each progressively more aggressive. Sites with a dedicated CSS override skip Layers 2 and 3 to avoid breaking app JavaScript.
Layer Flow
Layer 1: Native Dark Mode Forcing
Always applied. Zero side effects.
- Sets
color-scheme: darkon:root - Injects
<meta name="color-scheme" content="dark"> - Activates native dark mode on sites that already support
prefers-color-scheme: dark
This layer alone handles well-built sites that implement their own dark theme. Most modern sites and web apps respond to this signal correctly.
Layer 2: Generic CSS (styles/darkmode.css)
Only applied when no site-specific override exists.
- Applies an oklch-based dark palette to common HTML elements
- All rules are scoped under
html[data-darkmode] - Covers backgrounds, text colors, borders, inputs, and other standard elements
The oklch Color Palette
| Token | Value | Use |
|---|---|---|
| Base background | oklch(0.15 0.01 260) | Page/app background |
| Surface | oklch(0.20 0.01 260) | Cards, panels, modals |
| Elevated surface | oklch(0.25 0.01 260) | Buttons, inputs |
| Border | oklch(0.30 0.01 260) | Dividers, borders |
| Muted text | oklch(0.65 0.01 260) | Secondary text, placeholders |
| Body text | oklch(0.88 0.01 260) | Primary text |
| Heading text | oklch(0.93 0.01 260) | Headings, emphasis |
| Link | oklch(0.75 0.15 250) | Links |
| Accent | oklch(0.65 0.15 250) | Active states, highlights |
Layer 3: JS-Assisted (MutationObserver)
Only applied when no site-specific override exists.
- Watches for DOM mutations and processes elements with inline styles
- Remaps light backgrounds to dark equivalents
- Debounced via
requestAnimationFrameto avoid performance issues
This layer catches dynamically-injected inline styles that CSS alone cannot override.
Why Overrides Skip Layers 2 and 3
Complex web apps (Google Sheets, Notion, Slack, etc.) manage their DOM and read computed styles back in JavaScript. When our generic CSS injects oklch() values into elements these apps control, their JS crashes because they cannot parse oklch().
Real-world example: Google Sheets reads element.style.color, gets oklch(0.88 0.01 260), passes it to an internal color parser that expects rgb() format, and throws: Error in protected function: hg'oklch(0.88 0.01 260)' — crashing the entire app.
The fix is site-specific overrides that target only known-safe selectors (toolbar, sidebar, menus) and avoid touching elements that the app's JS manages. See ADR-001: No filter:invert() for more on this design decision.
Site Override Architecture
Override files follow strict rules:
- Wrapped in
@layer darkmode.overrides { ... } - Every selector scoped with
html[data-darkmode] - Never targets elements managed by the app's JavaScript
- Never uses
filter: invert()orfilter: brightness()on containers - Preserves media elements (
img,video,canvas,svg,picture,iframe) - Prefers semantic selectors (
[role="dialog"],[aria-label]) over fragile class names