box-shadow is one of CSS's most versatile properties. Its syntax has six parameters and supports multiple layered shadows — which makes it powerful and also easy to get wrong. Here's everything you need to know.
The syntax
box-shadow: offset-x offset-y blur-radius spread-radius color;
box-shadow: offset-x offset-y blur-radius spread-radius color inset;
- offset-x: Horizontal offset. Positive = right, negative = left.
- offset-y: Vertical offset. Positive = down, negative = up.
- blur-radius: How blurry the shadow is. 0 = sharp edge. Larger = softer. Default: 0.
- spread-radius: Expands or shrinks the shadow. Positive = larger, negative = smaller. Default: 0.
-
color: Shadow color. Default: the text color (
currentColor). - inset: Keyword. Converts the shadow from outside to inside (inner shadow).
Building intuition for the parameters
Just offset — a hard shadow:
box-shadow: 8px 8px 0 0 #2f855a;
Zero blur, zero spread. Creates a retro/flat design effect.
Soft drop shadow (most common):
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
Centered (no horizontal offset), some blur, slight transparency.
Spread positive vs negative:
/* Shadow bleeds out past element edges */
box-shadow: 0 4px 12px 4px rgba(0, 0, 0, 0.15);
/* Shadow is smaller than element (inward) */
box-shadow: 0 4px 12px -4px rgba(0, 0, 0, 0.15);
Negative spread is useful for creating a tight, contained shadow that doesn't bleed outward.
Inset shadow:
/* Inner shadow — top of element looks pressed in */
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
/* Inset works as a "pressed" button state */
button:active {
box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.3);
}
Multiple layered shadows
CSS allows comma-separated shadows. They stack, with the first listed shadow on top:
/* Two shadows for depth */
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.1), /* close shadow */
0 8px 20px rgba(0, 0, 0, 0.08); /* ambient shadow */
/* Three-layer depth (material design style) */
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
Design patterns
Flat / retro shadow:
.card {
box-shadow: 4px 4px 0 0 #1a202c;
}
/* Hover: shift to look "pressed" */
.card:hover {
transform: translate(2px, 2px);
box-shadow: 2px 2px 0 0 #1a202c;
}
Soft, elevated card (Material Design):
/* Resting elevation */
.card {
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.08);
}
/* Hover: increase elevation */
.card:hover {
box-shadow:
0 10px 20px rgba(0, 0, 0, 0.15),
0 3px 6px rgba(0, 0, 0, 0.10);
}
Neumorphic / soft UI:
.neu {
background: #e0e5ec;
box-shadow:
6px 6px 12px #b8bec7,
-6px -6px 12px #ffffff;
}
.neu:active {
box-shadow:
inset 6px 6px 12px #b8bec7,
inset -6px -6px 12px #ffffff;
}
Neumorphism requires: a neutral mid-tone background, a dark shadow in one direction and a light/white shadow in the opposite direction. The background color must match the page background.
Glow effect:
/* Colored glow */
.glow-green {
box-shadow:
0 0 10px rgba(47, 133, 90, 0.5),
0 0 20px rgba(47, 133, 90, 0.3),
0 0 40px rgba(47, 133, 90, 0.1);
}
Layering the same color at different spread/blur values creates a gradual glow.
Inner ring (focus ring):
/* Visible focus ring using inset */
button:focus-visible {
outline: none;
box-shadow:
inset 0 0 0 2px white,
inset 0 0 0 4px #2f855a;
}
This creates a two-color focus ring without using outline.
Border substitute (for rounded corners):
/* box-shadow instead of border (doesn't affect layout) */
.card {
border-radius: 12px;
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.12),
0 0 0 1px rgba(0, 0, 0, 0.08); /* subtle border */
}
Using box-shadow for borders means no layout shift (shadow doesn't occupy space in the box model, unlike border).
Using CSS variables for consistent shadows
:root {
--shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.08);
--shadow-lg: 0 10px 30px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.08);
--shadow-xl: 0 20px 40px rgba(0,0,0,0.15), 0 8px 16px rgba(0,0,0,0.10);
}
.card { box-shadow: var(--shadow-md); }
.modal { box-shadow: var(--shadow-xl); }
This is how Tailwind CSS handles shadows — predefined variable-based levels.
Generating box shadows
Composing multi-layered shadows by hand is tedious — especially for neumorphic effects or precise glow layering. An online CSS box shadow generator lets you adjust all parameters with sliders and see the result live, then copy the CSS. Look for one that supports multiple shadow layers and has an inset toggle.
Performance
box-shadow is a paint property — changes to it trigger repaint but not layout. It's one of the safer properties to animate (compared to width/height which trigger layout).
For smooth animations, use transform combined with box-shadow changes, and add will-change: box-shadow if the element animates frequently:
.card {
transition: box-shadow 0.2s ease, transform 0.2s ease;
will-change: transform;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
Avoid: Animating filter: drop-shadow() — it's significantly more expensive than box-shadow for most use cases.
box-shadow rewards experimentation. Start with a single soft shadow, then layer in complexity once you have the basic effect working. The real power is in multi-layer composition — that's where most of the interesting design patterns live.
Top comments (0)