What the four lengths do
The order is box-shadow: x y blur spread color, and only the first two are required. x and y offset the shadow, positive values pushing it right and down. Blur is the width of the transition zone at the edge; roughly half of it falls inside the shadow's boundary and half outside, so a 12px blur softens over about that distance rather than starting the fade 12px out.
Spread is the one people skip. It grows or shrinks the shadow shape before the blur is applied. Negative spread is genuinely useful: pairing 0 8px 16px -4px pulls the shadow in from the sides so it sits under the element rather than haloing around it, which is much closer to how a shadow behaves when the object is near the surface it rests on. inset flips the whole thing inward, giving pressed buttons, inset wells, and the inner top shadow that makes a scrollable area look recessed.
Why one layer never looks right
Physical shadows have a penumbra. A light source of nonzero size produces a sharp core near the point of contact and a progressively softer, fainter edge further out, and no single blur radius represents both. That is the whole reason every mature design system — Material, Tailwind, the Apple and Microsoft guidelines — specifies elevation as two or three stacked shadows rather than one.
The recipe that gets you most of the way: a wide layer with large blur and low opacity for the spill, plus a tight layer with a 1 to 3px blur at slightly higher opacity for the contact edge. Something like 0 8px 24px rgba(0,0,0,0.12) over 0 1px 3px rgba(0,0,0,0.10). In a comma-separated list the first shadow is painted on top of the later ones, so the tight contact layer generally goes first.
Elevation should also be consistent rather than per-component. Define three or four levels — resting, raised, floating, overlay — and increase y offset, blur and opacity together across them. A card that lifts on hover should move up one defined level, not receive a bespoke shadow.
Dark backgrounds change the rules
A black shadow on a near-black surface is invisible, because a shadow works by darkening what is behind it and there is nothing left to darken. Raising the opacity to 40 or 50 percent helps a little but mostly produces a grey smudge. The convention that works in dark interfaces is to signal elevation with surface lightness instead: each level up gets a slightly lighter background, and the shadow becomes a secondary cue or disappears entirely.
Colored shadows are the other case worth knowing. A saturated button with a shadow tinted toward its own hue at 20 to 30 percent opacity reads as glowing rather than dirty, where a black shadow under the same button just looks muddy. Take the hue from the element, not from the page.
The performance footnote
Large blur radii are expensive to rasterise, and animating box-shadow forces a repaint on every frame. Hundreds of list items each carrying a wide shadow will show up in a scroll profile. Two mitigations are standard: move the shadow to the container rather than every child where the design allows it, and for hover transitions, pre-render the target shadow on a pseudo-element and animate its opacity, which the compositor can handle without repainting.
None of that is a reason to avoid shadows. It is a reason to know which one of them is in the render path of something that moves.
Questions people ask
What is the difference between box-shadow and filter: drop-shadow?
box-shadow follows the element's border box, including its border-radius, and ignores the content inside. filter: drop-shadow() follows the alpha channel of what is actually painted, so it traces the visible shape of a transparent PNG, an SVG icon, or text. Use drop-shadow for irregular shapes and box-shadow for cards, buttons and inputs. Only box-shadow has spread and inset; drop-shadow has neither.
Why is my shadow being clipped or hidden?
Two common causes. An ancestor with overflow: hidden clips anything drawn outside its box, shadow included. Or a neighbouring element later in the source order paints over it, since the shadow belongs to its own element's stacking context — giving the shadowed element position: relative and a z-index usually resolves it. On a table cell, border-collapse: collapse suppresses cell shadows entirely.
Does spread work the same way as blur?
No. Spread changes the size of the shadow shape geometrically before any blur is applied, so a spread with zero blur gives you a hard-edged rectangle larger or smaller than the element. Blur only softens whatever edge results. Negative spread with a positive y offset is the standard way to keep a soft shadow tucked underneath an element instead of leaking out to the sides.
Can I use box-shadow instead of a border?
Yes, and there are good reasons to. "0 0 0 1px" with no blur draws a crisp one-pixel ring that does not affect layout the way a border does, so adding it on hover or focus causes no reflow. Multiple rings stack for a focus indicator with an offset. The catch is that a shadow ring is not part of the border box, so it can be clipped by an ancestor and will not be visible in forced-colors mode — which is why focus indicators should use outline, whose behaviour under high contrast settings is defined.