Tailwind UI Pattern Registry for humans and agents

dark-mode oklch contrast accessibility wcag tokens

Color & Contrast

Dark Mode Strategy

Systematic approach to dark mode with OKLCH, semantic tokens, and accessibility-first contrast rules.

Design Principles intermediate

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)
dark-modeoklchcontrastaccessibilitywcagtokens