$npx -y skills add dylantarre/design-system-skills --skill dark-modeImplements theme switching with semantic tokens and prefers-color-scheme detection. Use when adding dark mode, light/dark themes, color scheme toggles, or converting hardcoded colors to theme-aware tokens.
| 1 | # Dark Mode Implementation |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Implement robust dark mode using semantic color tokens, CSS custom properties, and system preference detection. Creates a theme system that's maintainable, accessible, and works across frameworks. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Adding dark mode to an existing project |
| 10 | - Setting up a theme system from scratch |
| 11 | - Converting hardcoded colors to semantic tokens |
| 12 | - Implementing system preference detection |
| 13 | - Building a theme toggle component |
| 14 | |
| 15 | ## Quick Reference: Theme Architecture |
| 16 | |
| 17 | | Layer | Purpose | Example | |
| 18 | |-------|---------|---------| |
| 19 | | Primitive tokens | Raw color values | `--color-blue-500: #3b82f6` | |
| 20 | | Semantic tokens | Purpose-based aliases | `--color-primary: var(--color-blue-500)` | |
| 21 | | Theme tokens | Mode-specific values | `--color-bg: var(--color-gray-50)` in light | |
| 22 | | Component tokens | Component-specific | `--button-bg: var(--color-primary)` | |
| 23 | |
| 24 | ## The Process |
| 25 | |
| 26 | 1. **Audit existing colors**: Find all color usage in codebase |
| 27 | 2. **Define primitive palette**: Use color-scale skill if needed |
| 28 | 3. **Create semantic tokens**: Map primitives to purposes |
| 29 | 4. **Define theme tokens**: Light and dark values |
| 30 | 5. **Choose switching mechanism**: CSS classes, data attributes, or media query |
| 31 | 6. **Add system detection**: `prefers-color-scheme` |
| 32 | 7. **Implement persistence**: localStorage or cookies |
| 33 | 8. **Handle edge cases**: Images, shadows, third-party components |
| 34 | |
| 35 | ### Implementation Checklist |
| 36 | |
| 37 | Copy this checklist and track progress: |
| 38 | |
| 39 | ``` |
| 40 | Dark Mode Implementation: |
| 41 | - [ ] Audit colors in codebase (grep for hex, rgb, hsl, color names) |
| 42 | - [ ] Create semantic token layer (bg, text, border, surface, interactive, status) |
| 43 | - [ ] Define light and dark theme values for all semantic tokens |
| 44 | - [ ] Implement switching mechanism (data-theme attribute recommended) |
| 45 | - [ ] Add system preference detection (prefers-color-scheme) |
| 46 | - [ ] Add inline script in <head> to prevent flash of wrong theme |
| 47 | - [ ] Build toggle component with localStorage persistence |
| 48 | - [ ] Handle edge cases (images, shadows, third-party components, form inputs) |
| 49 | - [ ] Test contrast ratios meet WCAG AA in both themes |
| 50 | ``` |
| 51 | |
| 52 | ## Theme Token Architecture |
| 53 | |
| 54 | ### Layer 1: Primitive Tokens (Theme-Independent) |
| 55 | |
| 56 | ```css |
| 57 | :root { |
| 58 | /* Gray scale */ |
| 59 | --color-gray-50: #f9fafb; |
| 60 | --color-gray-100: #f3f4f6; |
| 61 | --color-gray-200: #e5e7eb; |
| 62 | --color-gray-300: #d1d5db; |
| 63 | --color-gray-400: #9ca3af; |
| 64 | --color-gray-500: #6b7280; |
| 65 | --color-gray-600: #4b5563; |
| 66 | --color-gray-700: #374151; |
| 67 | --color-gray-800: #1f2937; |
| 68 | --color-gray-900: #111827; |
| 69 | --color-gray-950: #030712; |
| 70 | |
| 71 | /* Brand colors */ |
| 72 | --color-blue-500: #3b82f6; |
| 73 | --color-blue-600: #2563eb; |
| 74 | --color-blue-400: #60a5fa; |
| 75 | |
| 76 | /* Semantic colors */ |
| 77 | --color-green-500: #22c55e; |
| 78 | --color-red-500: #ef4444; |
| 79 | --color-amber-500: #f59e0b; |
| 80 | } |
| 81 | ``` |
| 82 | |
| 83 | ### Layer 2: Semantic Theme Tokens |
| 84 | |
| 85 | ```css |
| 86 | /* Light theme (default) */ |
| 87 | :root, |
| 88 | [data-theme="light"] { |
| 89 | /* Backgrounds */ |
| 90 | --color-bg-primary: var(--color-gray-50); |
| 91 | --color-bg-secondary: var(--color-gray-100); |
| 92 | --color-bg-tertiary: var(--color-gray-200); |
| 93 | --color-bg-inverse: var(--color-gray-900); |
| 94 | |
| 95 | /* Surfaces (cards, modals, dropdowns) */ |
| 96 | --color-surface-primary: #ffffff; |
| 97 | --color-surface-secondary: var(--color-gray-50); |
| 98 | --color-surface-elevated: #ffffff; |
| 99 | |
| 100 | /* Text */ |
| 101 | --color-text-primary: var(--color-gray-900); |
| 102 | --color-text-secondary: var(--color-gray-600); |
| 103 | --color-text-tertiary: var(--color-gray-500); |
| 104 | --color-text-inverse: var(--color-gray-50); |
| 105 | --color-text-disabled: var(--color-gray-400); |
| 106 | |
| 107 | /* Borders */ |
| 108 | --color-border-primary: var(--color-gray-200); |
| 109 | --color-border-secondary: var(--color-gray-300); |
| 110 | --color-border-focus: var(--color-blue-500); |
| 111 | |
| 112 | /* Interactive */ |
| 113 | --color-interactive-primary: var(--color-blue-500); |
| 114 | --color-interactive-primary-hover: var(--color-blue-600); |
| 115 | --color-interactive-secondary: var(--color-gray-100); |
| 116 | --color-interactive-secondary-hover: var(--color-gray-200); |
| 117 | |
| 118 | /* Status */ |
| 119 | --color-status-success: var(--color-green-500); |
| 120 | --color-status-warning: var(--color-amber-500); |
| 121 | --color-status-error: var(--color-red-500); |
| 122 | --color-status-info: var(--color-blue-500); |
| 123 | |
| 124 | /* Shadows - more visible in light mode */ |
| 125 | --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); |
| 126 | --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); |
| 127 | --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); |
| 128 | |
| 129 | /* Overlay */ |
| 130 | --color-overlay: rgb(0 0 0 / 0.5); |
| 131 | } |
| 132 | |
| 133 | /* Dark theme */ |
| 134 | [data-theme="dark"] { |
| 135 | /* Backgrounds */ |
| 136 | --color-bg-primary: var(--color-gray-950); |
| 137 | --color-bg-secondary: var(--color-gray-900); |
| 138 | --color-bg-tertiary: var(--color-gray-800); |
| 139 | --color-bg-inverse: var(--color-gray-50); |
| 140 | |
| 141 | /* Surfaces - slightly elevated from background */ |
| 142 | --color-surface-primary: v |