CSS Box Shadows Explained: The Five Parts, Layering, and Why Your Shadows Look Off
2026-08-18
You add box-shadow: 0 2px 4px rgba(0,0,0,0.2) to a card and it looks… fine, but never quite like the shadows on a real design. Or you crank the blur up to make it “soft” and the whole card turns into a gray smudge. The problem is almost never your taste — it’s that box-shadow is actually five numbers doing five different jobs, and most people treat them as one blob. This guide breaks the declaration into parts, then gives you the two-layer pattern that produces the shadows designers actually ship.
The Five Parts
A shadow is one declaration with five values:
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.2);
/* | | | | |
/* | | | | +-- color
/* | | | +------- spread-radius (grow/shrink the shadow box)
/* | | +----------- blur-radius (softness of the edge)
/* | +--------------- offset-y (down)
/* +-------------------- offset-x (right)
Every value is optional except the color — the browser fills in defaults: both offsets 0, blur 0 (a hard-edged shadow), spread 0. Two common misconceptions melt away once you read them as separate parts: a shadow with no blur is a solid rectangle drawn behind the element, and box-shadow never has anything to do with an element’s border — it’s a separate box projected on the same plane.
Offset: Direction Is a Decision
offset-x moves the shadow right (positive) or left (negative); offset-y moves it down or up. Two things worth internalizing:
- Light direction consistency. If every card shadows
0 2px 4px(light from above), keep it that way across the whole page. Mixed offsets are the fastest way to make a UI feel broken. - Offset is not a thickness. People sometimes use a large offset thinking it makes a “stronger” shadow; it just moves it further away, and a shadow far from its element reads as floating, not elevated.
Shadows that sit directly under an element (0 1px 2px) say “I’m resting on the surface.” Shadows with a bigger vertical offset (0 20px 40px) say “I’m floating high above it.”
Blur and Spread: The Two Radii
Blur controls how soft the edge is. Zero blur = a crisp, hard-edged rectangle (good for a border-like effect, bad for a realistic shadow). As the blur grows, the edge becomes a gradient and the shadow extends outward in all directions by roughly half the blur value — which is why 0 2px 4px softens down and slightly out.
Spread grows (positive) or shrinks (negative) the shadow box itself before blurring:
box-shadow: 0 0 0 1px rgba(0,0,0,0.1); /* 1px solid ring — a border, not a shadow */
box-shadow: 0 0 0 3px #3b82f6; /* focus ring */
box-shadow: 0 4px 12px -4px rgba(0,0,0,0.3); /* tight near shadow via negative spread */
The negative-spread trick is everywhere: -4px pulls the shadow box in so the visible shadow hugs the element’s corners even with a medium blur, avoiding the “gray rectangle peeking out” look.
Inset: Shadows That Go Inward
inset flips the shadow inside the element:
box-shadow: inset 0 1px 3px rgba(0,0,0,0.2); /* pressed-in / concave */
box-shadow: inset 0 -1px 0 rgba(255,255,255,0.4); /* top inner highlight */
Inset shadows are how buttons read as “pressed”, wells read as recessed, and toggles read as active. The same five parts apply, just mirrored inward. A common pair is a dark inner shadow at the top of a well plus a light inner shadow at the bottom — that one-two punch is what makes a groove look real.
Layering: The Two-Layer Card Shadow
You can stack any number of shadows with commas — the first listed is drawn on top:
box-shadow:
0 1px 2px rgba(0,0,0,0.06), /* tight contact shadow */
0 8px 24px rgba(0,0,0,0.10); /* large soft ambient shadow */
This two-layer pattern is the single most useful idea in this guide. Real shadows are two different things at once: a contact shadow right under the edge (small, tight, tells you the element’s exact base) and an ambient shadow from the room (big, soft, tells you how high it floats). One medium shadow gets you neither — it looks like a smudge. Match the pattern above and scale the numbers to your element size: small buttons need smaller layers, big cards need the ambient layer pushed further out with a lower alpha.
text-shadow and drop-shadow
text-shadow takes the same parts minus spread: text-shadow: offset-x offset-y blur color. It follows the glyph boxes, which means a big blur on text can look like a halo around each letter box rather than the letters.
For shadows that follow the actual rendered pixels — letters with descenders, transparent PNGs, SVG icons — reach for the filter property instead:
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3));
drop-shadow traces the image’s alpha channel, so it’s the right tool for icons and cutouts, and the wrong tool (blurry, expensive) when all you want is a text shadow on a plain label.
Why Shadows Look Bad: Four Rules
Most “ugly” shadows come down to four fixable decisions:
- Color, not just alpha.
rgba(0,0,0,0.5)on a colored background looks like a bruise. Use a low-alpha black (0.1–0.2 for cards) or a tint of the background color itself. - Two layers, not one. One medium blur = muddy. Tight + ambient = depth. See the layering section above.
- Don’t blur what you can’t afford. Enormous blur radii on many large elements are real paint cost, especially on scroll. Keep the ambient layer modest and let the contact layer do the work.
- Blur must be bigger than offset for a soft look —
0 1px 1pxis basically a hairline, while0 8px 8pxis soft. When a “shadow” looks like a border, it usually means blur ≈ offset.
Quick Reference
box-shadow: offset-x offset-y blur spread color— five independent knobs, only color is required.- Offset moves the shadow; keep light direction consistent across the page.
- Blur softens the edge; spread grows/shrinks the shadow box before blurring.
- Negative spread pulls the shadow in — the trick for “hugging” shadows.
insetdraws the shadow inside: pressed buttons, recessed wells, focus rings via0 0 0 Npx.- Layer two shadows (tight contact + large ambient) for realistic card depth.
text-shadowhas no spread;filter: drop-shadow()follows the actual pixels for icons and cutouts.- A shadow that looks like a border usually has blur ≈ offset; a muddy one has too few layers or too much alpha.
To design a shadow without hand-counting the five numbers, the CSS Box Shadow Generator lets you drag offset, blur, spread, and color with a live preview and copies the CSS — and the Text Shadow Generator does the same for typography. Shadows are part of the same layout toolbox as flexbox and grid; once the box sits where you want, the shadow is what makes it feel real.