X Xerobit

CSS Box Shadow — How to Use box-shadow Correctly

CSS box-shadow adds depth, elevation, and focus effects to elements. Here's the syntax, how each parameter works, and practical shadow patterns for cards, modals, and buttons.

Mian Ali Khalid · · 7 min read
Use the tool
Box Shadow Generator
Visual CSS box-shadow builder. Control offset, blur, spread, color, inset. Multi-layer shadows. Live preview + copyable CSS.
Open Box Shadow Generator →

box-shadow adds shadows to HTML elements. A single line of CSS can make a flat card look elevated, indicate depth in a UI, or create a focus ring for accessibility. But box-shadow has 6 parameters and several non-obvious behaviors — stacking, inset, spread radius — that most developers learn by trial and error.

Use the Box Shadow Generator to build box-shadow values visually and copy the CSS.

The box-shadow syntax

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

Multiple shadows are comma-separated (first shadow is on top):

box-shadow: shadow1, shadow2, shadow3;

Parameters explained

offset-x (required): Horizontal position. Positive = shadow right, negative = shadow left. 0 = directly below.

offset-y (required): Vertical position. Positive = shadow down, negative = shadow up. 0 = directly beside.

blur-radius (optional, default 0): Controls how blurred the shadow is. 0 = hard edge shadow. Higher values = more diffuse. Cannot be negative.

spread-radius (optional, default 0): Expands or contracts the shadow size before blurring. Positive = shadow larger than element. Negative = shadow smaller.

color (optional, default black or currentColor): The shadow color. Supports all CSS color values including rgba() for transparency.

inset (optional): Makes the shadow appear inside the element instead of outside.

Shadow examples

No shadow (default)

box-shadow: none;

Hard drop shadow

box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.2);
/* 4px right, 4px down, 0 blur = sharp edge */

Soft elevation shadow (cards)

box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
/* Centered below element, medium blur, subtle */

Deep elevation (modals, dropdowns)

box-shadow: 0 8px 32px rgba(0, 0, 0, 0.24);

Inset shadow (pressed button effect)

box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);

Ring (focus indicator)

box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.6);
/* No offset, no blur, positive spread = ring around element */

This is a common pattern for custom focus styles. The 0 0 0 means no directional offset or blur — the shadow appears as a uniform ring. The spread-radius 3px defines the ring thickness.

Practical patterns

Card elevation system

A consistent elevation system uses increasing shadow intensity for higher elevation levels:

/* Tailwind CSS-style elevation scale */
.shadow-sm   { box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); }
.shadow      { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); }
.shadow-md   { box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); }
.shadow-lg   { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); }
.shadow-xl   { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); }
.shadow-2xl  { box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); }
.shadow-inner { box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); }

The multi-layer shadows (two comma-separated values) create more realistic elevation — a short diffuse ambient shadow plus a longer directional cast shadow.

Interactive elevation change

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

.card:hover {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  transform: translateY(-2px); /* Lift effect */
}

Combining box-shadow change with transform: translateY(-2px) creates a physical “lifting” effect on hover. The shadow grows as if the card is moving away from the surface.

Colored shadows (brand effects)

.btn-primary {
  background: #3B82F6;
  box-shadow: 0 4px 14px rgba(59, 130, 246, 0.4);
  /* Blue shadow matching the button color */
}

.btn-primary:hover {
  box-shadow: 0 6px 20px rgba(59, 130, 246, 0.5);
}

Matching shadow color to element color creates a “glow” effect popular in modern UI design.

Accessible focus ring

/* Override default browser outline with box-shadow */
.btn:focus-visible {
  outline: none; /* Remove default */
  box-shadow: 0 0 0 3px white, 0 0 0 5px #3B82F6;
  /* White ring + blue ring = visible on any background */
}

The two-shadow focus ring (white inner + colored outer) is visible against both light and dark backgrounds.

Text input focus state

.input {
  border: 1px solid #D1D5DB;
  box-shadow: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.input:focus {
  border-color: #3B82F6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
  outline: none;
}

Inset shadows

The inset keyword moves the shadow inside the element’s border box:

/* Sunken/pressed state */
.pressed {
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
}

/* Inset glow */
.active-tab {
  box-shadow: inset 0 -3px 0 #3B82F6; /* Bottom border using shadow */
}

The inset bottom border (inset 0 -3px 0 color) is a common technique for active tab indicators — it avoids the box model complexity of actual borders.

Hollow shadow effect

Combining inset and outset shadows:

.neumorphic {
  background: #e0e5ec;
  box-shadow:
    6px 6px 12px #b8bec7,  /* Dark shadow bottom-right */
    -6px -6px 12px #ffffff; /* Light shadow top-left */
}

This “neumorphic” effect (raised surface on a matching background) requires an exact background color match between the element and its container.

Multiple shadows

Shadows are rendered from last to first (last listed shadow is at the bottom, first is on top):

/* Three-layer realistic shadow */
box-shadow:
  0 1px 1px rgba(0, 0, 0, 0.08),   /* Top: tight, transparent */
  0 2px 4px rgba(0, 0, 0, 0.06),   /* Mid: medium diffusion */
  0 4px 16px rgba(0, 0, 0, 0.04);  /* Bottom: diffuse ambient */

Using multiple shadows with increasing blur and decreasing opacity creates a more physically accurate shadow than a single shadow value.

Performance considerations

box-shadow triggers paint but not layout in most browsers. Animating box-shadow forces GPU re-compositing which can cause frame drops on slow devices.

For smooth shadow animations on performance-critical elements:

/* Workaround: animate opacity of a pseudo-element shadow */
.card {
  position: relative;
}

.card::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  opacity: 0;
  transition: opacity 0.2s;
}

.card:hover::after {
  opacity: 1; /* Animates opacity (GPU-composited) not box-shadow */
}

For most interfaces this is premature optimization — animate box-shadow directly until profiling shows it’s a bottleneck.

filter: drop-shadow() is the alternative to box-shadow that works with non-rectangular shapes (SVGs, clipped elements). It’s slower and less flexible but necessary when you need a shadow that follows an irregular element shape.

box-shadow vs outline vs border

PropertyPurposeSpace impactCan be inset?
box-shadowVisual effect, focus ringsNo layout impactYes
outlineFocus indicator (browser default)No layout impactNo
borderStructural boundaryTakes up spaceN/A

Use box-shadow when you need a visual effect that doesn’t push content around. Use border when the boundary is part of the layout.

The Box Shadow Generator

The Box Shadow Generator lets you adjust all six parameters visually and see the live preview. You can add multiple shadow layers, copy the CSS, and generate code for both standard shadows and inset effects.


Related posts

Related tool

Box Shadow Generator

Visual CSS box-shadow builder. Control offset, blur, spread, color, inset. Multi-layer shadows. Live preview + copyable CSS.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.