How CSS box-shadow works

Offset, blur, spread, colour and inset, one value at a time, with what each does to the pixels.

The box-shadow syntax

MDN opens its reference page with the plainest possible definition, and it is worth holding onto because everything else on this page is a refinement of it:

"The box-shadow CSS property adds shadow effects around an element's frame."

MDN Web Docs, box-shadow

Around the frame. Not around the content, not around the visible pixels: around the rectangle the element occupies, rounded by its border-radius. That is why a shadow on a transparent PNG draws a box rather than following the picture, and why the property is such a good fit for cards, buttons and panels, which are boxes anyway.

A single shadow is up to four lengths, a colour and an optional keyword, in this order:

CSS
box-shadow: 0 12px 24px 0 rgba(27, 35, 64, 0.18);
/*          |  |    |   |  colour                   */
/*          |  |    |   spread radius (optional)   */
/*          |  |    blur radius (optional)         */
/*          |  y offset                            */
/*          x offset                               */

The two offsets are required. Blur and spread are optional and default to zero. Colour is optional too, defaulting to the element's color, but I would never rely on that in a shared stylesheet: the day someone changes the text colour, every shadow changes with it. The generator on this site always writes the colour and always writes the blur, and it drops the spread only when it is zero, which is the shape most stylesheets use.

X and Y offset

The first length moves the shadow horizontally, the second vertically. Positive X moves it right, positive Y moves it down, and negative values go the other way. In the generator these are the two values you change by dragging the handle, and it is the drag that teaches the idea faster than any diagram: the offset is where the shadow's centre sits relative to the box's centre.

x 12
y 12
x -12, y -12
Hard shadows (blur 0) make the offset obvious. Right, down, then up-left.

Two habits follow from this. First, decide where the light is coming from and keep every offset on the page consistent with it. Almost every real interface lights from above, which is why nearly every shadow you will ever write has a positive Y and an X of zero or close to it. Second, keep offsets small. A shadow offset by 40 pixels says the object is floating 40 pixels off the page, and very few interface elements are meant to look like that.

Blur radius

The third length is the blur radius. Zero gives a hard-edged shadow the same shape as the box. Anything above zero softens the edge, and the specification is unusually precise about how:

"The resulting shadow must approximate a Gaussian blur with a standard deviation equal to half the blur radius."

W3C, CSS Backgrounds and Borders Module Level 3

What that means for your eyes: the shadow fades out over a band roughly as wide as the blur value, centred on where the hard edge would have been. Half the band sits inside the box's outline, half outside. So a 24 pixel blur does not add 24 pixels of shadow beyond the box; it adds about 12 of visible fade beyond it and softens 12 inward. MDN puts the practical effect in one line: "The larger this value, the bigger the blur, so the shadow becomes bigger and lighter." Bigger and lighter together, because the same ink is spread over more area.

blur 0
blur 12
blur 48
Same offset and opacity; only the blur changes. Note how the 48 version is both wider and paler.

A rule of thumb I use in client work: blur about twice the Y offset for a natural look. 4 and 8, 12 and 24, 24 and 48. When the blur is smaller than the offset the shadow looks like a second object; when it is much larger it turns into a glow.

Spread radius

The fourth length grows or shrinks the shadow before the blur is applied. The MDN wording is that positive values "cause the shadow to expand and grow bigger, negative values will cause the shadow to shrink". Positive spread with zero blur is the cheap way to draw a border that does not affect layout, which is how the focus rings on this site are drawn. Negative spread is the more useful trick and the one people miss.

spread 4
spread 0
spread -12
Left: spread as a ring. Middle and right: the same drop shadow with and without a negative spread pulling it in from the sides.

With a negative spread the shadow is narrower than the box, so it only shows where the offset pushes it out: beneath the element and not at its sides. That reads as a light directly above the object. Material Design's key shadow uses this on every elevation level, and I reach for it whenever a card looks like it has a grey halo instead of a shadow.

Colour and opacity

The colour can be any CSS colour, but a shadow almost always needs transparency, and the way to get it is an alpha channel: rgba(27, 35, 64, 0.18), or the newer rgb(27 35 64 / 18%), or an eight digit hex such as #1b23402e. The generator keeps colour and opacity as separate controls and combines them into rgba() when it writes the code, because rgba() is the one form that every browser back to 2012 understands.

Two things about the colour itself. Pure black at low opacity produces a grey shadow, and grey shadows look like dirt on a coloured page. Tint the shadow toward the page it falls on: a navy on a cool white page, a warm brown on a cream one. And keep the opacity low, typically between 0.06 and 0.25 per layer. Every shadow on this site is tinted to the same ultramarine as the brand colour, at opacities between 0.04 and 0.18, and stacked; you will not find a single black shadow in the stylesheet.

The inset keyword

Add inset and the same four lengths draw the shadow inside the box instead of outside it. MDN describes the effect as "as if the content is pressed into the box", and adds the detail that decides how it composites: inset shadows "are clipped to the element's padding box and appear above the background but below the content". So an inset shadow never covers your text, but it does sit on top of a background image, which is what makes the frosted edge on a glass card possible.

outer
inset
both
Outer, inset, and the pairing that lifts almost every raised surface on this site: a one pixel white inset highlight over a soft outer shadow.

There is a full guide to inset shadows further in; the short version is that they are for wells, pressed states and edge highlights, and they look wrong on anything that is supposed to float.

Multiple shadows

One element can carry as many shadows as you like, comma separated. The order matters: MDN states that "the first specified shadow is on top". So the sharp, tight shadow goes first and the wide, soft one last, and an inset highlight goes first of all if it must stay visible over everything else.

CSS, four layers
box-shadow:
  0 1px 2px rgba(27, 35, 64, 0.06),
  0 4px 8px rgba(27, 35, 64, 0.06),
  0 12px 24px rgba(27, 35, 64, 0.08),
  0 32px 64px rgba(27, 35, 64, 0.12);

That list is the generator's own output for the layered soft shadow example. The reasoning behind the ratios is the subject of the layered shadows guide. Here the point is only that the syntax allows it and the order is top-first.

Mistakes I see in client stylesheets

I have been hosting and fixing small business websites since 2002, and box-shadow is a property I end up correcting again and again in inherited page-builder themes. The same five faults account for nearly all of it.

  1. Vendor prefixes. -webkit-box-shadow and -moz-box-shadow have not been needed since Chrome 10, Firefox 4 and Safari 5.1. They triple the bytes for nothing. Delete them.
  2. Black shadows. rgba(0, 0, 0, 0.5) is the default in a lot of page builders and it makes every card look like it is sitting in soot. Tint it and halve the opacity.
  3. One giant blur. 0 0 50px with no offset is a glow, not a shadow. If the element is meant to be lit from above, give it a Y offset.
  4. Animating the shadow. A hover state that transitions box-shadow repaints the element on every frame. The performance guide shows the cheap alternative.
  5. Shadows inside overflow: hidden. The shadow is painted outside the box, so a clipping parent slices it off. Either move the overflow rule or give the parent padding equal to the shadow's reach.
Try these values in the generator

The link opens the tool loaded with the single shadow from the top of this page.

Frequently asked questions

What is the order of values in CSS box-shadow?

Offset X, offset Y, blur radius, spread radius, then colour. The first two are required. The optional inset keyword can go at the start or the end. Colour can come first or last too, but keeping it last is the convention every generator and most codebases follow.

Does a bigger blur make the shadow darker or lighter?

Lighter. Blur spreads the same amount of ink over a larger area, so the shadow gets bigger and fainter at the same time. If you want a softer edge without losing weight, raise the opacity a little as you raise the blur.

Can box-shadow spread be negative?

Yes. A negative spread shrinks the shadow before it is blurred, which is how you get a shadow that only shows beneath an element and not at its sides. Material Design uses negative spread on its umbra layer for exactly this reason.

Why does my box-shadow not show up?

The usual causes: a parent with overflow hidden clipping it, an opacity of zero, a colour with no alpha on a background of the same colour, or a shadow drawn behind a sibling with a higher z-index. Try a hard red shadow with no blur first; if that shows, the problem is the values, not the property.

Does box-shadow affect layout?

No. A shadow is painted outside the box model and never pushes other elements, which is why it is safe for focus rings and hover lifts. It can be clipped by an ancestor with overflow hidden, so leave room where that matters.

Is box-shadow the same as drop-shadow()?

No. box-shadow follows the element's rectangle and border-radius. The drop-shadow() filter follows the alpha channel of the element, so it wraps around transparent PNGs, SVG shapes and text. Use drop-shadow() for irregular shapes and box-shadow for boxes.

Sources

  1. MDN Web Docs: box-shadow
  2. W3C, CSS Backgrounds and Borders Module Level 3, section 7 (box-shadow)
  3. MDN Web Docs: backdrop-filter
  4. Can I use: CSS3 Box-shadow