SuperAPI AI Directory

dark-mode

Design & creation

View repository

Updated 10-15 19:52

About this skill

Deriving a Dark Theme

A dark theme is derived from a finished light theme by re-binding roles to steps, never by mirroring the pixels. The ink/canvas pair does flip — the near-white step becomes the near-black one — but elevation does not: a raised surface is lighter than its page in both themes, so a mechanical inversion yields black cards floating on grey, and is the most common way a dark mode ends up wrong without anyone naming why. The default posture is that dark mode is optional: Benji Taylor's site carries exactly two prefers-color-scheme rules and both sit inside embedded demos, so the site itself has none. Commit or build it properly; half-built is the worst of the three. This skill maps an existing set into the second theme — color picks the values and owns contrast, and this skill never picks a brand hue. surfaces owns the elevation ladder within a theme (shadow recipes, nested radii, hairlines); this skill owns expressing that same ladder as surface lightness once shadows stop reading.

Find the theme switch before writing a single token. Read whether the project flips a [data-theme] attribute, a .dark class, prefers-color-scheme, or a provider such as next-themes, and put every dark value behind that exact mechanism. If a numbered variable scale already flips at one root selector, add to it. Never introduce a second switching mechanism, and never answer a dark-mode bug with per-component dark: overrides on top of a variable system built to make them unnecessary.

Quick Reference

Open this When
derivation-table.md You are producing the actual dark values and need the role-by-role mapping — canvas, raised surface, border, ink, accent, status, shadow, image.

The Derivation Order

Each step depends on the one above it.

  1. Surfaces first. Darkest canvas, then each elevation level lighter than the one below.
  2. Borders and rings second, against the surfaces you just fixed.
  3. Ink third, then measure every pair per color.
  4. Accent and status fourth, moving chroma and lightness together.
  5. Images, illustrations, and shadows last — they sit on top of everything above.

Core Principles

  1. Point elevation toward lighter in both themes. In light mode the page is grey and the card is white; in dark mode the page is near-black and the card is a lighter grey. The topmost surface is always the brightest in its theme. Exception: a modal scrim, which goes darker than the canvas because its job is to remove depth rather than add it.

  2. Never bottom out at #000. Pure black leaves nothing below the base surface, so the elevation ladder loses its floor, and on OLED the bottom step becomes an unlit void the next step visibly detaches from. Use #171717 as the darkest canvas and #1f1f1f as the first level above it. Exception: video letterboxing and full-screen media chrome, where true black is the right frame for the content.

  3. Never top out at #fff either. Maximum-white ink on a near-black surface halates, thickening the letterforms and tiring the eye over a long session. The lightest ink step is #fafafa. Exception: one small element that must read as the brightest thing on screen — a live indicator, a focused caret.

  4. Replace shadows with lightness steps plus one ring. A stacked depth shadow is invisible against a dark surface, so collapse each shadow token to a 1px white ring at 0 0 0 1px rgb(255 255 255 / 0.09), hover 0.14, and let surface lightness carry the depth. Exception: an overlay floating above a photo or video, where a real shadow still reads because the backdrop is not a theme surface.

  5. Flip elevation rings to translucent white, but keep separation borders solid. A compositing ring (white at 0.09) reads as light catching an edge; a separation border built the same way glows. Dividers, gridlines, and input edges go solid in the #2a2a2a range instead of rgba(255,255,255,0.1). Exception: a border over a translucent or blurred material, where solid would reveal the panel's true colour.

  6. Pull brand chroma back 20–30%, and raise L rather than lowering it. A hue carried across at full chroma vibrates against a dark ground; reducing chroma settles it. But chroma ceilings collapse toward the poles, so a merely-darkened accent loses vividness twice over — move it up the ramp and desaturate. Exception: destructive red, tuned against contrast rather than comfort because it must stay loud enough to stop someone.

  7. Declare color-scheme: dark on the root. Without it, native form controls, scrollbars, the caret, and the autofill background stay in their light rendering and give the theme away instantly — and no amount of custom-property work fixes any of them. Exception: none. It is one declaration.

  8. Dim assets until their own ground joins the surface ladder. A screenshot or illustration authored on white out-glows every surface around it. The check is measurable: the asset's largest flat area must land below L 0.6, the same cutoff separating a light surface from a dark one. Re-export against the dark canvas where you can; dim in CSS only where you cannot. Exception: photography and user uploads, which are the subject and are never dimmed — give them the 1px white outline at 10% instead.

  9. Never reach for filter: invert(). It flips photographs into negatives, turns logos into their own complements, and drags every hue 180° around the wheel. No asset class survives it. Exception: none.

  10. Suspend transitions across the theme swap. Every component carries its own duration, so a live switch cascades raggedly for as long as the slowest one runs. Kill transitions for the frame of the swap — with next-themes that is disableTransitionOnChange on the provider. Exception: a deliberately choreographed theme transition built as one animation, not thirty.

  11. Re-measure every pair; do not trust the mirror. Perceptually even L steps make the ink/canvas flip safe, but muted text, disabled states, and placeholder greys routinely fail in exactly one theme. Run color's thresholds again on the dark set. Exception: none — half of all dark-mode contrast failures are greys that passed in light.

  12. Ship light-only rather than half-built. A dark theme is a second complete surface set with its own contrast pass, not dark: classes over the 60% of components someone reached. If the product is not used at night, beside a dark editor, or in media contexts, that budget buys more elsewhere. Exception: once you claim support, honouring prefers-color-scheme is not optional — a toggle with no system default is a half-build too.

Smells and Fixes

Smell Fix
Cards vanish in dark mode The light shadow stack is still active; collapse to the ring and raise the surface step
Cards darker than the page behind them Elevation was mirrored; point it toward lighter
#000 canvas Raise to #171717 so the ladder has a floor
Text feels smeared or too heavy #fff ink; drop to #fafafa
Dividers glow Translucent white on a separation border; go solid #2a2a2a
The accent buzzes against the surface Cut chroma 20–30% and move it up the ramp
Scrollbars, caret, or autofill still light Missing color-scheme: dark on the root
A screenshot blazes white in the middle of the page Its flat ground is above L 0.6; re-export or dim it
Photos look like negatives filter: invert() — remove it entirely
The page ripples for half a second on toggle Transitions not suspended during the swap
dark: overrides sprinkled through components Flip the variable scale at the theme root instead

Reporting a Derivation

Report the dark theme as a table, one row per role: the light value, the derived dark value, and the rule that produced it — mirrored, elevation-inverted, chroma-reduced, ring-replaced, re-measured. Then list separately every pair that failed in dark and passed in light, with its repaired value; that set is where a reviewer's attention pays most.

Checklist

  • Elevation points toward lighter in both themes
  • Darkest canvas is #171717, not #000; lightest ink is #fafafa, not #fff
  • Shadow stacks collapsed to 0 0 0 1px rgb(255 255 255 / 0.09)
  • Elevation rings translucent white; separation borders solid #2a2a2a
  • Accents desaturated 20–30% and moved up the ramp, not down
  • color-scheme: dark declared on the root
  • Every asset's flat ground sits below L 0.6; no filter: invert() anywhere
  • Transitions suspended for the theme swap
  • Every pair re-measured against color's thresholds in the dark set
  • The theme is complete, or the project ships light-only on purpose