Build beautiful layered shadows visually. Drag, tune, and copy your CSS instantly.
box-shadow
accepts up to 6 values: optional
inset
, horizontal offset, vertical offset, blur radius, spread radius, and color. Multiple shadows are comma-separated, letting you stack unlimited layers for complex effects like neumorphism or glassmorphism.
The first shadow in the list is rendered on top. This matters most with inset shadows and when blending multiple colored layers. Drag layers in the list to reorder them — the live preview and CSS update instantly.
Large blur values are GPU-expensive. For animated shadows, animate
opacity
of a pseudo-element instead of animating
box-shadow
directly — this stays entirely on the compositor thread and avoids layout recalculations.
Changing box-shadow during an animation or transition triggers a paint operation on every frame. Paint runs on the CPU, not the GPU, which means the browser has to interrupt compositing to redraw the element. On a fast desktop this produces occasional jank. On a mid-range mobile device or a page with many animated shadows, you get consistent dropped frames.
The standard workaround is to put the shadow on a ::after pseudo-element and transition its opacity instead. Opacity changes are composited on the GPU without triggering paint. Here’s the pattern:
Two requirements: the element needs position: relative (so inset: 0 on the pseudo-element works), and border-radius: inherit on the pseudo-element so the shadow matches the card shape. This technique is especially worth using on card grids, lists of interactive items, or anywhere box-shadow changes on hover at scale.
| Use case | Value | Notes |
|---|---|---|
| Resting card | 0 1px 3px rgba(0,0,0,.06), 0 4px 16px rgba(0,0,0,.06) | Two layers: a tight shadow for definition, a soft one for depth. Looks good on white or very light backgrounds. Single-layer shadows at the same opacity look flatter. |
| Hover / lifted card | 0 4px 8px rgba(0,0,0,.06), 0 12px 40px rgba(0,0,0,.12) | Increase both values proportionally from the resting state. Transition between the two on :hover for a satisfying lift. |
| Modal / overlay | 0 20px 60px rgba(0,0,0,.25), 0 4px 12px rgba(0,0,0,.12) | High enough to clearly separate the modal from the page without looking cartoonish. The tight inner shadow adds definition at the edge. |
| Input focus ring | 0 0 0 3px rgba(99,102,241,.35) | Zero blur, zero offset, spread 3px. A cleaner focus indicator than outline on most designs, and you control the color. Don’t remove the native outline without providing this. |
| Inset pressed state | inset 0 2px 4px rgba(0,0,0,.12) | For button :active states. Gives a physical "press" feel. Pair with a slight downward transform for maximum effect. |
| Dark mode card | 0 4px 24px rgba(0,0,0,.5) | Black shadows disappear on dark backgrounds. Go higher opacity, or switch to a colored glow: 0 4px 24px rgba(99,102,241,.3) using your brand color. |
| Glow / neon | 0 0 20px rgba(99,102,241,.6), 0 0 60px rgba(99,102,241,.2) | Two layers of zero-offset shadows at different blur radii. The tight inner glow defines the shape; the outer one creates the ambient spread. |
box-shadow applies to the element’s rectangular bounding box. If the element is a transparent PNG of a cat silhouette, the shadow appears as a rectangle around the image, not around the cat shape. The rounded corners (if any) from border-radius are respected, but alpha-transparent areas are not.
filter: drop-shadow(x y blur color) casts a shadow from the actual visible pixels of the element, respecting transparency. The cat silhouette gets a cat-shaped shadow. It also works correctly on <svg> elements with complex paths. The trade-off: no spread radius parameter, no inset option, and filter creates a new stacking context which can affect z-index behavior.
box-shadow: offset-x offset-y blur spread color (spread is the 4th value). filter: drop-shadow(): drop-shadow(offset-x offset-y blur color) — no spread, and the values go inside the function call. Easy to mix up when switching between them.
Both box-shadow and filter: drop-shadow() are suppressed in print stylesheets unless you explicitly include them. If your UI has important shadows that communicate information (like a floating action button or a dropdown that needs to look lifted even on paper), add a @media print override with a border or outline fallback.
box-shadow is entirely outside the layout flow — it paints over or under surrounding elements without affecting their position or triggering reflow. This is different from outline (also no layout effect) and very different from border (which is part of the box model and does affect layout). If you’re seeing overflow issues, it’s because the shadow paints outside the element’s boundary. Add overflow: visible on any ancestor that’s clipping it, or add padding to create space.overflow: hidden clips all painting outside the element’s border box, including shadows. This is a common surprise when you apply overflow: hidden to a card to clip its content, then discover the shadow disappears. The solution is to apply overflow: hidden to an inner wrapper for the content, and let the outer card element handle the shadow without clipping. A parent element with overflow: hidden on an ancestor further up the tree has the same effect, so check the whole chain when shadows disappear unexpectedly.#e0e5ec, the element must also be #e0e5ec; any difference in background color breaks the illusion entirely. Useful values to start from: 6px offset, 12px blur, shadows at roughly ±10% lightness from the background. The effect looks very poor on dark backgrounds and has significant accessibility concerns — the low contrast it relies on for its aesthetic is the same thing that makes it hard to read and hard to interact with for users with low vision.