The box-shadow property is one of the most underused tools in CSS. Most developers use it for a single subtle drop shadow and stop there — but the full syntax supports layered shadows, inset effects, and colour-based glows that can replace entire images.
This guide covers everything from the basic syntax to advanced multi-layer techniques.
The full syntax
box-shadow: [inset] offset-x offset-y [blur-radius] [spread-radius] color;
Each part:
- offset-x — horizontal distance. Positive = right, negative = left.
- offset-y — vertical distance. Positive = down, negative = up.
-
blur-radius — how soft the edge is.
0= crisp edge,20px= soft fade. Cannot be negative. - spread-radius — expands (+) or contracts (-) the shadow beyond the element size.
-
color — usually
rgba()so you can control opacity. - inset — optional keyword that puts the shadow inside the element.
Why rgba() for colour?
Most shadow colours are variations of black with opacity. rgba(0, 0, 0, 0.12) gives you black at 12% opacity — the element behind shows through, making the shadow look natural regardless of background colour.
/* Avoid this — opaque black shadow looks fake */
box-shadow: 0 4px 8px #000000;
/* Do this — transparent shadow works on any background */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
Multiple shadows: the real power
Separate shadows with commas. The first shadow is drawn on top (closest to the element):
.card {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12), /* tight shadow for sharpness */
0 4px 16px rgba(0, 0, 0, 0.08); /* wide shadow for ambient depth */
}
This two-layer pattern is the basis of Material Design's elevation system. Each layer doubles in size while halving in opacity — the result looks like physically accurate depth.
Inset shadows
The inset keyword flips the shadow inside the element. This is perfect for:
- Buttons in their active/pressed state
- Input fields with a focus ring
- Recessed UI surfaces
/* Pressed button effect */
.btn:active {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* Input focus ring */
input:focus {
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
}
box-shadow as a border
box-shadow: 0 0 0 2px #2f855a creates a 2px ring identical to a border — but without affecting layout. Unlike outline, it respects border-radius exactly.
Useful for stacking multiple rings:
.card:focus {
box-shadow:
0 0 0 2px white, /* inner white gap */
0 0 0 4px #2f855a; /* outer green ring */
}
Glow effects
Zero offset + large blur + saturated colour = glow:
.btn-primary {
box-shadow:
0 0 12px rgba(47, 133, 90, 0.5),
0 0 32px rgba(47, 133, 90, 0.2);
}
Common preset values
| Effect | CSS |
|---|---|
| Soft card | 0 2px 8px rgba(0,0,0,0.08) |
| Material card | 0 1px 3px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.08) |
| Floating modal | 0 8px 24px rgba(0,0,0,0.14), 0 16px 48px rgba(0,0,0,0.08) |
| Focus ring | 0 0 0 3px rgba(66,153,225,0.5) |
| Pressed inset | inset 0 2px 6px rgba(0,0,0,0.2) |
Performance note
box-shadow triggers repaint, not reflow. For static use it is fast — most GPUs handle it without any measurable impact. For animated shadows, add will-change: box-shadow or animate via transform + a static shadow on a pseudo-element for smoother 60fps results.
If you want to build shadows visually without writing code, try the CSS Box Shadow Generator — it gives you sliders for every parameter, a live preview card, 5 presets, and copies the complete CSS with one click.
Top comments (0)