Color & Contrast
Dark Mode Strategy
Systematic approach to dark mode with OKLCH, semantic tokens, and accessibility-first contrast rules.
How To Use This Guide
Read this first to understand the decision rules before you generate or tweak UI.
At A Glance
- Level
- intermediate
- Type
- Design Principles
- Updated
- 2026-03-22
The Problem with Naive Dark Mode
Simply inverting colors creates:
- Washed-out surfaces (white → pure black is too harsh)
- Broken contrast ratios (accent colors designed for light fail on dark)
- Lost depth cues (shadows invisible on dark backgrounds)
The Elevated Surface Model
Apple, Material Design, and Tailwind all use the same principle: darker base, lighter elevations.
Light Mode: Dark Mode:
┌─────────────────┐ ┌─────────────────┐
│ Card (white) │ │ Card (gray-800) │ ← Elevated = lighter
│ │ │ │
│ Page (gray-50) │ │ Page (gray-900) │ ← Base
│ │ │ │
│ Root (white) │ │ Root (gray-950) │ ← Deepest
└─────────────────┘ └─────────────────┘
System Prompt for Dark Mode Design
You are implementing dark mode for a Tailwind CSS website.
Rules:
1. NEVER use pure black (#000). Use gray-950 or oklch(12% 0.005 hue).
2. Elevated surfaces get LIGHTER, not darker. Card > Page > Root.
3. Reduce accent saturation by 10-15% in dark mode (OKLCH chroma).
4. Shadows become glows: dark mode shadows use accent color at 5-10% opacity.
5. Borders get MORE visible (increase opacity from 8% to 15%).
6. Text: primary = 90% lightness, secondary = 65% lightness, muted = 45%.
7. Never put saturated colors on large surfaces — only accents and CTAs.
8. Test contrast: WCAG AA minimum (4.5:1 for text, 3:1 for large text).
9. Use prefers-color-scheme for system detection + manual toggle.
10. Images: reduce brightness to 90%, increase contrast slightly.
Token Architecture:
- Define semantic tokens (:root + .dark override)
- Component tokens reference semantic tokens
- NEVER use Tailwind dark: on component-level colors
OKLCH Dark Mode Token Set
/* Semantic tokens with OKLCH — hue 75 (warm sand) */
:root {
--color-bg: oklch(98% 0.005 75);
--color-surface: oklch(100% 0 0);
--color-surface-elevated: oklch(100% 0 0);
--color-text: oklch(18% 0.01 75);
--color-text-secondary: oklch(45% 0.01 75);
--color-border: oklch(88% 0.008 75);
--color-accent: oklch(55% 0.18 75);
--color-accent-hover: oklch(48% 0.18 75);
}
.dark {
--color-bg: oklch(14% 0.008 75);
--color-surface: oklch(18% 0.008 75);
--color-surface-elevated: oklch(22% 0.008 75);
--color-text: oklch(92% 0.005 75);
--color-text-secondary: oklch(65% 0.008 75);
--color-border: oklch(28% 0.008 75);
--color-accent: oklch(65% 0.15 75); /* reduced chroma */
--color-accent-hover: oklch(72% 0.15 75);
}
Contrast Ratio Quick Reference
| Pair | Min Ratio | WCAG Level | |------|-----------|------------| | Body text on bg | 4.5:1 | AA | | Large text (18px+) on bg | 3:1 | AA | | UI components (borders, icons) | 3:1 | AA | | Decorative elements | None | — |
Checklist
- [ ] No pure black backgrounds — use gray-950 or oklch(12-14%)
- [ ] Elevated surfaces are lighter than base
- [ ] Accent chroma reduced by 10-15% in dark mode
- [ ] Shadows replaced with subtle glows (accent-tinted)
- [ ] Border opacity increased (8% → 15%)
- [ ] All text passes WCAG AA contrast (4.5:1)
- [ ] Large text passes 3:1 minimum
- [ ] System detection + manual toggle supported
- [ ] Images dimmed (brightness: 0.9)
- [ ] Tested on actual dark display (not just CSS preview)
Related Content
Pattern
Hero Base
Clean hero baseline with headline, body copy, and dual CTA for design-system adoption.
Pattern
Product Cards
Three-column product card grid with image placeholder, badge, price, and CTA link.
Pattern
Navbar Dark
Dark-themed top navigation with logo, centered links, and accent CTA for bold product sites.
Snippet
Frosted Glass
Classic frosted glass effect with backdrop-filter blur. Configurable blur strength, opacity, and border. Includes dark mode and fallback.