For years my shadows looked fake and I could not work out why. I would write box-shadow: 0 5px 10px black, get a muddy grey halo, tone it down, and move on feeling vaguely dissatisfied. The card looked like it had a bruise, not a shadow. It was only when I started pulling apart the design systems I admired - the ones where cards seem to genuinely float - that I realised the trick. They never use one shadow. They stack two or three, each doing a different job, and the result reads as light behaving the way light actually behaves.
I build interfaces in React and Tailwind for a living, I maintain a WordPress admin theme in WP Adminify where shadows carry a lot of the visual hierarchy, and I built the CSS Box Shadow Generator on toolz.dev because I got tired of hand-tuning multi-layer shadows in a text editor and reloading to see the result. This guide is everything I have learned about the box-shadow property: what each value does, why layering works, how to avoid the common traps, and how to use the generator to get from a blank element to production CSS in a couple of minutes.
TL;DR: A CSS
box-shadowtakes an X offset, a Y offset, a blur radius, a spread radius, and a color, with an optionalinsetkeyword. Blur softens the edge; spread grows or shrinks the shadow before blurring. Real elevation comes from stacking multiple comma-separated shadows, not from cranking one shadow's opacity. Use low-opacity black, a small Y offset, generous blur, and a slightly negative spread - then copy the CSS straight from the generator.
What does the box-shadow property actually do?
box-shadow paints one or more shadows around an element's border box, and the property is specified in CSS Backgrounds and Borders Level 3. The syntax for a single shadow looks like this:
box-shadow: <inset?> <offset-x> <offset-y> <blur-radius> <spread-radius> <color>;
The offsets are required; blur, spread, and color are optional but you will almost always set them. Here is what each one controls, in the way I think about them when tuning:
Offset X moves the shadow horizontally. Positive values push it right, negative left. For most interface shadows you want this at or near zero, because light in the real world usually comes from above, casting shadows straight down rather than off to the side.
Offset Y moves the shadow vertically. Positive values push it down, which is what you want for an element that appears to sit above the page. This is the single value that most controls how "high" the element feels.
Blur radius softens the shadow's edge. A value of zero gives a hard-edged shadow like a solid shape; larger values spread the softness out. This cannot be negative. Almost every natural-looking shadow uses a blur several times larger than its Y offset.
Spread radius changes the size of the shadow before the blur is applied. Positive spread makes the shadow larger in every direction; negative spread shrinks it. Negative spread is the secret weapon for tight, believable card shadows, because it pulls the shadow in so it does not bleed out beyond the element.
Color is any valid CSS color. The best interface shadows use black or near-black at low opacity - something like rgba(0, 0, 0, 0.1) - rather than a fully opaque grey, because a translucent black tints whatever is behind it the way a real shadow does.
Why do good shadows use more than one layer?
This is the idea that changed my shadows completely, so it gets its own section. A single shadow is a single, uniform blob. Real shadows are not uniform. Close to the object the shadow is darker and sharper; further away it is lighter and more diffuse. You approximate that falloff by stacking shadows.
A classic recipe is two layers: a tight, slightly darker shadow that sits close to the element and gives it a crisp base, plus a softer, wider, lighter shadow beneath it that provides the ambient diffusion. Something like:
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -2px rgba(0, 0, 0, 0.1);
The browser paints comma-separated shadows in order, with the first one on top. Each layer is cheap, and two or three together read as genuine depth in a way that no single shadow can, no matter how you tune it. This is exactly why the generator is built around layers rather than a single set of sliders - the CSS Box Shadow Generator ships with presets like Card, Elevated, and Floating that are all multi-layer, so you start from a realistic base instead of a lonely blob.
The mental model I use: think in elevation levels. A resting card sits low with a small, tight shadow. A dropdown or popover sits higher with a larger, softer one. A modal sits highest of all. Increasing elevation means increasing the Y offset and blur across all layers together, not just darkening one shadow.
How do I create an inner shadow?
The inset keyword flips a shadow inside the element's padding box instead of casting it outward. Add inset to the front of a shadow declaration and it paints within the element:
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
Inner shadows are how you make a control look pressed, recessed, or carved in. A text input often reads better with a subtle inset shadow that suggests a well the text sits inside. A pressed button state can swap its outer shadow for an inner one to feel physically depressed. And the whole neumorphism trend is built on pairing a light inset from one direction with a dark inset from the other. In the generator, inset is a single toggle per layer, so you can mix inset and outset shadows in the same element - which is exactly what neumorphic surfaces need.
How do I use the Box Shadow Generator?
The flow is designed to match how I actually work. Open the CSS Box Shadow Generator and you land on a preview element with a realistic Card preset already applied, so you are never staring at a blank canvas.
Pick a preset to get close to what you want - Subtle, Card, Elevated, Floating, Inner, Glow, Neumorphic, or Sharp. Each one loads its full layer stack. Then select a layer and tune it with the sliders: offset X, offset Y, blur, and spread each have their own control, plus a color picker, an opacity slider, and the inset toggle. The preview updates live as you drag, so you are shaping the shadow directly rather than editing numbers and imagining the result.
If you need more depth, add a layer. If a layer is doing nothing useful, remove it. There is a background toggle that switches the preview between light and dark, which matters because a shadow that looks perfect on a light card can vanish or turn into an ugly grey ring on a dark one. Checking both before you ship saves a round trip.
When you are happy, copy the CSS. You can grab the plain box-shadow declaration, or a block that also includes the -webkit-box-shadow prefix if you have to support very old WebKit builds. Paste it into your stylesheet, your styled component, or your Tailwind arbitrary value and you are done.
Like every tool on the site, it runs entirely in your browser. Nothing is uploaded and it keeps working offline once loaded, which is the same client-side approach I describe in the data privacy guide.
Real-world use cases
Building an elevation scale for a design system
When I set up a new project, I define a fixed set of elevation levels rather than letting every component invent its own shadow. Usually that is four or five steps from "resting card" up to "modal." I build each one in the generator, copy the CSS, and drop them into my design tokens as named values. Every card, menu, and dialog then references a level, so the whole product shares a consistent lighting model. Doing this by hand in a text editor is miserable; doing it with a live preview takes a few minutes.
Making a card actually float
The default instinct is a single dark shadow, which looks pasted-on. The fix is the two-layer recipe: a tight layer with a small Y offset and negative spread to anchor the card, and a wider, softer layer beneath for ambient light. Start from the Card or Elevated preset, nudge the values to taste, and you get a card that reads as lifted rather than outlined.
Designing pressed and focused states
Interactive feedback often lives in the shadow. A button can lose most of its outer shadow and gain a small inset on press to feel physically pushed. An input can carry a permanent subtle inset to look like a well. These are quick to prototype by toggling inset and watching the preview, and they add a lot of perceived polish for very little code.
Neumorphic surfaces
If you are going for the soft-UI look, you need two shadows on the same element pointing opposite directions - a dark one down-right and a light one up-left - so the surface appears extruded from the background. The Neumorphic preset sets this up, and you tune the color, opacity, and blur to match your specific background. It only works on backgrounds that are neither pure white nor pure black, which the dark-background toggle helps you check.
box-shadow versus filter: drop-shadow
A question I get often: when should I use filter: drop-shadow() instead of box-shadow? They look similar but behave differently, and choosing wrong causes real bugs.
| Feature | box-shadow | filter: drop-shadow() |
|---|---|---|
| Follows | The rectangular border box | The actual visible (non-transparent) pixels |
| Best for | Cards, buttons, panels, inputs | Logos, icons, PNGs with transparency, irregular shapes |
| Supports inset | Yes | No |
| Supports spread | Yes | No |
| Multiple shadows | Yes, comma-separated | Yes, by chaining filters |
| Affects layout | No | No |
The short version: for a normal rectangular element, use box-shadow - it is more capable, it supports spread and inset, and it is what this generator produces. Reach for drop-shadow() only when you need the shadow to trace a transparent or irregular shape, like a logo or a cutout icon, where a rectangular shadow would look wrong.
Advanced tips and traps
A few things I have been burned by. First, shadows get clipped by overflow: hidden on an ancestor. If your beautiful shadow is mysteriously cut off, an ancestor is clipping it - either give the shadow room or move the overflow rule elsewhere. Because box-shadow never affects layout, it does not push neighbouring elements, which is a feature, but it also means a shadow can silently extend under a sibling.
Second, huge blurs on many elements can cost paint performance, especially when animated. If you animate a shadow on scroll or hover across a long list, watch your frame rate; sometimes it is cheaper to animate opacity on a pseudo-element that holds a static shadow. Once your CSS is finalised, running it through the CSS Minifier shaves the extra bytes that verbose multi-layer shadows add.
Third, do not forget that shadow color can be more than black. A very subtly tinted shadow - a dark version of your surface's hue rather than pure black - often looks richer than neutral grey, because real shadows pick up ambient color. It is a small move with a disproportionate payoff.
If you are assembling a wider CSS toolkit, the web developer toolkit guide covers where shadow work fits among the other browser-based utilities, and once your shadows are done, the Gradient Generator and Color Picker round out the surface styling. If accessibility is on your mind, pair this with the Color Contrast Checker so the content sitting on your shadowed cards stays readable.
FAQ
What is the CSS box-shadow property?
box-shadow adds one or more shadows around an element's frame. Each shadow takes a horizontal offset, a vertical offset, an optional blur radius, an optional spread radius, and a color, with an optional inset keyword to draw the shadow inside the box. Multiple shadows are separated by commas and painted with the first on top.
What is the difference between blur and spread?
Blur radius softens the shadow edge, so a larger value makes it more diffuse. Spread radius changes the shadow's size before blurring: a positive spread grows it in every direction, while a negative spread shrinks it, which is how you keep a shadow tucked tightly under a card instead of bleeding past its edges.
How do I create an inner shadow in CSS?
Add the inset keyword to the shadow. inset draws the shadow inside the element's padding box instead of outside, which is useful for pressed buttons, input wells, and neumorphic surfaces. Toggling the inset switch in the generator adds it to the selected layer automatically.
Can I use multiple box-shadows on one element?
Yes. You can list several shadows in a single box-shadow declaration separated by commas, and the browser paints them in order with the first on top. Layering a soft, wide shadow beneath a smaller, darker one is the standard way to make elevation look realistic rather than pasted on.
Why does my box-shadow get clipped?
A shadow is cut off when an ancestor has overflow: hidden or when the element sits at the edge of a clipping container. Give the shadow room, or move the overflow: hidden rule to a different element. Because box-shadow does not affect layout, it never pushes neighbouring elements, so it can also extend silently under a sibling.
What is the difference between box-shadow and filter: drop-shadow?
box-shadow follows the element's rectangular border box, so it ignores transparent areas inside a PNG or an irregular shape. filter: drop-shadow() traces the actual visible pixels, which suits logos and icons with transparency. For standard rectangular cards and buttons, box-shadow is the right choice and supports spread and inset that drop-shadow does not.
Do I still need the -webkit- prefix for box-shadow?
For modern browsers, no. box-shadow has been unprefixed and widely supported for many years. The prefixed block the generator can output is only useful if you must support very old WebKit builds, and for most projects the standard box-shadow property alone is enough.
Does this box-shadow generator send my data anywhere?
No. The preview and CSS output are generated entirely in your browser with JavaScript. No values are uploaded or stored, and the tool continues to work with no network connection once the page has loaded.



