Theme System
The Reality of Dark Mode
Dark mode isn't optional:
- •User preferences split roughly 50/50 (varies by industry)
- •4,605 ADA web accessibility lawsuits filed in 2024
- •European Accessibility Act enforceable since June 28, 2025
- •Pure black backgrounds cause eye strain in research studies
Before Theming (Analyze First)
If adding dark mode to existing code:
- •Audit current colors:
grep -r "bg-" . | grep -v "dark:"— find unhardened colors - •Map to semantic tokens:
bg-white→--surface-default;text-gray-900→--text-primary - •Preserve brand colors: Primary/accent colors should shift, not replace
- •Check contrast in both modes: A color that passes WCAG in light may fail in dark
Never assume a blank slate. Check what exists first.
Reference Loading
CRITICAL for Any Dark Mode Work
**MANDATORY - READ ENTIRE FILE BEFORE WRITING CODE**: ../../references/theming/dark-mode-antipatterns.md Do NOT write dark mode code until you have read this file.
By Task
| Task | MANDATORY Read First | Do NOT Load |
|---|---|---|
| Color palette | theming/color-psychology.md, design-direction/index.md (then industry file) | theming/typography-*.md |
| Design tokens | theming/palette-architecture.md | animation/ files |
| Dark mode (CSS) | theming/dark-mode-css.md, theming/dark-mode-antipatterns.md | seo/ files |
| Dark mode (Tailwind) | theming/dark-mode-tailwind.md, theming/dark-mode-antipatterns.md | copywriting/ files |
| Dark mode (Next.js) | theming/dark-mode-nextjs.md, theming/dark-mode-antipatterns.md | patterns/ files |
| Typography | theming/typography-pairing.md, theming/typography-scale.md | theming/dark-mode-*.md |
WCAG Contrast Requirements
The Formula
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05) Where: - L1 = relative luminance of lighter color - L2 = relative luminance of darker color - Relative luminance = 0.2126×R + 0.7152×G + 0.0722×B (after gamma correction)
Required Ratios
| Content | WCAG AA | WCAG AAA | Notes |
|---|---|---|---|
| Normal text (< 18px) | 4.5:1 | 7:1 | Most body copy |
| Large text (≥ 18px or 14px bold) | 3:1 | 4.5:1 | Headings |
| UI components | 3:1 | — | Buttons, form borders |
| Non-text graphics | 3:1 | — | Icons, charts |
AAA compensates for ~20/80 vision loss (W3C)
Quick Reference: Safe Color Combinations
| Background | Minimum Text (AA) | Safe Text | Never Use |
|---|---|---|---|
White #fff | gray-600 | gray-700+ | gray-400 or lighter |
Gray-100 #f3f4f6 | gray-600 | gray-700+ | gray-500 or lighter |
Gray-900 #111827 | gray-400 | gray-300+ | gray-500 or darker |
| #121212 (dark mode) | gray-400 | gray-300+ | gray-500 or darker |
Blue-600 #2563eb | white only | white | Any gray |
Tool: Check before shipping with npx @contrast-check or WebAIM Contrast Checker
Dark Mode Anti-Patterns (Research-Backed)
| Anti-Pattern | Why It Fails | Research | Fix |
|---|---|---|---|
Pure black #000000 | Eye strain, OLED "black smear", harsh contrast | Studies show dark gray is more comfortable | #121212 or #171717 |
| Dark shadows on dark bg | Shadows disappear, UI feels flat | — | Use elevation (lighter surfaces) or glowing shadows |
| Same saturation colors | Vibrant colors feel "radioactive" on dark | Research on visual comfort | Desaturate 10-20% for dark mode |
| No FOUC prevention | Flash of wrong theme on load | — | Blocking script or next-themes |
| White text on black | Halation for users with astigmatism | Vision research | Off-white #e5e5e5 on dark gray |
| Ignoring form inputs | Inputs invisible or unreadable | — | Explicitly style ALL form elements |
| Skipping interactive states | Hover/focus look wrong in one mode | — | Test EVERY state in BOTH modes |
The Astigmatism Problem
~33% of adults have some degree of astigmatism. Bright text on dark backgrounds can cause "halation" — text appears to glow or blur. Mitigation:
- •Use slightly off-white text:
#e5e5e5orgray-200instead of pure white - •Slightly heavier font weight in dark mode (some systems do this automatically)
Color Psychology (Industry-Specific)
| Color | Best For | Why | Avoid For | Why |
|---|---|---|---|---|
| Blue | Finance, SaaS, Healthcare | Trust, calm, security | Food | Suppresses appetite |
| Green | Health, Finance, Eco | Growth, money, nature | Luxury, Gaming | Feels budget, not exciting |
| Purple | AI/Tech, Premium, Creative | Innovation, luxury | Healthcare, Budget | Too mystical, too expensive |
| Orange/Red | CTAs, Food, Entertainment | Urgency, appetite, energy | Healthcare, Finance | Signals danger/error |
| Black/White | Luxury, Editorial, Fashion | Sophistication, timelessness | Budget brands | Feels too premium |
| Earth Tones | D2C, Organic, Outdoor | Natural, authentic | Tech, Speed-focused | Feels slow, traditional |
CTA Button Color Research (2,588 A/B tests):
- •Blue buttons won 31% of tests
- •Green won 22%, Red won 16%
- •Red CTAs outperformed green by 20% on average (CXL)
Token Architecture
Three-tier system for maintainability:
/* TIER 1: Primitive (never use directly in components) */
:root {
--blue-500: hsl(217, 91%, 60%);
--gray-50: hsl(210, 20%, 98%);
--gray-900: hsl(222, 47%, 11%);
}
/* TIER 2: Semantic (use for general styling) */
:root {
--color-surface: var(--gray-50);
--color-surface-elevated: white;
--color-text-primary: var(--gray-900);
--color-text-secondary: hsl(215, 16%, 47%);
--color-action: var(--blue-500);
--color-border: hsl(214, 32%, 91%);
}
:root.dark {
--color-surface: #121212;
--color-surface-elevated: #1e1e1e;
--color-text-primary: #e5e5e5;
--color-text-secondary: #a3a3a3;
--color-action: hsl(217, 91%, 65%); /* slightly lighter */
--color-border: hsl(215, 14%, 25%);
}
/* TIER 3: Component (specific usage) */
.btn-primary {
background: var(--color-action);
color: white;
}
.card {
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
}
Standard Semantic Token Names
surface-default, surface-elevated, surface-sunken, text-primary, text-secondary, text-muted, text-inverse, action-primary, action-secondary, status-success, status-warning, status-error, border-default, border-strong
Typography by Industry
| Industry | Recommended | Characteristics | Avoid |
|---|---|---|---|
| SaaS/Tech | Inter, Geist, SF Pro | Clean, highly legible, modern | Decorative fonts |
| Finance | Source Sans 3, IBM Plex | Professional, trustworthy | Playful fonts |
| Healthcare | Nunito, Source Sans | Friendly but clear | Heavy serifs |
| Luxury | Playfair Display, Cormorant | Elegant, distinctive | Generic sans |
| Creative/Agency | Space Grotesk, Clash Display | Bold, memorable | Safe/boring fonts |
| Editorial | Georgia, Charter | Readable at length | Geometric sans |
Implementation Approach Decision Tree
Need dark mode?
├── Next.js project?
│ └── Use next-themes (FOUC prevention built-in)
│ npm install next-themes
│ Add ThemeProvider with attribute="class"
│
├── Vite/React SPA?
│ └── Use blocking script + localStorage
│ Add script to <head> before any content
│ Script must run synchronously
│
└── Static/vanilla?
└── CSS media query + JS toggle
@media (prefers-color-scheme: dark)
Optional: localStorage for user preference
Output Checklist
Before delivering theme code, verify:
- • No
#000000pure black backgrounds - •
#121212or darker gray for dark surfaces - • All text passes WCAG AA contrast (4.5:1 normal, 3:1 large)
- • Form inputs explicitly styled for both modes
- • Interactive states (hover, focus, disabled) work in both modes
- • FOUC prevention implemented (if applicable)
- • Tokens follow three-tier architecture
- • Saturated colors desaturated 10-20% for dark mode
MANDATORY PIPELINE (DO NOT SKIP)
This skill is part of a multi-step pipeline. You MUST follow these steps:
PHASE 1: Pre-Implementation (MANDATORY)
Before writing ANY theme/dark mode code:
- •
CRITICAL - Read anti-patterns first:
codeREAD: ../../references/theming/dark-mode-antipatterns.md
DO NOT PROCEED without reading this file.
- •
Load implementation guide (by framework):
codeNext.js → READ: ../../references/theming/dark-mode-nextjs.md Tailwind → READ: ../../references/theming/dark-mode-tailwind.md CSS → READ: ../../references/theming/dark-mode-css.md
- •
Load design context:
codeREAD: ../../references/theming/color-psychology.md READ: ../../references/theming/palette-architecture.md READ: ../../references/design-direction/index.md
- •
If creating typography system:
codeREAD: ../../references/theming/typography-pairing.md READ: ../../references/theming/typography-scale.md
PHASE 2: Analyze Existing Code
Before adding dark mode to existing code:
- •Run:
grep -r "bg-" . | grep -v "dark:"to find unhardened colors - •Map existing colors to semantic tokens
- •Identify brand colors to preserve
- •Check current contrast ratios
PHASE 3: Implementation
Apply theming following:
- •Three-tier token architecture (Primitive → Semantic → Component)
- •WCAG contrast requirements (4.5:1 text, 3:1 UI)
- •Anti-pattern avoidance (no pure black, proper shadows, desaturated colors)
- •FOUC prevention (next-themes or blocking script)
PHASE 4: Post-Implementation (MANDATORY)
After implementing, you MUST:
- •
Run dark-mode-checker agent:
"Let me validate the dark mode implementation..."
Launch using Task tool with
dark-mode-checkeragent:- •Background colors (no pure black)
- •Shadow handling
- •Color saturation
- •FOUC prevention
- •Form element styling
- •
Verify output checklist (from above):
- • No #000000 backgrounds
- • #121212 or darker gray for surfaces
- • WCAG AA contrast passes
- • Form inputs styled both modes
- • Interactive states work both modes
- • FOUC prevention implemented
- • 3-tier token architecture
- • Saturated colors desaturated
- •
If score <80, fix issues and re-validate
PHASE 5: Completion
Only complete when:
- • dark-mode-antipatterns.md was read
- • Framework-specific guide was read
- • dark-mode-checker agent passes (≥80)
- • No pure black backgrounds
- • FOUC prevention implemented
- • All form inputs styled
INTEGRATION WITH OTHER SKILLS
When called FROM component-builder:
- •You're adding theming to built components
- •Run dark-mode-checker before returning
- •Fix any issues found
When called FROM design-audit (fix mode):
- •You're fixing identified theme issues
- •Address specific issues from audit
- •Re-run dark-mode-checker to verify
When called STANDALONE:
- •Run full pipeline above
- •Suggest design-audit for complete validation