CSS box-shadow generator
Adjust offset, blur, spread, colour and alpha per layer, stack as many shadow layers as you need, and toggle inset for a pressed effect. The preview updates live and the CSS is ready to copy underneath it.
How box-shadow works
A single box-shadow entry is up to six pieces of information, in a fixed order:
| Value | Required | Negative allowed | Default | What it controls |
|---|---|---|---|---|
| offset-x | Yes | Yes | — | Horizontal shift; negative moves the shadow left. |
| offset-y | Yes | Yes | — | Vertical shift; negative moves the shadow up. |
| blur-radius | No | No | 0 | Softens the edge; larger values spread the blur further and fade it lighter. |
| spread-radius | No | Yes | 0 | Grows (or, negative, shrinks) the shadow's shape before blurring. |
| color | No | n/a | currentColor | Any valid CSS colour, including an alpha channel. |
| inset | No | n/a | outer | Moves the shadow inside the box instead of outside it. |
Only offset-x and offset-y are required — box-shadow: 4px 4px red; is a completely valid, sharp-edged shadow. Everything else has a default that this tool always writes out explicitly, so the CSS you copy is unambiguous.
Spread versus blur
These two get confused constantly. Blur radius softens the shadow's edge — it turns a hard rectangle into a fading gradient, and the shadow's visible footprint grows roughly by the blur amount on every side. Spread radius changes the size of the shape being blurred, before any blurring happens — a positive spread grows the shadow's silhouette outward on every side, a negative spread shrinks it inward. Elevation-style shadows lean on negative spread constantly: a tight, slightly-negative-spread shadow close to the edge reads as contact, paired with a larger, more positive shadow further out for ambient light.
Layering shadows for realism
A single soft shadow reads as flat and slightly synthetic, because real light rarely produces just one shadow of one softness. Stacking two or three shadows — different offsets, different blur, different alpha, all comma-separated in one declaration — gets close to how light actually behaves: a crisp near shadow plus a diffuse far one. This is exactly how Material-style elevation shadows and neumorphism are built; the examples page has the layer values for both.
Performance note
Blurring is more expensive to paint than a flat fill, and the cost scales with both the blur radius and how many elements carry a shadow. A large-blur shadow on every card in a big grid, especially one that repaints during scroll, hover or resize, is a heavier paint workload than the same layout with no shadow at all. If a shadow needs to move or pulse, animating a separate element's opacity or transform is usually cheaper than animating box-shadow's own blur or spread values directly, since a changing blur has to be recomputed rather than just recomposited.
box-shadow versus the drop-shadow() filter
box-shadow always shadows the element's box — its rectangle, or its rounded rectangle where border-radius is set — no matter what is actually rendered inside it. filter: drop-shadow() instead follows the alpha channel of the rendered content itself, so a transparent PNG of an icon casts an icon-shaped shadow rather than a rectangular one, and it treats an element and all of its children as one shape. drop-shadow() also takes only offset-x, offset-y, blur-radius and colour — there is no spread-radius and no inset — but it composes with other filters such as blur() in the same filter pipeline. For a plain card or button, box-shadow is the cheaper, simpler tool; for shadowing an irregular silhouette, drop-shadow() is the one that actually follows the shape.
Frequently asked questions
What are the values in box-shadow, in order?
box-shadow: [inset] offset-x offset-y [blur-radius] [spread-radius] color; — offset-x and offset-y are required; blur-radius and spread-radius are optional and default to 0 if omitted; color is optional and defaults to currentColor; inset is an optional keyword that can appear first or last. Multiple shadows are separated by commas.
Can offset-x, offset-y and spread be negative?
Yes, all three accept negative values. A negative offset-x moves the shadow left, a negative offset-y moves it up, and a negative spread-radius shrinks the shadow's box smaller than the element before the blur is applied — the technique elevation shadows use to keep their edges from looking like a hard-edged rectangle.
Can blur radius be negative?
No. Negative blur-radius values are invalid and browsers clamp or ignore them; the smallest usable value is 0, which produces a hard-edged shadow with no softening at all. This generator floors blur at 0 for the same reason.
What does the inset keyword actually do?
Without inset, the shadow is drawn outside the element's border box — a drop shadow. With inset, it is drawn inside the padding box instead, as if the element were recessed into the page — the effect used for pressed buttons, neumorphic UI and sunken input fields. Everything else about the six values works the same way; only where the shadow is painted changes.
How is box-shadow different from the drop-shadow() filter?
box-shadow always shadows the element's own box — its rectangle, or its rounded rectangle if border-radius is set — regardless of what is actually drawn inside it. filter: drop-shadow() instead follows the alpha channel of the rendered content, so a transparent PNG of a star casts a star-shaped shadow rather than a rectangular one, and it shadows all of an element's children as a single unit. drop-shadow() also only accepts offset-x, offset-y, blur-radius and colour — there is no spread-radius and no inset — and it participates in the same filter pipeline as blur() or grayscale(), so it can be chained with them. For a plain rectangular or rounded card, box-shadow is the simpler and cheaper choice.
Why stack multiple shadows instead of using one?
A single shadow with a big blur radius looks flat and slightly artificial because real ambient light produces several overlapping shadows of different softness and reach. Material-style elevation shadows layer a tight, darker shadow close to the edge with a soft, paler one further out; neumorphism layers a dark shadow on one corner with a light highlight on the opposite corner to fake a raised or pressed surface. Multiple box-shadow entries, comma-separated, are painted front to back — the first one listed sits closest to the viewer, later ones sit behind it.
Is a big blur radius bad for performance?
It can be. Rendering a shadow's blur is more expensive than painting a flat colour, and the cost scales with both the blur radius and the number of elements carrying a shadow — a card grid where every card has a large-blur shadow, especially one that repaints on scroll, hover or resize, is a much heavier paint workload than the same layout with no shadows. If a shadow needs to animate, animating a separate pseudo-element's opacity or transform is typically cheaper than animating the blur or spread values directly, because those force the shadow to be recomputed on every frame rather than just recomposited.
Does box-shadow follow border-radius?
Yes. An outer box-shadow traces the same rounded corners as border-radius, and an inset shadow is clipped to the same rounded padding box, so a shadow on a rounded card comes out rounded too without any extra property.
Do you store the shadows I build?
No. The layer values you set exist only in this browser tab's memory while the page is open — there is no server call involved in building or copying a shadow, and nothing you configure here is uploaded, logged or recorded anywhere.
Use cases & limitations
Where this is genuinely useful
- Design-system elevation tokens. Work out two or three shadow "levels" for cards, modals and menus, then keep the CSS in your token file instead of re-guessing it per component.
- Neumorphic or pressed-state UI. A dark shadow on one corner and a light highlight on the opposite corner, both inset, is the whole trick — the presets below have it built.
- Quick iteration on a button or card's depth without editing CSS and reloading a browser tab for every tweak.
- Learning what each value actually does by dragging one slider at a time and watching the preview and the generated line change together.
What this tool cannot tell you
- How the shadow will look against your real background. The preview stage is a neutral checkerboard specifically so a transparent colour is visible; test the final CSS against your actual page background too.
- Whether a shadow is a good idea for accessibility. A shadow is decorative — it should never be the only way a control communicates state (focus, hover, disabled). Pair it with a real contrast change or a border, and check that pairing with a contrast checker.
- Runtime paint cost on your actual page. This tool shows you correct CSS; how expensive it is to render depends on your element count, animation and the visitor's device, none of which this page can see.
- Anything about filter: drop-shadow(). That is a different property with different syntax; this generator only produces
box-shadowvalues.