CSS Box Shadows Getting Realistic Results Without Trial and Error
Box shadow is one of those CSS properties that looks simple until you’re staring at a result that looks like clip art from 2008. The syntax is short, but what each value actually does — and how they interact — isn’t obvious. Here’s how to write shadows that look considered, not pasted in.
The Syntax, Demystified
box-shadow: offset-x offset-y blur-radius spread-radius color;
Five values. Let’s be precise about each:
- offset-x: How far the shadow is pushed horizontally. Positive moves it right; negative moves it left.
- offset-y: How far the shadow is pushed vertically. Positive moves it down.
- blur-radius: Softens the shadow. At
0, it’s a hard edge. Higher values blur it outward using Gaussian blur applied to the shadow shape. - spread-radius: Expands or contracts the shadow before blur is applied. Positive spread makes it bigger than the element; negative makes it smaller.
- color: The shadow color. A common mistake is using pure black.
- inset: An optional keyword that flips the shadow to the inside of the element.
Blur vs Spread: The Confusion That Ruins Shadows
These two values are the source of most “why does this look weird” moments.
Blur makes the shadow soft. It simulates light diffusion — the further from the source, the more the shadow bleeds. A blur-radius of 8px on a small element looks very different than on a large one.
Spread grows or shrinks the base shadow shape. If you’re trying to create the impression of a shadow that’s “far away” and soft, increasing blur without negative spread means the shadow bleeds outside the element boundary, which can look ungrounded. A slight negative spread tightens it back in, so the blur starts from a more natural footprint.
The right combo is usually moderate blur with zero or slightly negative spread. That’s how real-world shadows work — they’re softer at distance, but they don’t expand beyond the object casting them.
Making Shadows Look Natural
Three rules for shadows that look intentional:
1. Don’t use pure black. Real shadows pick up ambient color. A dark navy or dark warm gray at 0.10–0.15 opacity reads as a shadow without looking like a stamped shape. Try rgba(0, 0, 0, 0.12) as a starting point, then shift the hue slightly warm or cool to match your palette.
2. Match a single light source. If shadows go in different directions on different elements, the page reads as inconsistent. Pick a direction — typically positive offset-y (light from above) — and stick to it system-wide.
3. Keep opacity low. A shadow at full opacity reads as a border. If you can clearly see a hard edge, you’ve either gone too opaque or zeroed out the blur. Shadows that look real are subtle — they give volume, not outline.
Layering Multiple Shadows for Depth
CSS allows comma-separated shadow layers on a single element. This is where you get real dimension without visual artifacting.
Instead of one large shadow, try two: a tight close shadow for contact, and a diffuse far shadow for elevation.
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.08),
0 8px 24px rgba(0, 0, 0, 0.06);
The first layer grounds the element at the surface. The second adds the sense of it floating above. Neither is “the shadow” in isolation — together they create the illusion of depth. Physically, this mimics how a nearby strong light source creates a tight shadow underneath, while ambient light fills in a softer halo further out.
Inset Shadows: When the Light Flips
The inset keyword draws the shadow inside the element’s border instead of outside it. The main use cases are:
- Pressed button state: A shallow inset shadow on
:activecreates the physical feeling of depression, like a real button click. - Input field depth: Form inputs and text areas feel inset from the surface with a top-edge inset shadow — a pattern that goes back to skeuomorphic design but still reads clearly.
/* Pressed button */
button:active {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
}
/* Input field depth */
input {
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.10);
}
Performance: When to Use filter: drop-shadow Instead
box-shadow triggers a repaint on every change — not just on the element, but on the painted layer beneath it. For animations or scroll-heavy UIs, this adds measurable cost.
filter: drop-shadow() runs on the GPU compositor and is significantly cheaper for animated shadows. It also follows the element’s actual rendered shape (including transparent areas), while box-shadow always follows the box model rectangle.
The practical rule: use box-shadow for static shadows on layout elements. Reach for filter: drop-shadow on animated elements or SVGs where the shadow needs to conform to the visual shape rather than the bounding box.
Shadow Tokens for a Design System
Hardcoding shadow values per component leads to inconsistency. Define a small set of elevation tokens once, then reference them everywhere.
:root {
--shadow-sm:
0 1px 2px rgba(0, 0, 0, 0.05),
0 1px 4px rgba(0, 0, 0, 0.04);
--shadow-md:
0 2px 4px rgba(0, 0, 0, 0.07),
0 4px 12px rgba(0, 0, 0, 0.06);
--shadow-lg:
0 4px 8px rgba(0, 0, 0, 0.08),
0 8px 24px rgba(0, 0, 0, 0.06);
--shadow-xl:
0 8px 16px rgba(0, 0, 0, 0.08),
0 16px 48px rgba(0, 0, 0, 0.06);
--shadow-2xl:
0 12px 24px rgba(0, 0, 0, 0.10),
0 32px 80px rgba(0, 0, 0, 0.08);
}
Each token uses the two-layer approach: a tight shadow for contact grounding, a diffuse shadow for elevation. Blur and opacity both increase as elevation rises — the farther from the surface, the softer and more spread-out the shadow becomes.
These values work well on white and light gray backgrounds. On dark backgrounds, invert the logic — use lighter, semi-transparent colors for inset shadows instead of dark ones for cast shadows.
Skip the Guessing
If you’re iterating on shadow values — especially while layering multiple shadows or testing different opacities — a live preview saves significant time. The CSS Box Shadow Generator at IO Tools lets you adjust all six parameters in real time, stack multiple shadow layers, and copy the generated CSS directly. Useful for dialing in new design tokens or validating that a shadow reads correctly before it lands in production.
The difference between a shadow that looks designed and one that looks pasted in usually comes down to three things: low opacity, a non-pure-black color, and letting blur do the work that spread was never meant to do.
Install Our Extensions
Add IO tools to your favorite browser for instant access and faster searching
恵 Scoreboard Has Arrived!
Scoreboard is a fun way to keep track of your games, all data is stored in your browser. More features are coming soon!
Must-Try Tools
View All New Arrivals
View AllUpdate: Our latest tool was added on May 1, 2026
