From harness-claude
Guides dark mode color adaptation: inverted hierarchy, lightness-based elevation, desaturated palettes, surface layering, and brand identity preservation for UIs.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Color adaptation for dark themes — inverted hierarchy, reduced saturation, elevation through lightness, surface layering, and maintaining brand identity in dark contexts
Designs dark mode UIs using surface elevation, desaturated colors, contrast ratios (4.5:1 min), accessibility checks, and semantic tokens for smooth light/dark switching.
Designs dark mode interfaces using Tailwind CSS with backgrounds, text hierarchy, borders, cards, status colors, and best practices for contrast and visual hierarchy.
Generates accessible color palettes from brand hex: 11-shade scales (50-950), semantic tokens, dark mode variants, Tailwind v4 CSS, WCAG contrast checks. For design systems and themes.
Share bugs, ideas, or general feedback.
Color adaptation for dark themes — inverted hierarchy, reduced saturation, elevation through lightness, surface layering, and maintaining brand identity in dark contexts
prefers-color-scheme CSS media query with design-informed token mappingDark mode is not "invert colors." The fundamental error is thinking dark mode means swapping black and white. It means rebuilding the entire visual hierarchy for dark surfaces. Key inversions:
Never use pure black (#000000) as the base surface. Pure black creates maximum contrast (21:1) against white text, causing eye fatigue during extended reading. It also makes the screen look like a "hole" — the content floats in void rather than sitting on a surface. Use dark gray instead:
#1C1B1F (slightly warm purple-tinted dark gray)#0d1117 (blue-tinted near-black)#121212 (near-neutral dark gray)#000000 for OLED true black mode (power saving exception), #1C1C1E for standard dark modeDecision procedure: use #121212 or darker as your base surface. Tint it toward your brand hue at very low saturation (2-5%) for chromatic unity. Exception: OLED-specific modes may use #000000 for power efficiency, but this should be an opt-in mode, not the default.
Build surface elevation through lightness, not shadows. In dark mode, shadows are invisible (dark on dark). Replace the shadow-based elevation system with a lightness-based one:
| Elevation Level | Light Mode | Dark Mode (MD3) | Purpose |
|---|---|---|---|
| Level 0 (base) | #FFFFFF | #1C1B1F | Page background |
| Level 1 | #FFFFFF + shadow | #211F26 (+3% lighter) | Cards, raised sections |
| Level 2 | #FFFFFF + deeper shadow | #2B2930 (+6% lighter) | Navigation drawers, modal backgrounds |
| Level 3 | #FFFFFF + heavy shadow | #322F37 (+8% lighter) | Floating action buttons, search bars |
| Level 4 | #FFFFFF + maximum shadow | #393542 (+11% lighter) | Popovers, dropdown menus, tooltips |
Material Design 3 adds a surface tint: overlay the primary color at low opacity (5-14%) on each surface level. This creates chromatic elevation — higher surfaces are not just lighter, they are slightly more tinted toward the brand color, reinforcing the connection between elevation and brand identity.
Reduce saturation for chromatic colors on dark backgrounds. Vivid colors on dark surfaces produce halation — a perceived glow around the color boundary caused by the eye's inability to focus all wavelengths at the same depth. The effect is strongest with warm, saturated colors (red, orange).
Desaturation guidelines:
systemBlue shifts from #007AFF (light) to #0A84FF (dark) — the dark variant is lighter (L+5%) and very slightly desaturated#533afd would need to lighten to approximately #7B6BFF for dark mode to maintain contrast while reducing halation#EF4444 to #FCA5A5 (lighter, less saturated); success green from #22C55E to #86EFACSet text opacity tiers, not fixed text colors. Using opacity-based text creates a self-calibrating system — when the surface color changes across elevation levels, the text contrast adjusts automatically:
| Text Role | Light Mode | Dark Mode | Contrast on #121212 |
|---|---|---|---|
| Primary (headings, body) | rgba(0,0,0,0.87) | rgba(255,255,255,0.87) | ~15.8:1 |
| Secondary (descriptions, metadata) | rgba(0,0,0,0.60) | rgba(255,255,255,0.60) | ~9.1:1 |
| Disabled / Placeholder | rgba(0,0,0,0.38) | rgba(255,255,255,0.38) | ~4.7:1 |
Material Design established these tiers. The 87%/60%/38% ratios ensure that primary text exceeds 7:1 (AAA), secondary exceeds 4.5:1 (AA), and disabled hovers just above 4.5:1 — providing hierarchy through contrast while meeting accessibility requirements at each tier.
Maintain brand recognition across themes. The brand should be instantly recognizable in dark mode. Strategies:
Handle images and media. Full-brightness images on a dark surface create jarring contrast. Solutions:
filter: brightness(0.85)border: 1px solid rgba(255,255,255,0.1) around images in dark mode to separate image content from the dark backgroundHalation occurs when bright, saturated colors are displayed on very dark backgrounds. The eye's lens cannot focus all wavelengths at the same point (chromatic aberration), causing colored text or elements to appear to glow, bleed, or vibrate. The effect is worst for:
Fix: desaturate colors by 15-30% for dark mode use, or increase lightness to shift toward pastel territory. Apple's dark mode color adaptations demonstrate this: every system color shifts lighter and slightly less saturated in dark mode. systemRed goes from #FF3B30 to #FF453A — subtle but measurable.
A robust dark mode implementation uses semantic surface tokens, not hard-coded colors:
--surface-base: #121212 /* Page background */
--surface-raised: #1E1E1E /* Cards, sections */
--surface-overlay: #2C2C2C /* Modals, popovers */
--surface-highest: #383838 /* Tooltips, dropdowns */
--on-surface: rgba(255,255,255,0.87) /* Primary text */
--on-surface-secondary: rgba(255,255,255,0.60) /* Secondary text */
--on-surface-disabled: rgba(255,255,255,0.38) /* Disabled text */
These tokens swap between themes while component code remains unchanged. The on-surface naming convention (from Material Design) makes the relationship explicit: --on-surface is always readable on --surface-base.
Pure Black Background. Using #000000 as the default dark surface. Symptoms: the UI feels like a void, text creates harsh contrast that causes eye fatigue, elevated surfaces require enormous lightness jumps to be distinguishable. OLED power saving is not a sufficient justification for default pure black — even Samsung's One UI uses #1A1A1A, not #000000. Fix: use #121212 to #1C1B1F as your dark surface base.
Direct Palette Inversion. Taking the light mode palette and mechanically inverting it (white becomes black, light gray becomes dark gray). Symptoms: brand colors look wrong, the hierarchy feels off, saturated elements glow uncomfortably. Fix: rebuild the dark palette from the same brand hue but with dark-mode-specific lightness, saturation, and contrast calibration. Light-300 does not become Dark-700 — the relationship is more nuanced than simple number inversion.
Shadow Dependence. Keeping box-shadow as the primary elevation mechanism in dark mode. Symptoms: cards are flat (shadows invisible against dark backgrounds), the UI loses all depth perception, modal overlays do not feel elevated. Fix: replace shadows with surface lightness tiers and/or subtle borders. Material Design 3 uses both surface tint and a 1px hairline border (rgba(255,255,255,0.05)) for dark mode elevation.
Maximum Contrast Text. Using pure white #FFFFFF text on the dark surface. Symptoms: eye fatigue after extended reading, text appears to "shimmer" or strobe on high-DPI screens, the overall feel is clinical and harsh. Fix: use rgba(255,255,255,0.87) or #E0E0E0 for primary text, reserving pure white only for the most critical, short-form content (page titles, modal headers).
Forgetting Borders. Relying on background contrast to separate adjacent components that were separated by shadows in light mode. In dark mode, two adjacent cards at the same surface level are indistinguishable. Fix: add 1px borders using rgba(255,255,255,0.08-0.12) between same-level surfaces. GitHub's dark mode uses this extensively — every card, comment box, and code block has a subtle light border.
Material Design 3 — Surface Tint System. MD3's dark mode overlays the primary color at escalating opacities on each surface level: Level 1 = primary at 5%, Level 2 = 8%, Level 3 = 11%, Level 4 = 12%, Level 5 = 14%. On a base of #1C1B1F with primary #D0BCFF, Level 1 becomes a barely-perceptible warm purple tint. This creates a hierarchy that is both functional (elevated surfaces are lighter) and branded (the elevation is tinted with the product color).
Apple — Semantic Color Adaptation. Apple's system colors are not single values — they are dynamic. Each system color has four variants: light mode default, light mode accessible (higher contrast), dark mode default, and dark mode accessible. systemBlue in dark mode is #0A84FF — 7% lighter and 2% more saturated than light mode's #007AFF. This shift is calibrated per-color: red shifts more in lightness, green shifts less, because each hue has different perceptual behavior on dark backgrounds.
GitHub — Primer Dark Palette. GitHub's Primer design system defines a complete dark mode palette with 10 levels of gray from #0d1117 (canvas default) through #f0f6fc (fg default). The palette maintains a cool blue undertone in dark mode (matching their brand), and every surface level has explicit named tokens: canvas.default, canvas.overlay, canvas.inset, canvas.subtle. Border tokens shift from shadow-based separation in light mode to 1px borders at rgba(240,246,252,0.1) in dark mode.
#121212 to #1C1B1F, optionally tinted toward brand hue).prefers-color-scheme).#121212 or warmer with optional brand tint.