Shadows are one of the most powerful visual tools in a front-end developer's toolkit. A well-placed box-shadow can add depth to flat layouts, guide the user's attention, and make your interface feel polished and professional. This guide covers everything from the basic syntax to advanced layering techniques you can use in production today.
Understanding the box-shadow Syntax
The box-shadow property accepts the following values in order:
box-shadow: [inset] offset-x offset-y blur-radius spread-radius color;
Each value plays a specific role in how the shadow renders:
- offset-x — Horizontal distance from the element. Positive values move the shadow right; negative values move it left.
- offset-y — Vertical distance from the element. Positive values move the shadow down; negative values move it up.
- blur-radius — Controls how soft or sharp the shadow edge appears. A value of
0produces a hard-edged shadow. Higher values create a softer, more diffused look. - spread-radius — Expands or contracts the shadow size. Positive values make the shadow larger than the element; negative values shrink it.
- color — The shadow color. Using
rgba()orhsla()lets you control transparency for subtle effects.
The inset Keyword
Adding the inset keyword flips the shadow inward, making it appear inside the element's border. This is perfect for creating recessed input fields, pressed buttons, or inner glow effects.
/* Inset shadow for a pressed button */
.button:active {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}
Practical Shadow Patterns
Subtle Elevation
Material Design popularized the concept of using shadows to convey elevation. A common approach is a small offset with a moderate blur:
/* Card elevation */
.card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.08);
}
/* Higher elevation on hover */
.card:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15),
0 4px 10px rgba(0, 0, 0, 0.08);
}
Using two stacked shadows — one with a small blur for a tight edge and one with a larger blur for ambient diffusion — creates a natural look that single-shadow approaches struggle to match.
Neon Glow Effect
Neon and glassmorphism designs rely heavily on colored shadows with large blur radii:
/* Neon glow */
.neon {
box-shadow: 0 0 20px rgba(139, 92, 246, 0.5),
0 0 60px rgba(139, 92, 246, 0.2);
}
The key is using zero offsets so the glow radiates evenly in all directions. Layering a tighter inner glow with a wider outer glow creates depth.
Floating Cards
A popular pattern for dark-themed dashboards is a low-contrast shadow that lifts cards slightly off the background:
/* Dark theme floating card */
.dark-card {
background: #1a1d2e;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
Stacking Multiple Shadows
You can combine as many shadows as needed by separating them with commas. The rendering order is important: the first shadow in the list appears on top. This means you should list your most prominent shadow first.
/* Layered shadow composition */
.sophisticated {
box-shadow:
0 1px 1px rgba(0, 0, 0, 0.08), /* tight edge */
0 2px 4px rgba(0, 0, 0, 0.06), /* near shadow */
0 8px 16px rgba(0, 0, 0, 0.1), /* ambient shadow */
0 16px 32px rgba(0, 0, 0, 0.05); /* far ambient */
}
This four-layer approach mimics how real-world shadows behave: a sharp contact shadow, a nearby penumbra, and two levels of ambient light diffusion. The result looks significantly more natural than a single shadow.
Performance Considerations
Box shadows are rendered by the compositor in modern browsers, which means they generally perform well. However, there are a few things to keep in mind:
- Avoid animating blur-radius — Changes to blur require repainting, which is expensive. Stick to animating opacity or transform for smooth transitions.
- Large blur values are costly — Shadows with blur radii above 100px can cause performance issues on lower-end devices, especially when applied to many elements.
- Prefer
will-changesparingly — If you must animate shadows, addingwill-change: box-shadowcan help, but don't apply it to more than a few elements at a time.
Animating Shadows Smoothly
Instead of animating the shadow directly, consider using a pseudo-element with the shadow and animating its opacity:
/* Performant shadow animation */
.card {
position: relative;
transition: transform 0.2s ease;
}
.card::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
opacity: 0;
transition: opacity 0.2s ease;
}
.card:hover::after {
opacity: 1;
}
.card:hover {
transform: translateY(-2px);
}
This approach keeps the animation on the GPU compositor, resulting in smooth 60fps transitions even on mobile devices.
Common Mistakes
- Using pure black — Shadows in the real world are never pure black. Always tint your shadows with the background color or use dark gray with transparency.
- Overdoing the spread — Large spread values make shadows look unnatural. Keep spread small (0-4px) for realistic results.
- Forgetting border-radius — Shadows follow the element's border-radius, but make sure both the element and shadow use the same radius for consistency.
- Ignoring contrast — On dark backgrounds, use higher opacity shadows. On light backgrounds, lower opacity works better.
Try It Yourself
Understanding box-shadow syntax is one thing — experimenting with it is how you develop an eye for what looks right. Use our free CSS Box Shadow Generator to visually adjust offset, blur, spread, and color in real time. No signup required, and everything runs in your browser.