CSS Box Shadow: A Complete Guide with Examples
Master every parameter of box-shadow, layer multiple shadows for depth, and understand elevation design systems.
Syntax Breakdown
box-shadow: [inset] offset-x offset-y [blur-radius] [spread-radius] color;
/* Example */
box-shadow: 4px 8px 16px 0px rgba(0, 0, 0, 0.15);- offset-x: Horizontal shift. Positive moves right, negative moves left.
- offset-y: Vertical shift. Positive moves down, negative moves up.
- blur-radius: How blurry the shadow edges are. 0 = hard edge. Required to be ≥ 0.
- spread-radius: Expands (+) or contracts (−) the shadow before blurring. Optional, defaults to 0.
- color: The shadow color. Use
rgba()for transparency. - inset: Makes the shadow render inside the element rather than outside.
Blur vs Spread
Blur and spread are often confused. Blur softens the edges — higher values create a more diffuse, atmospheric shadow. Spread changes the size of the shadow shape before blurring — a positive spread makes the shadow larger than the element; a negative spread makes it smaller.
/* Same offset and color, different blur and spread */
box-shadow: 0 4px 0px 0px #000; /* hard shadow, no blur */
box-shadow: 0 4px 8px 0px #000; /* soft shadow */
box-shadow: 0 4px 8px 4px #000; /* soft shadow, expanded */
box-shadow: 0 4px 8px -4px #000; /* soft shadow, contracted */Multiple Shadows
You can layer multiple shadows with a comma-separated list. They render front-to-back — the first shadow is on top.
Layered shadow for depth
box-shadow:
0 1px 2px rgba(0,0,0,0.04),
0 4px 8px rgba(0,0,0,0.08),
0 16px 32px rgba(0,0,0,0.06);This technique produces a more natural shadow that tapers off with distance, simulating real light behavior.
Inset Shadows
The inset keyword makes the shadow appear inside the element, creating a pressed or recessed effect.
/* Pressed button effect */
.button:active {
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.2);
}
/* Neumorphic well */
.input {
box-shadow:
inset 3px 3px 6px #d1d1d1,
inset -3px -3px 6px #ffffff;
}Elevation Systems (Material Design Pattern)
Material Design defines a set of elevation levels, each with a specific shadow. This creates a consistent visual hierarchy across components:
:root {
--shadow-1: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
--shadow-2: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
--shadow-3: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
--shadow-4: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}Performance Considerations
Box shadows trigger paint (not just composite) when they change during animation. To animate shadows smoothly:
- Animate opacity of a pseudo-element with a shadow instead of the shadow itself
- Or use
filter: drop-shadow()which is hardware-accelerated via composite - Avoid animating
spread-radius— it is especially expensive to repaint
/* Performant shadow animation using opacity */
.card::after {
content: '';
position: absolute; inset: 0;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
opacity: 0;
transition: opacity 0.2s ease;
}
.card:hover::after { opacity: 1; }Common Mistakes
- Using pure black:
rgba(0,0,0,0.15)looks more natural than#000000with opacity - Too many shadows: More than 3 layers rarely adds visible benefit and increases paint cost
- Ignoring dark mode: Dark-mode designs typically replace box shadows with subtle borders — shadows disappear on dark backgrounds
Try it yourself
Build CSS box shadows visually with live sliders and copy the generated code instantly.
Open Box Shadow Generator →