Box Shadow Generator

The box-shadow property takes up to four lengths and a colour, and only two of them behave the way most people assume. Getting a shadow that looks like light rather than like a grey rectangle is mostly a matter of understanding what blur and spread actually do.

How to use it

  1. Adjust offset, blur, spread, and colour.
  2. The preview updates live as you change values.
  3. Copy the generated CSS.

What each value controls

The syntax is horizontal offset, vertical offset, blur radius, spread radius, colour, with an optional inset keyword.

Taking them in turn:

Layering shadows is what makes them look real

A single shadow is a poor approximation of how light works. Real objects cast a tight, dark contact shadow immediately beneath them and a much softer, wider ambient shadow further out.

Reproducing that means two or more comma-separated shadows on the same property: one with a small offset, small blur, and slightly higher opacity, and another with a larger offset, much larger blur, and lower opacity. The result reads as depth in a way a single shadow does not, and it is what every well-regarded design system does.

The other half of realism is scale. An element that appears close to the surface takes a small, tight shadow; one that appears to float takes a larger, softer, more offset one. Elevation systems in design frameworks are exactly this: a fixed ladder of shadow values so that height is communicated consistently.

Inset shadows

The inset keyword draws the shadow inside the element rather than outside, which produces a recessed or pressed appearance. It is how inputs get their subtle inner edge and how a pressed button state is usually built.

A useful side effect: an inset shadow with zero offset and zero blur but a positive spread renders as a solid inner border that does not affect layout, unlike a real border. It is a common way to add a hairline that can be animated without causing reflow.

The performance cost

A static box-shadow is cheap. Animating one is not.

Changing any shadow value forces the browser to repaint the element and, because the shadow extends beyond the element bounds, the area around it. Doing that sixty times a second on a hover transition is a common source of jank, particularly on large elements and on lower-powered devices.

The standard workaround is to put the shadow on a pseudo-element and animate its opacity instead. Opacity is compositor-driven and does not trigger a repaint, so the transition runs on the GPU. It is more markup for a smoother result, and it is worth it on anything large or frequently hovered.

Large blur radii are expensive even when static, because the browser has to compute a gradient over a large area. A 100px blur on many elements at once is noticeable on a phone.

At a glance

Syntaxx-offset y-offset blur spread color, optional inset
Blur behaviourGradient spans roughly the blur radius, centred on the edge
Multiple shadowsComma-separated, first is drawn on top
TransmittedNothing

Frequently asked questions

Why does my shadow look like a grey box?

Usually pure black at high opacity with too little blur. Drop the alpha to around 0.1 to 0.2, increase the blur, and add a small negative spread so the shadow does not leak out at the sides.

What does spread actually do?

It grows or shrinks the shadow shape before blurring. Negative spread with a positive vertical offset gives a tight shadow that sits under the element rather than haloing it.

Can I use more than one shadow?

Yes, comma-separated, and you generally should. A tight contact shadow plus a wide soft ambient one reads as real depth; a single shadow rarely does.

Is animating box-shadow expensive?

Yes. Every change repaints the element and the area around it. Animate the opacity of a pseudo-element carrying the shadow instead, which runs on the compositor.

Related tools