🌑 CSS Box Shadow Generator

Build beautiful layered shadows visually. Drag, tune, and copy your CSS instantly.

Quick Presets
Shadow Layers
Active Layer
Offset X 0px
Offset Y 0px
Blur Radius 20px
Spread Radius 0px
Opacity 35%
Shadow Color
Inset shadow
Preview Box
Box Color
Width 200px
Height 200px
Border Radius 12px
Live Preview
Box Shadow
Generated CSS
History
No history yet — make changes to record them.

About CSS Box Shadows

The box-shadow Property

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.

Layer Order Matters

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.

Performance Tips

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.

The one box-shadow behaviour that genuinely hurts production sites
Animating box-shadow is more expensive than it looks. Here’s the actual mechanism and the fix that actually works.

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:

/* The element itself — no shadow here */
.card { position: relative; }

/* Shadow lives on the pseudo-element */
.card::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: 0 8px 40px rgba(0,0,0,.22);
  opacity: 0;
  transition: opacity .2s ease;
}

/* On hover, fade in the shadow — GPU only */
.card:hover::after { opacity: 1; }

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.

Shadow values that actually work across real designs
Tested against light backgrounds, dark backgrounds, and coloured surfaces. The RGBA values are a starting point — adjust opacity based on your specific background color.
Use caseValueNotes
Resting card0 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 card0 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 / overlay0 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 ring0 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 stateinset 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 card0 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 / neon0 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 vs filter: drop-shadow() — they are not interchangeable
The practical difference only matters for certain element types, but when it does matter, the wrong choice looks obviously broken.
box-shadow follows the bounding rectangle

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.

drop-shadow follows the alpha channel

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.

Syntax difference worth remembering

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.

Neither shows in print by default

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.

Other CSS Tools
Questions
No. 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.
There’s no hard limit in the spec. In practice, 2–3 layers is the visual sweet spot for realistic depth — more than that rarely adds anything perceptible. Performance-wise, each additional layer adds paint cost. Where this becomes a real concern is if you have many elements with multiple shadows on the same page, particularly during scroll or resize. If you’re building a card grid with 50+ items, use a single shadow per card rather than a layered approach.
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.
Two shadows on the same element: one slightly lighter than the background color going top-left, one slightly darker going bottom-right (or vice versa depending on your light source direction). The element’s own background must be the same color as the page background — this is the part most people get wrong. If the page is #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.