CSS box-shadow takes four lengths and a colour, and the difference between a shadow that reads as depth and one that reads as a grey smudge is entirely in how those numbers relate to each other. This builds one visually, with as many stacked layers as you want, previewed on light and dark.
Why one shadow never looks right
Reach for a single box-shadow and the instinct is to raise the blur until it looks soft. What you get is a uniform grey halo — the same amount of darkness everywhere around the element, which is not how anything casts a shadow.
Real shadows have two parts. A tight, relatively dark one right under the object where it meets the surface, and a wide, faint one spreading outward as the light falls off. Stack those and the element looks lifted; use one blur for both jobs and it looks like it was pasted on.
- Contact layer — small offset, small blur, slightly higher opacity. This is what tells the eye where the object touches.
- Ambient layer — larger offset, much larger blur, lower opacity. This is the depth.
- A third, wider and fainter still, for anything that should look genuinely raised — a modal, a popover, a dragged card.
What the four numbers do
They always appear in the same order, and the third and fourth are the ones that get confused.
- Horizontal and vertical offset — where the shadow sits relative to the box. Most interfaces light from above, so the horizontal offset is usually 0 and the vertical is positive.
- Blur — how far the edge fades. Zero gives a hard-edged copy of the box, which is occasionally what you want for a brutalist border.
- Spread — grows or shrinks the shadow before it is blurred. A small negative spread is the quiet trick behind most good shadows: it pulls the shadow in so it does not peek out around the sides of the element.
- Inset — draws the shadow inside the box instead of outside, for pressed buttons, wells and inset fields.
Order matters
Layers are painted first to last, so the first one in the list sits on top. When two layers overlap — which is the whole point of stacking them — the order changes what you see, and it is not something you can reason about from the values alone.
That is why the layers here can be reordered, with buttons rather than by dragging, so the reordering works from the keyboard as well as the mouse.
Light and dark are different problems
A shadow works by darkening what is behind it. On a white page that reads immediately. On a near-black surface there is very little left to darken, so the same shadow simply disappears, and an interface that ships both themes ends up with cards that float in one and sit flat in the other.
The usual answer on dark surfaces is not a stronger shadow but a different signal: a slightly lighter surface colour for the raised element, a subtle light border along its top edge, or both. The preview here switches between light and dark for exactly this reason — so the disappearance is something you see now rather than after release.
Cost
Shadows are painted, not composited, so a large blur across many elements is real work for the browser — most visibly on a long list where every row carries one, and on mobile hardware.
It only becomes a problem when the shadow changes: animating box-shadow forces a repaint on every frame. The standard workaround is to put the shadow on a pseudo-element and animate its opacity instead, which the compositor can handle without repainting.
FAQ
How do I make a shadow look realistic?
Use two or three layers instead of one, keep the opacities low — 0.05 to 0.2 per layer, not 0.5 — and give the outer layers a larger blur and a more negative spread than the inner ones. A shadow you notice is usually too strong; the goal is that the element looks raised, not that the shadow looks impressive.
What does the spread value do?
It grows the shadow’s shape before it is blurred, and a negative value shrinks it. Negative spread is what stops a soft shadow from spilling out around the sides of an element — it is the difference between a shadow that sits underneath and one that surrounds.
Why can I not see my shadow on a dark background?
Because a shadow darkens what is behind it, and there is not much darkness left to add. On dark surfaces, signal elevation with a lighter surface colour or a subtle top border instead. Switch the preview here to dark to see it happen before your users do.
What is the difference between box-shadow and drop-shadow?
box-shadow follows the element’s box and its border-radius. The filter: drop-shadow() function follows the shape of what is actually painted, including transparency, which is what you need for a PNG with a cut-out or an irregular SVG. For rectangles and rounded rectangles, box-shadow is cheaper and behaves more predictably.
Can I use several shadows on one element?
Yes — that is what the comma-separated list in the output is. There is no practical limit, though beyond three or four layers you are adding paint cost without adding anything anyone can see.