CSS box-shadow patterns that look professional (not cheap)
A single 5px shadow makes any UI look like a bootstrapped 2015 landing page. Here are the layered-shadow patterns that modern design systems use โ Material, Tailwind, Apple.
Why single-shadow feels cheap
The default anyone writes:
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
That's a single blur, single color, single position. In real light, objects cast shadows with multiple falloffs at once โ a sharp near shadow and a soft ambient shadow. Simulating that means stacking two or three shadows.
Compare:
/* cheap */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* professional */
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.06),
0 4px 6px rgba(0, 0, 0, 0.1),
0 10px 15px rgba(0, 0, 0, 0.08);
Same element, dramatically different perceived polish.
The Material-inspired elevation scale
Google's Material Design (and every design system since) uses an elevation scale โ cards, dialogs, and buttons sit at different "heights" and cast different shadows.
Tailwind's default scale is a good starting point:
/* sm โ hairline shadow, subtle hover states */
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
/* base โ the everywhere shadow, resting state */
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
/* md โ cards, dropdown menus */
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
/* lg โ popovers, tooltips */
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
/* xl โ floating panels */
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
/* 2xl โ dialogs, high-elevation cards */
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
Use sm for hover states, md for resting cards, lg for floating UI. Ship all six as design tokens.
The five knobs
Every shadow has five parameters:
- X offset โ horizontal displacement. Positive = right.
- Y offset โ vertical. Positive = down. This is usually the only non-zero offset โ light comes from above.
- Blur radius โ how soft the edge is. Larger = softer.
- Spread โ expand or contract before blurring. Negative spread makes a tight, hard shadow.
- Color โ including alpha. RGBA at low opacity (0.05-0.15) is the default; solid black looks harsh.
The single most-underused knob is negative spread on the outer layer โ it tightens the shadow directly under the element, preventing the "floating in empty space" look.
Inset shadows for depth
The inset keyword paints the shadow inside the box:
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
Uses:
- Form inputs โ inset shadows read as recessed, matching the "you can type here" affordance.
- Pressed buttons โ combine
:active { box-shadow: inset ... }with a slight downward transform. - Inner borders โ a 1px inset at low opacity is a nicer border than
border: 1px solid.
Colored shadows
Recent design trend: shadows in the accent color, not black. A blue button casts a blue shadow, matching the object.
.btn-primary {
background: #3b82f6;
box-shadow: 0 8px 16px -4px rgba(59, 130, 246, 0.4);
}
Reads more expensive, more modern. Overdo it and the UI looks like a slot machine. Use sparingly โ big CTAs, hero cards.
Dark mode: shadows disappear
The single biggest shadow mistake: shipping the same shadow scale in dark mode. On a black background, a black shadow is invisible. On a dark-gray background, a black shadow is worse than invisible โ it flattens contrast.
Two approaches:
1. Skip shadows in dark mode. Use borders (border: 1px solid rgba(255,255,255,0.1)) instead. Cleaner, matches how macOS and iOS handle dark surfaces.
2. Use inset white glow. A subtle inset light-colored highlight on the top edge simulates ambient light.
@media (prefers-color-scheme: dark) {
.card {
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.05),
0 8px 16px rgba(0, 0, 0, 0.4);
}
}
Design systems that skip this get "invisible cards" complaints. Notion, Linear, and Figma all handle dark-mode shadows explicitly.
Performance: box-shadow is not free
Large blur radii are expensive to paint, and box-shadow triggers a repaint on every animation frame. Three optimizations:
- Cap the blur radius. Under 30px is fine; over 50px hurts on mid-range devices.
- Animate
transform, not shadow. Transitioningbox-shadowon:hoveris choppy on 60fps displays. Transitiontransform: translateY(-2px)and keep the shadow static; the eye reads it as motion. - Use
filter: drop-shadow()for non-rectangular content. Icons, SVGs, images with transparency.box-shadowdraws the shadow of the box;drop-shadowfollows the alpha channel. Slower than box-shadow but the only option for irregular shapes.
Building shadows without eyeballing
Building shadows by tweaking numbers in DevTools is slow. Box Shadow Generator has sliders for every parameter, multi-layer stacking, and a live preview โ you build a professional shadow in seconds, not minutes.
The design-system checklist
For your own component library:
- Ship an elevation scale โ at least 4 levels (subtle, resting, floating, high).
- All shadows use CSS custom properties โ swap for dark mode centrally.
- Hover shadows are one step up the scale, not a random custom value.
- Pressed states use
insetto communicate depth reversal. - Test on a light and a dark background before shipping โ every scale value.
Related workflows
- CSS Gradient Generator โ for hero backgrounds behind cards with shadows.
- Color Palette Generator โ for colored-shadow accent colors.
- Color Converter โ for tuning RGBA alpha values quickly.
Tools mentioned in this post
Related reading
CSS gradient patterns that actually look good (and the two that don't)
Two colors and a 45ยฐ angle is not a gradient โ it's a shortcut to a design that looks like every SaaS site from 2018. Here's how to build ones that don't.
Tailwind to plain CSS: migration, extraction, and component patterns
You inherited a Tailwind codebase and want to extract a component library. Or you're moving away from Tailwind entirely. Here's how the utility classes map to real CSS.
Shan builds 712 Tools. He holds a Master's degree in Mechanical Engineering and now works as a Software Engineer, shipping browser-based developer utilities out of Ontario, Canada. Learn more ยท 712studiogames@gmail.com