The box-shadow property is one of the most useful tools in CSS for adding depth, focus, and visual hierarchy to UI elements. It looks simple on the surface but has enough flexibility to produce everything from subtle card lifts to complex layered lighting effects.

This guide covers the full syntax, every parameter explained, and the patterns actually used in modern UI design.

The syntax

box-shadow: offset-x offset-y blur-radius spread-radius color;

/* Example */
.card {
  box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
}

Each value does a specific job:

Only offset-x, offset-y, and color are required. Blur and spread default to 0 if omitted.

Blur radius vs spread radius

These two are commonly confused:

/* Blur only — shadow fades out gradually */
box-shadow: 0 4px 16px rgba(0,0,0,0.2);

/* Spread only — shadow is the same size as element, sharp edge */
box-shadow: 0 0 0 4px rgba(124,106,247,0.4);

/* Negative spread — shadow is smaller than element */
box-shadow: 0 8px 16px -4px rgba(0,0,0,0.3);

Negative spread is useful for containing the shadow so it only appears below an element rather than on all sides.

Inset shadows

Adding the inset keyword moves the shadow inside the element instead of outside:

.input {
  /* Outer shadow */
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

.input:focus {
  /* Inset shadow gives a pressed-in feel */
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.3);
}

.well {
  /* Inset all sides for a recessed container */
  box-shadow: inset 0 0 12px rgba(0,0,0,0.25);
}

Multiple shadows

You can stack multiple shadows on a single element by separating them with commas. They layer from top to bottom — the first one listed is on top.

.card {
  box-shadow:
    0 1px 2px rgba(0,0,0,0.07),   /* tight shadow for grounding */
    0 4px 8px rgba(0,0,0,0.07),   /* mid-range depth */
    0 12px 24px rgba(0,0,0,0.1);  /* wide ambient shadow */
}

Layering 2–3 shadows like this creates a much more realistic depth effect than a single shadow, because real-world shadows have multiple components (key light, ambient light, ground reflection).

Common UI patterns

Subtle card lift

.card {
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
  transition: box-shadow 0.2s, transform 0.2s;
}
.card:hover {
  box-shadow: 0 8px 24px rgba(0,0,0,0.15);
  transform: translateY(-2px);
}

Focus ring (accessibility)

.button:focus-visible {
  outline: none;
  /* Use box-shadow instead of outline for rounded focus rings */
  box-shadow: 0 0 0 3px rgba(124,106,247,0.6);
}

Colored glow effect

.btn-primary {
  background: #7c6af7;
  box-shadow: 0 4px 20px rgba(124,106,247,0.5);
}
.btn-primary:hover {
  box-shadow: 0 6px 28px rgba(124,106,247,0.7);
}

Hard shadow (retro / brutalist style)

.retro-card {
  border: 2px solid #e2e8f0;
  /* No blur = completely hard edge */
  box-shadow: 4px 4px 0 #e2e8f0;
}

Performance tip

Animating box-shadow directly causes the browser to repaint on every frame, which can be slow on complex pages. A better approach is to animate opacity on a pseudo-element or use filter: drop-shadow() for SVGs.

For hover animations: preload both shadow states and transition between them using the transition property. This is faster than generating a new shadow on every frame.

box-shadow vs filter: drop-shadow()

filter: drop-shadow() follows the actual shape of an element including transparent areas — useful for PNG images and SVGs. box-shadow only follows the rectangular box of the element. For regular HTML elements, box-shadow is always the right choice.

Build box shadows visually

Use our free Box Shadow Generator to create and preview shadows in real time with live code output — no manual tweaking needed.

Open Box Shadow Generator →