Elevation without shadows
Shadow is the absence of light. On a dark background there is barely any light to remove, so depth has to come from somewhere else.
Tag
4 articles on design systems.
A design system's color layer fails in a predictable way: it starts as a palette, accumulates one-off values under deadline, and ends as 200 hex codes nobody can safely change. The defence is to separate the two kinds of token. Primitive tokens name colors — blue-600. Semantic tokens name jobs — surface-raised, text-muted, border-strong. Components reference only the second kind.
That indirection is what makes a theme swap possible. If a button reaches for blue-600, retheming means editing every component. If it reaches for action-bg, retheming means editing one map. It is also what makes dark mode tractable, since the semantic layer is exactly the set of relationships that has to hold in both themes.
Generate the primitive ramps rather than hand-picking them. A ramp built in OKLCH with lightness marching evenly and chroma following a curve will have consistent steps across every hue, which is the property that makes *-600 mean the same thing for blue and for yellow.
Shadow is the absence of light. On a dark background there is barely any light to remove, so depth has to come from somewhere else.
A story about --blue-500, told as a cautionary tale, with the two-layer token structure that prevents it.
One brand color is not a palette. Here is how the 50–950 scale works, why the ends always look washed out, and which step you should actually be using for text.
filter: invert(1) takes about four seconds and produces a theme where your brand blue has turned orange. Here is what a real dark theme requires.