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
insetkeyword (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:
- Use a big positive y-offset with a negative spread so the shadow stays mostly underneath.
- 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
:hoverwon’t work. Puttransitionon 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-visibleso 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-shadowuses 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
insetto 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.
