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
Glassmorphism, neumorphism, and where 2026 UI is actually heading
The design trends that shipped, the ones that flopped, and what makes modern UI actually feel modern - from frosted glass to soft shadows to conic gradients.
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.
The WCAG color contrast checklist every designer should bookmark
AA vs AAA, the math behind the ratios, why gray-on-gray fails on retina screens, and the two-line HTML that makes a color contrast test run in seconds.
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