ContentsAct II · MakeDesign it yourself
Move 26
Two-part shadows, five levels
The thing that reads as designed is not the recipe. It is that there are only five of them and nobody is allowed to invent a sixth.
Search your codebase for box-shadow. Most teams find between fifteen and forty distinct
values, every one of them written by somebody solving one component at a time, none of them
related to any other.
That is what makes an interface look assembled rather than designed, and the fix is less about the shadow recipe than the title of this chapter implies.
What every design system actually agrees on
I checked seven published systems, and the agreement is not about construction. It is about closure: a small, named, fixed set of elevation levels, with one-off shadows forbidden.
Material 3 ships six levels. Radix six. Tailwind seven. Polaris seven. Primer seven. Atlassian three. Bootstrap three.
Different numbers, same shape. Somewhere between three and seven named steps, and a rule that you pick one rather than write one.
Where the title overreaches
Both halves, honestly.
Two parts is one construction among several. It is Material’s, Atlassian’s, and Tailwind’s for four of its seven levels. But Polaris and Bootstrap ship single-layer shadows, and Primer and Radix go up to five layers. So a two-part shadow is a good default rather than the technique.
The reasoning behind it is sound, and it comes from Refactoring UI, whose Creating Depth chapter includes a lesson called “Shadows can have two parts”: “The first shadow is larger and softer and accounts for the light cast behind an object by a direct light source”, with a second, tighter, darker shadow for the contact edge. Their observation about what the second one does as things rise is the useful part: “As an object gets further away from a surface, the small, dark shadow created by a lack of ambient light slowly disappears.”
And “five levels” has no primary source. I could not find it prescribed anywhere. The nearest trace is one reader’s public notes on Refactoring UI, recording “Establish an elevation system: 5 options is usually enough”. Material happens to have five non-zero levels. That is the whole provenance. Five is a reasonable number. It is not a rule.
The dial nobody teaches
Here is the one genuinely independent finding, and it contradicts how most developers tune shadows.
Cavanagh, Casati and Elder found that perceived depth scales linearly with shadow offset, and is relatively unaffected by blur.
Offset is the dial. Most people reach for blur first, spend an afternoon adjusting it, and change the perceived elevation very little. If you want something to read as further from the surface, move it down, do not soften it.
The move
Define five elevation levels once, name them, and make writing a sixth
box-shadowanywhere in the codebase a thing that requires a conversation.
/* BEFORE: found in one codebase, all in production */
box-shadow: 0 2px 4px rgba(0,0,0,.1);
box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.24);
box-shadow: 0 3px 6px rgba(0,0,0,.16);
box-shadow: 0 10px 20px rgba(0,0,0,.19);
/* ...and 23 more */
/* AFTER */
:root {
--elev-1: 0 1px 2px rgb(0 0 0 / .06), 0 1px 1px rgb(0 0 0 / .10);
--elev-2: 0 2px 4px rgb(0 0 0 / .06), 0 1px 2px rgb(0 0 0 / .10);
--elev-3: 0 6px 12px rgb(0 0 0 / .08), 0 2px 4px rgb(0 0 0 / .08);
--elev-4: 0 12px 24px rgb(0 0 0 / .10), 0 4px 8px rgb(0 0 0 / .06);
--elev-5: 0 24px 48px rgb(0 0 0 / .12);
}
/* offset grows fastest, because offset is what reads as height.
the tight second layer fades out as elevation rises, then
disappears entirely at 5. */
Note what the values do as they climb. The first number in each pair, the vertical offset, roughly doubles each step. The tight contact shadow shrinks and then vanishes. Blur grows, but it is along for the ride rather than doing the work.
What it costs
Shadows are the least evidence-backed thing in this part of the book, and I would rather say so than dress up a recipe. Beyond the offset finding, this is craft: an aesthetic convention that current interfaces share, which is a real reason to follow it and not the same as a reason to believe it.
They are also nearly invisible in dark mode. A dark shadow on a dark surface does almost nothing, which is why Material shifts to surface tinting at higher elevations instead. If you ship both themes, elevation needs a second mechanism, not a darker shadow.
And five levels is a constraint you will fight. Somebody will need something between 2 and 3. Give them 3. The whole value is in the closure, and a system with five levels plus exceptions is a system with forty shadows and extra steps.
Try this week
Grep for box-shadow and count the distinct values. The number itself is usually the
argument.
Then define five custom properties, map every existing shadow onto the nearest one, and delete the originals. It is mechanical, it takes an afternoon, and nothing will look meaningfully different on any single component.
Open a page with six components on it, though, and it will look like somebody designed it, because for the first time the depths on that page are related to each other.