What is box-shadow in CSS?

box-shadow adds a shadow behind (or inside) an element’s box. It’s one of the fastest ways to make flat UI look like it has depth: cards lift off the page, buttons look clickable, and panels feel “real”.

A few key beginner-friendly facts:

  • It follows the element’s shape. If you have border-radius, the shadow rounds too.
  • It does not change layout. Shadows don’t take up flow space; they just paint.
  • You can stack multiple shadows. One element can have many shadows (comma-separated) for more realistic results.
  • You can also put shadows inside. That’s the inset keyword (we’ll do that later).
 .card { box-shadow: 0px 14px 30px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: 0px 6px 0px rgba(0, 0, 0, 0.25); } 
 .card { box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.0); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 260px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 320px; border-radius: 16px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px 18px 16px 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } 
 

Box shadow demo

Click the CSS snippets to switch between shadow styles.

CSS box-shadow syntax and parameters

Here’s the classic syntax:

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

And here’s what each part means:

  • x-offset: moves the shadow left/right. Negative values go left, positive values go right.
  • y-offset: moves the shadow up/down. Negative values go up, positive values go down.
  • blur: how soft the shadow edge is. Bigger blur = softer shadow.
  • spread: grows or shrinks the shadow size. Positive = larger, negative = tighter.
  • color: the shadow color (often semi-transparent).
  • inset (optional): puts the shadow inside the element.

Two small but important details:

  • Blur and spread are optional. If you omit spread, it defaults to 0.
  • Color can appear anywhere in the value. People usually put it at the end because it’s readable.
 .card { box-shadow: 18px 18px 0px 0px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: -18px 18px 0px 0px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: 0px 18px 18px 0px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: 0px 18px 18px 10px rgba(0, 0, 0, 0.18); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 280px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 320px; border-radius: 16px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card ul { margin: 0; padding-left: 18px; font-size: 14px; opacity: 0.85; line-height: 1.4; } .card li { margin: 6px 0; } 
 

Move & shape the shadow

  • x-offset: left / right
  • y-offset: up / down
  • blur: soft edge
  • spread: grow / shrink

Interactive box-shadow playground

This playground has four sliders that live-edit a single box-shadow declaration:

  • Horizontal offset (x)
  • Vertical offset (y)
  • Blur radius
  • Spread radius

Notice how the shadow can feel like it’s “floating” (bigger blur) or “stamped” (low blur). Also try negative x or y values.

 .card { box-shadow: 0px 18px 30px 0px rgba(0, 0, 0, 0.22); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 320px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; font-family: ui-sans-serif, system-ui, sans-serif; } .card { width: 340px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px 18px 16px 18px; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } 
 

Slide me around

Use the sliders to control x, y, blur, and spread.

CSS box-shadow color

The most common mistake with shadows is using a fully opaque color like #000. Real shadows are usually soft and semi-transparent.

You can use any CSS color format:

  • Named colors: black, rebeccapurple, etc.
  • Hex: #000, #111, #1f2937 (no alpha unless you use 8-digit hex)
  • RGB/RGBA: rgb(0 0 0), rgba(0, 0, 0, 0.25)
  • HSL/HSLA: great for tweaking lightness and keeping consistent hues

RGBA is the easiest because you can control opacity directly with the last number.

 .card { box-shadow: 0px 16px 34px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: 0px 16px 34px rgba(37, 99, 235, 0.25); } 
 .card { box-shadow: 0px 16px 34px hsl(260 80% 45% / 0.28); } 
 .card { box-shadow: 0px 16px 34px #111111; } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 300px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 340px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } 
 

Color matters

Try the opaque hex shadow last. It’s… intense.

CSS box-shadow examples you’ll actually use

Let’s build a small “shadow library”. These examples focus on common UI looks:

  • Soft card: subtle, blurry, low opacity
  • Hard shadow: sharp edge, sticker style
  • Neon glow: blur + colored shadow around the element
  • Layered shadows: multiple shadows for realism
 .card { box-shadow: 0px 14px 40px rgba(0, 0, 0, 0.12); } 
 .card { box-shadow: 10px 10px 0px rgba(0, 0, 0, 0.25); } 
 .card { box-shadow: 0px 0px 28px rgba(16, 185, 129, 0.45); } 
 .card { box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.08), 0px 10px 25px rgba(0, 0, 0, 0.12); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 320px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 360px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } .badge-row { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; } .badge { font-size: 12px; padding: 6px 10px; border-radius: 999px; border: 1px solid #e6e7ee; background: #fbfbfd; } 
 

Shadow styles

Click each snippet to swap the vibe.

soft hard glow layered

CSS box-shadow bottom only

“Bottom-only shadow” is a super common request: you want the object to feel like it’s sitting on a surface.

There are two go-to approaches:

  1. Use a big positive y-offset with a negative spread so the shadow stays mostly underneath.
  2. Use a pseudo-element to draw a custom shadow shape (more control, great for “contact shadows”).

Bottom-only with offset + negative spread

This is the quick win. The negative spread “pulls in” the shadow so it doesn’t wrap around the sides too much.

 .card { box-shadow: 0px 22px 22px -16px rgba(0, 0, 0, 0.28); } 
 .card { box-shadow: 0px 30px 40px -26px rgba(0, 0, 0, 0.30); } 
 .card { box-shadow: 0px 12px 14px -10px rgba(0, 0, 0, 0.26); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 320px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 340px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } 
 

Bottom-only look

Notice how the sides stay cleaner with negative spread.

Bottom-only with a pseudo-element (extra control)

This technique is more “design-y”: we create a fake shadow blob under the card using ::after. It’s great when you want a soft ellipse or a long contact shadow.

  • The card needs position: relative.
  • The pseudo-element sits behind using z-index.
  • We blur the pseudo-element itself to fake a shadow.
 .card { position: relative; }

.card::after {
content: "";
position: absolute;
left: 8%;
right: 8%;
bottom: -14px;
height: 18px;
background: rgba(0, 0, 0, 0.28);
filter: blur(16px);
z-index: -1;
}
 .card { position: relative; } .card::after { content: ""; position: absolute; left: 18%; right: 18%; bottom: -10px; height: 14px; background: rgba(0, 0, 0, 0.30); filter: blur(14px); z-index: -1; } 
 .card { position: relative; } .card::after { content: ""; position: absolute; left: 10%; right: 10%; bottom: -22px; height: 24px; background: rgba(0, 0, 0, 0.26); filter: blur(22px); z-index: -1; } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 340px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; isolation: isolate; } .card { width: 360px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } 
 

Pseudo-element shadow

This is a custom “shadow shape” under the card.

Notice that for the pseudo-element to show up at all, we need an ancestor element with either a z-index along with position:relative;, or isolation: isolate;. That way the pseudo-element isn't behind everything, including the body background.

CSS box-shadow inside with inset

The inset keyword turns the shadow inward. This is perfect for:

  • “Pressed” buttons
  • Inputs with inner depth
  • Cutout / recessed panels

Syntax-wise, it’s the same values, just with inset added:

box-shadow: inset x y blur spread color;

 .card { box-shadow: inset 0px 10px 18px rgba(0, 0, 0, 0.18); } 
 .card { box-shadow: inset 0px 2px 0px rgba(255, 255, 255, 0.75); } 
 .card { box-shadow: inset 0px 10px 18px rgba(0, 0, 0, 0.14), inset 0px -10px 18px rgba(255, 255, 255, 0.55); } 
 .card { box-shadow: inset 0px 0px 0px rgba(0, 0, 0, 0), 0px 14px 32px rgba(0, 0, 0, 0.14); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 340px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; } .card { width: 360px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; font-family: ui-sans-serif, system-ui, sans-serif; } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } .hint { margin-top: 12px; font-size: 12px; opacity: 0.65; } 
 

Inset shadows

Inset can make elements look pressed or carved out.

Try the “two inset shadows” snippet for a recessed feel.

Animating CSS box-shadow on hover

Yes, you can animate box-shadow, and it’s a super common “micro-interaction” for buttons and cards. The idea is simple: the element “lifts” when you hover it, so the shadow gets bigger and softer.

The key is to animate it with transition. For hover effects, you’ll usually transition:

  • box-shadow (the obvious one)
  • transform (a tiny upward move sells the lift)

One important tip: keep your hover shadows tasteful. If the shadow jumps from “barely there” to “huge shadow”, the UI feels twitchy.

 .card { transition: box-shadow 220ms ease, transform 220ms ease; }

.card:hover {
box-shadow: 0px 18px 45px rgba(0, 0, 0, 0.18);
transform: translateY(-4px);
}
 .card { transition: box-shadow 220ms ease, transform 220ms ease; } .card:hover { box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.08), 0px 18px 40px rgba(0, 0, 0, 0.14); transform: translateY(-4px); } 
 .card { transition: box-shadow 220ms ease, transform 220ms ease; } .card:hover { box-shadow: 0px 0px 34px rgba(59, 130, 246, 0.42); transform: translateY(-2px); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 320px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; font-family: ui-sans-serif, system-ui, sans-serif; } .card { width: 360px; border-radius: 18px; border: 1px solid #e6e7ee; background: #ffffff; padding: 18px; box-shadow: 0px 10px 26px rgba(0, 0, 0, 0.10); } .card h3 { margin: 0 0 8px 0; font-size: 18px; } .card p { margin: 0; font-size: 14px; opacity: 0.75; line-height: 1.4; } .hint { margin-top: 12px; font-size: 12px; opacity: 0.65; } 
 

Hover me

Switch snippets, then hover the card to see the animation.

The base shadow is in Default CSS. Hover changes it.

Common pitfalls when animating box-shadow

  • Only transitioning on :hover won’t work. Put transition on the base element (not inside :hover), so it animates both in and out.
  • Animating huge blurs everywhere can be expensive. Prefer moderate blur values, especially if many elements animate at once.
  • Don’t forget keyboard users. If it’s a link or button, add the same “lift” effect on :focus-visible so it feels consistent.
 .button { transition: box-shadow 200ms ease, transform 200ms ease; }

.button:hover,
.button:focus-visible {
box-shadow: 0px 14px 30px rgba(0, 0, 0, 0.18);
transform: translateY(-3px);
}
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { min-height: 240px; display: grid; place-items: center; padding: 24px; background: #f6f7fb; font-family: ui-sans-serif, system-ui, sans-serif; } .button { border: 0; border-radius: 999px; background: #111827; color: #ffffff; padding: 12px 18px; font-size: 14px; cursor: pointer; box-shadow: 0px 8px 18px rgba(0, 0, 0, 0.12); } .button:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; } 
 

That’s the core pattern: a reasonable base shadow, a slightly stronger hover/focus shadow, and a small upward transform. It’s subtle, but it makes UI feel alive.

Learn more about transitions in the CSS Transition Interactive Tutorial

Common box-shadow mistakes and helpful tips

Tip: use opacity for realistic shadows

If your shadow looks like a thick black outline, reduce opacity first. A lot of UI shadows live around 0.06 to 0.20 opacity.

Tip: stack multiple shadows for depth

Real shadows often have both a tight, subtle dark part near the object and a bigger, softer fade further away. That’s why layered shadows look “more expensive”.

Tip: match shadow direction to a light source

If your UI implies light from above, use a positive y-offset. If you mix directions randomly, things start to look like they’re lit by a disco ball.

Tip: performance and when to be gentle

Shadows are generally fine, but lots of huge blurred shadows on many elements can hurt performance on slower devices. If something feels “laggy”, try reducing blur and opacity, or using fewer layered shadows.

 .grid { gap: 18px; }

.card {
box-shadow: 0px 20px 55px rgba(0, 0, 0, 0.12);
}
 .grid { gap: 18px; } .card { box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.06), 0px 10px 22px rgba(0, 0, 0, 0.10); } 
 .grid { gap: 18px; } .card { box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.08), 0px 12px 30px rgba(0, 0, 0, 0.12), 0px 30px 70px rgba(0, 0, 0, 0.10); } 
 *, ::before, ::after { box-sizing: border-box; } .demo-wrap { padding: 24px; background: #f6f7fb; font-family: ui-sans-serif, system-ui, sans-serif; } .grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); } .card { border-radius: 16px; border: 1px solid #e6e7ee; background: #ffffff; padding: 14px; } .card h4 { margin: 0 0 6px 0; font-size: 14px; } .card p { margin: 0; font-size: 12px; opacity: 0.75; line-height: 1.35; } @media (max-width: 560px) { .grid { grid-template-columns: 1fr; } } 
 

Card A

Same markup, different shadows.

Card B

Try each snippet.

Card C

Layering can feel more natural.

Quick CSS Box Shadow recap

  • box-shadow uses x, y, blur, spread, and color.
  • For nicer shadows, use semi-transparent colors like rgba(0, 0, 0, 0.12).
  • “Bottom-only” shadows are often done with a positive y-offset and negative spread, or a pseudo-element for custom shapes.
  • Add inset to place the shadow inside the element.
  • Layering multiple shadows can make the result look more realistic and less flat.

Further reading and resources

If you are looking to add a shadow that will respect the shape of the transparent PNG or SVG images, check out our CSS Drop Shadow Interactive Tutorial.

If you want to add a shadow to text, check out our CSS Text Shadow Interactive Tutorial.