CSS Drop Shadow Tutorial: box-shadow vs filter: drop-shadow()
Two Ways to Add Shadows
CSS offers two shadow mechanisms: box-shadow and filter: drop-shadow(). They look similar but behave differently in ways that directly affect your design. Picking the wrong one leads to rectangular shadows behind transparent pixels, clipped effects on irregular shapes, or confusing double-shadow artifacts.
Box Shadow vs. Drop Shadow
The core difference is shape. box-shadow follows the rectangular bounding box of an element. filter: drop-shadow() follows the visible alpha contour — the actual rendered pixels.
| Property | Follows | Ignores | Best For |
|---|---|---|---|
box-shadow | Element bounding box | Alpha transparency | Cards, buttons, containers |
drop-shadow() | Visible alpha contour | Bounding box geometry | PNG icons, clip-path shapes, SVG graphics |
When the element is a plain rectangle, both produce identical output. The divergence starts as soon as transparency enters the picture.
When Drop Shadow Follows Shape
This is the killer feature of drop-shadow(). If your element has transparent pixels — a PNG icon with a transparent background, an SVG with open areas, a clip-path cutout, or a rotated element — drop-shadow() traces the actual visible outline.
/* box-shadow — shadows the full rectangle */
.icon {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
/* drop-shadow — shadows only the opaque pixels */
.icon {
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
Apply both to a hamburger menu icon. box-shadow draws a rectangular shadow behind the transparent gaps between the lines. drop-shadow() casts a shadow that traces each line individually — the result looks correct.
Clip-Path Shapes
clip-path removes visual pixels but leaves the bounding box intact. box-shadow renders on the full rectangle; drop-shadow() respects the clip.
.arrow {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.3));
}
Using box-shadow here would produce a rectangular shadow behind a diamond shape — clearly wrong.
SVG and Transparent PNGs
SVGs often contain shapes with empty space between elements. A logo with cutout letterforms, an icon with a transparent center — box-shadow fills those gaps with shadow paint. drop-shadow() follows the inked pixels precisely.
Drop Shadow Syntax
filter: drop-shadow(h-offset v-offset blur color);
| Value | Required | Notes |
|---|---|---|
h-offset | Yes | Positive shifts right, negative left |
v-offset | Yes | Positive shifts down, negative up |
blur | Yes | Must be specified — no zero default like box-shadow |
color | No (but recommended) | Browser default varies — always specify explicitly |
Key differences from box-shadow syntax:
- No spread value —
drop-shadow()cannot enlarge or shrink the shadow - No inset keyword — inner shadows are not supported
- No multi-shadow — you cannot comma-separate multiple
drop-shadow()values in a singlefilter
Stacking Multiple Drop Shadows
Chain drop-shadow() calls within a single filter property to create layered effects:
.icon {
filter:
drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1))
drop-shadow(0 4px 8px rgba(0, 0, 0, 0.15));
}
Each drop-shadow() renders relative to the output of the previous filter. The second shadow is cast by the element plus its first shadow — which subtly shifts the result compared to box-shadow layering where each shadow casts independently from the element bounding box.
When to Use Each
Use box-shadow When
- The element is a rectangle with no transparency concerns — cards, modals, inputs, buttons
- You need inset shadows —
drop-shadow()does not supportinset - You need spread control — shrinking or growing the shadow shape
- You need multiple shadows with independent positioning — comma syntax is cleaner
- Performance is critical —
box-shadowcomposites more efficiently than filters
Use drop-shadow() When
- The element has transparent regions — PNG/SVG icons, logos with cutouts
- The element uses
clip-path— clipped shapes need contour-following shadows - The element is transformed in ways that alter its rendered shape
- You want a shadow around irregular borders — dashed or dotted borders cast uneven shadows with
box-shadow
Inheritance and Composition
filter applies to the entire element and its subtree. A filter on a parent element affects all descendants as a composited group. This means:
drop-shadow()on a parent casts a single shadow around the combined alpha shape of the parent and all children — efficient for grouped elementsbox-shadowon a parent only shadows the parent bounding box — children may visually extend beyond the shadow
This distinction matters for icon groups, composite components, and card layouts with overflow content.
/* Single shadow around the whole group */
.icon-group {
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
}
/* Shadow only on the container box — children extend beyond */
.icon-group {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
Performance Comparison
Both properties are GPU-accelerated, but they compose differently:
| Factor | box-shadow | drop-shadow() |
|---|---|---|
| Compositing | Painted with the element layer | Applied as a filter — requires an extra compositing step |
| Multiple shadows | Single paint operation | Each drop-shadow() is a separate filter pass |
| Animation | Smooth with transition or @keyframes | Can trigger filter recalculation — test on low-end devices |
| Large blur values | Efficient — rasterized at paint time | Expensive — blur radius multiplies compositing cost |
| Alpha analysis | Not needed — rectangle is known | Browser must traverse pixel alpha to compute shape |
For most UI elements, the performance gap is negligible. It matters only when you animate shadows on dozens of elements simultaneously or use blur radii beyond 40px.
Common Mistakes
Using Both on the Same Element
Applying box-shadow and drop-shadow() to the same element produces a shadow of a shadow. The drop-shadow() is cast from the painted output including the box-shadow, resulting in a blurry, misaligned double shadow.
Pick one. Never combine both on the same element.
Forgetting the Blur Value
box-shadow defaults blur to 0 if omitted. drop-shadow() does not — omitting blur is a syntax error or produces unexpected results depending on the browser.
/* Valid box-shadow — blur defaults to 0 */
box-shadow: 4px 4px rgba(0, 0, 0, 0.2);
/* Invalid drop-shadow — blur is required */
filter: drop-shadow(4px 4px rgba(0, 0, 0, 0.2));
/* Correct */
filter: drop-shadow(4px 4px 0px rgba(0, 0, 0, 0.2));
Drop Shadow on Dense Layouts
Because drop-shadow() follows the alpha shape, it can produce visually noisy shadows on elements with many small transparent gaps — like text with letter-spacing or elements with detailed SVG patterns. For these cases, box-shadow on a wrapper element often produces a cleaner result.
Browser Support
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
box-shadow | 10+ | 4+ | 5.1+ | 12+ |
filter: drop-shadow() | 53+ | 35+ | 9.1+ | 12+ |
backdrop-filter (glass) | 76+ | 103+ | 9+ | 17+ |
drop-shadow() has slightly newer support than box-shadow, but both work in all modern browsers. No prefixes are needed.
Key Takeaways
box-shadowfollows the bounding box;drop-shadow()follows the alpha contour — this is the deciding factordrop-shadow()has no spread, no inset, and no multi-shadow comma syntax- Chain
drop-shadow()calls in a singlefilterto stack alpha-aware shadows - Use
drop-shadow()for PNG/SVG icons, clip-path shapes, and irregular forms - Use
box-shadowfor rectangular elements, inset effects, spread control, and better composition performance - Never combine both on the same element — pick one
Try It Yourself
Build and compare both shadow types in our CSS Box Shadow Generator. Experiment with offset, blur, and color values — then copy the generated code directly into your project.
Related Guides
- CSS Box Shadow Guide — Master offset, blur, spread, inset, and multi-layer shadow patterns
- CSS Visual Effects — Combine shadows with gradients, glow effects, and glass morphism for modern UI
- CSS Design Tips — Practical techniques for pairing shadows with gradients and color