DEV Community

Snappy Tools
Snappy Tools

Posted on

CSS Box Shadow: The Complete Guide (with Copy-Paste Examples)

The box-shadow property sounds simple — but once you start layering shadows, building card elevation systems, or trying to recreate that exact Figma drop shadow in CSS, the syntax gets complicated fast.

This guide covers everything from the basics to advanced multi-layer techniques, with copy-paste examples throughout.

The Basic Syntax

box-shadow: offset-x offset-y blur-radius spread-radius color;
Enter fullscreen mode Exit fullscreen mode

Each part explained:

Parameter What it does
offset-x Horizontal position. Positive = right, negative = left
offset-y Vertical position. Positive = down, negative = up
blur-radius Edge softness. 0 = crisp, higher = more diffuse
spread-radius Expands (+) or contracts (−) the shadow before blur
color Any valid CSS color, usually rgba() for opacity control

A simple card shadow:

.card {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
Enter fullscreen mode Exit fullscreen mode

Inset Shadows

Add the inset keyword to flip the shadow inside the element — useful for pressed button states and recessed input fields:

/* Pressed button */
.btn:active {
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.2);
}

/* Recessed input field */
input {
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}
Enter fullscreen mode Exit fullscreen mode

Multiple Shadow Layers

You can stack multiple shadows with a comma-separated list. The first shadow is drawn on top (closest to the element):

.card {
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.12),   /* tight inner shadow for crispness */
    0 6px 24px rgba(0, 0, 0, 0.08);  /* wide outer shadow for ambient depth */
}
Enter fullscreen mode Exit fullscreen mode

This two-layer pattern mimics how real shadows work — a bright direct light source creates a sharp inner shadow, while ambient light creates a soft outer glow.

The Four Elevation Levels

Here's a practical elevation scale for UI design:

/* Flat — default state */
.elevation-0 {
  box-shadow: none;
}

/* Subtle — cards, sections */
.elevation-1 {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.10), 0 2px 8px rgba(0, 0, 0, 0.06);
}

/* Standard — hover state, dropdowns */
.elevation-2 {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12), 0 8px 24px rgba(0, 0, 0, 0.08);
}

/* Elevated — modals, dialogs */
.elevation-3 {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14), 0 16px 48px rgba(0, 0, 0, 0.10);
}
Enter fullscreen mode Exit fullscreen mode

Glow Effects

Set both offsets to 0 and use a saturated colour for a glow:

/* Green glow */
.btn-primary:focus {
  box-shadow: 0 0 0 3px rgba(47, 133, 90, 0.4);
}

/* Stacked glow for stronger effect */
.notification-badge {
  box-shadow:
    0 0 8px rgba(229, 62, 62, 0.5),
    0 0 24px rgba(229, 62, 62, 0.25);
}
Enter fullscreen mode Exit fullscreen mode

Using box-shadow as a Border

box-shadow can replace a border without affecting layout — useful for focus rings and outlines:

/* Focus ring that respects border-radius */
button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.6);
}

/* 2px green border without layout shift */
.selected-card {
  box-shadow: 0 0 0 2px #2f855a;
}
Enter fullscreen mode Exit fullscreen mode

Neumorphism

The neumorphic trend uses two shadows in opposite directions on a matching background:

/* Element background must match --surface */
:root {
  --surface: #e0e5ec;
}

.neumorphic {
  background: var(--surface);
  box-shadow:
    9px 9px 16px #b8bec7,    /* dark shadow — bottom-right */
    -9px -9px 16px #ffffff;  /* light shadow — top-left */
}

/* Pressed state */
.neumorphic:active {
  box-shadow:
    inset 4px 4px 8px #b8bec7,
    inset -4px -4px 8px #ffffff;
}
Enter fullscreen mode Exit fullscreen mode

Animating box-shadow

Animating box-shadow directly triggers a repaint on every frame. For smooth hover transitions, it's still acceptable for most UI:

.card {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.card:hover {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  transform: translateY(-2px);
}
Enter fullscreen mode Exit fullscreen mode

For high-frequency animations (scroll-tied effects), animate opacity on a ::after pseudo-element instead — this stays on the compositor layer.

box-shadow vs filter: drop-shadow()

box-shadow filter: drop-shadow()
Follows Element's box Actual rendered shape
Supports inset
Supports spread
Multiple layers ✅ (comma list) ❌ (wrap in multiple elements)
Best for Buttons, cards, divs Transparent PNGs, SVGs

Converting Figma Shadows to CSS

Figma's shadow panel maps directly to CSS:

  • Xoffset-x
  • Yoffset-y
  • Blurblur-radius
  • Spreadspread-radius
  • Colour + opacityrgba() or hsla()
  • Inner Shadow → add inset keyword

Quick Reference: Common Patterns

/* Soft card */
box-shadow: 0 1px 3px rgba(0,0,0,0.10), 0 2px 8px rgba(0,0,0,0.06);

/* Material elevation 2 */
box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.08);

/* Modal / dialog */
box-shadow: 0 8px 32px rgba(0,0,0,0.15), 0 20px 64px rgba(0,0,0,0.10);

/* Focus ring */
box-shadow: 0 0 0 3px rgba(66,153,225,0.5);

/* Inset (pressed button) */
box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);

/* Neumorphic (on #e0e5ec background) */
box-shadow: 9px 9px 16px #b8bec7, -9px -9px 16px #ffffff;

/* Remove shadow */
box-shadow: none;
Enter fullscreen mode Exit fullscreen mode

Building Shadows Without Guessing

If you'd rather adjust sliders than type raw CSS, the CSS Box Shadow Generator at SnappyTools gives you a live preview with multi-layer support, Figma-style controls, and a one-click copy button. No install, runs 100% in the browser.


The key insight with box-shadow is that single-layer shadows look flat. Stack a tight, slightly opaque shadow with a wider, more transparent one — and you get depth that actually looks right.

Top comments (0)