# How to Build a Perfect CSS Box Shadow in 5 Minutes

> A good CSS box shadow is subtle, layered, and matches the lighting of the rest of the design. Learn the four-part anatomy of a real shadow (offset, blur, spread, color), the trick of stacking two shadows for depth, and the values that work in production.

URL: https://uttir.com/blog/how-to-build-a-perfect-css-box-shadow-in-5-minutes
Published: 2026-08-21
Author: Uttir
Reading time: 6 min
Tags: css, design, shadow, box-shadow, web-design

## Quick answer

A perfect CSS box shadow is two stacked shadows: a tight, dark one close to the element (the contact shadow) and a soft, lighter one further away (the ambient shadow). The four parts of every box-shadow: offset-x, offset-y, blur, spread, and color. For the contact shadow, use small offset (0-2px) and small blur (2-4px) with low opacity black. For the ambient shadow, use larger offset (4-12px) and larger blur (8-24px) with even lower opacity. The [Uttir CSS Gradient Generator](/css-gradient-generator) shows the same shadow-stacking principle for gradients, and the [Color Picker](/color-picker) is the right tool to dial in the exact shadow color with alpha.

Most CSS box shadows look bad. They are too dark, too sharp, too uniform, or too obviously a "drop shadow" that the design never asked for. The reason is that a single box-shadow value, with the default blur and zero spread, does not look like real-world lighting. Real shadows have multiple parts: a tight, dark contact shadow where the object meets the surface, and a soft, lighter ambient shadow that fades off into the background.

This post covers the anatomy of a good box shadow, the trick of stacking two shadows for depth, the values that work in production, and how to tune them. The [Uttir CSS Gradient Generator](/css-gradient-generator) applies the same layered approach to gradients; the [Color Picker](/color-picker) is the right tool for dialing in the exact shadow color with alpha.

## The four parts of every box-shadow

Every CSS box-shadow has four parts, in this order: offset-x, offset-y, blur-radius, spread-radius, and color. The shorthand is `box-shadow: X Y Blur Spread Color`. The first two are required; the rest have defaults (blur defaults to 0, spread defaults to 0, color defaults to the current text color).

**Offset-x and offset-y.** How far the shadow is shifted from the element. Positive values shift right and down; negative values shift left and up. A shadow with offset (0, 0) is centered on the element; a shadow with offset (0, 4) is shifted 4 pixels down. For a top-down light source (the default in most UI design), use a small positive Y offset (2-8 pixels).

**Blur-radius.** How soft the shadow is. A value of 0 produces a hard, sharp shadow (rarely what you want). Larger values produce softer shadows. For UI design, blur is usually 4-24 pixels.

**Spread-radius.** How much larger than the element the shadow is. A value of 0 means the shadow is the same size as the element. Positive values make it larger (and softer-looking); negative values make it smaller (and tighter). For most UI shadows, spread is 0 or slightly negative.

**Color.** The shadow color. Almost always a black or near-black with low alpha (10-25%). A fully opaque black shadow looks like a hard cutout; a black shadow with 20% alpha looks like a real shadow. The [Hex to RGB](/hex-to-rgb) tool is the fastest way to convert a design-system color into the rgba() format box-shadow needs.

## Why one shadow is not enough

A single box-shadow can represent one part of a real shadow. Real shadows are not single shapes; they are layered. A hand resting on a desk casts a sharp shadow right under it (where the hand touches the desk) and a soft, larger shadow that fades off into the room. The two parts have different characteristics: the contact shadow is tight and dark; the ambient shadow is soft and faint.

To replicate this in CSS, you stack two box-shadows on the same element. The first one (in the CSS) is the contact shadow: small offset, small blur, darker. The second one is the ambient shadow: larger offset, larger blur, lighter. The browser composites them into a single, more believable shadow.

```
box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.06),
    0 4px 12px rgba(0, 0, 0, 0.10);
```

This is the standard "subtle UI shadow" pattern. The first shadow is the tight contact shadow (1 pixel offset, 2 pixel blur, 6% black). The second is the ambient shadow (4 pixel offset, 12 pixel blur, 10% black). Both are subtle; both are visible; together they look like a real shadow.

Compare with the single-shadow version:

```
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
```

This is darker, harder, and less believable. It works in a pinch, but it does not have the depth of the two-shadow version. The two-shadow version is the difference between "this design has depth" and "this design has a drop shadow."

## The standard values

Four pairs of values cover almost every shadow in a typical UI design. Memorise these and you can produce a good shadow without thinking.

**Subtle.** For raised elements that should look like they are slightly above the surface (cards on a page, list items):

```
box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 2px 6px rgba(0, 0, 0, 0.08);
```

**Medium.** For floating elements that need to feel distinct from the background (modals, popovers, dropdowns):

```
box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.06),
    0 8px 16px rgba(0, 0, 0, 0.12);
```

**Heavy.** For elements that are clearly hovering above the page (tooltips, menus on hover):

```
box-shadow:
    0 4px 8px rgba(0, 0, 0, 0.08),
    0 16px 32px rgba(0, 0, 0, 0.16);
```

**Inset.** For pressed elements (input fields, pressed buttons):

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

The [Uttir CSS Gradient Generator](/css-gradient-generator) follows the same two-layer pattern for gradients (one tight stop, one soft stop) and produces output that drops straight into a stylesheet.

## The lighting direction

The shadow direction has to match the rest of the design's lighting. If your design has a top-left light source, the shadow falls to the bottom-right, with positive X and Y offset. If your design has a top-right light source, the shadow falls to the bottom-left, with negative X offset.

Most UIs use a top-down or top-left light source by default. The values above are for top-down (offset 0 in X, positive in Y). For top-left, use offset (2, 4) instead of (0, 4).

Mixing shadow directions in the same design is a visual bug. If one card has its shadow falling right and another has its shadow falling left, the design looks broken even if the shadows are technically correct. Pick a light direction and apply it consistently across every shadow in the design.

## Color considerations

The shadow color is almost always a desaturated dark color with low alpha. Pure black (`#000`) at 20% alpha is the most common choice; a slightly warm dark (`rgba(20, 20, 30, 0.15)`) is the second most common. A colored shadow (a blue or red tint) is a stylistic choice that can work in some designs but reads as "unusual" in most.

The [Uttir Color Picker](/color-picker) is the right tool to dial in the exact shadow color. Pick the hue, adjust the alpha, and copy the rgba() value. The [Hex to RGB](/hex-to-rgb) tool is faster if you already have the hex and just need the rgba() form.

One subtlety: the shadow color should be derived from the background, not the element. A pure white element on a light grey background needs a darker shadow than a white element on a dark background, because the contrast is different. Tune the shadow for the context, not in isolation.

## Performance

Box shadows are cheap to render in modern browsers, but not free. A page with hundreds of elements all casting box shadows will see a small performance hit, especially on lower-end devices. The blur radius is the most expensive part: a 32-pixel blur on a large element is more expensive than a 4-pixel blur.

A few practical limits:

- Avoid box shadows on elements that animate frequently (a shadow that re-renders 60 times per second is expensive).

- Keep blur radius under 32 pixels for most use cases; larger blurs rarely look better and always cost more.

- Use `will-change: box-shadow` sparingly, only on elements where the shadow actually animates. The hint reserves a separate compositor layer for the element, which has its own cost.

For most designs, the performance is a non-issue. The values in this post are small enough that even a page with 50 shadowed elements renders fine. The performance limits matter when you are doing something unusual (a 3D visualisation with hundreds of cards, each with a complex shadow).

The [CSS Flexbox Playground](/css-flexbox-playground) and [CSS Grid Playground](/css-grid-playground) are good companions when you are iterating on a design that has shadows — the playgrounds let you see the layout, the shadow, and the spacing all at once. The [CSS Minifier](/css-minifier) is the right tool once you have the final values and want to ship the smallest possible file.

## Related tools

- [CSS Gradient Generator](https://uttir.com/css-gradient-generator) — Build linear and radial CSS gradients visually. Pick colors, drag stops, copy the CSS.
- [CSS Minifier](https://uttir.com/css-minifier) — Shrink CSS by stripping comments, collapsing whitespace, and tightening punctuation. See byte savings live.
- [Color Picker](https://uttir.com/color-picker) — Pick any color and copy it as HEX, RGB, or HSL.
- [HEX to RGB](https://uttir.com/hex-to-rgb) — Convert any HEX color code to its RGB and HSL equivalents.
- [CSS Flexbox Playground](https://uttir.com/css-flexbox-playground) — Visual playground for CSS Flexbox. Adjust container and item properties with sliders, see the live layout update, and copy the generated CSS.
- [CSS Grid Playground](https://uttir.com/css-grid-playground) — Visual playground for CSS Grid. Define rows, columns, and gaps, place items by area or line number, and copy the generated CSS.

---

For the full HTML article, visit https://uttir.com/blog/how-to-build-a-perfect-css-box-shadow-in-5-minutes.
This file is the markdown rendering at https://uttir.com/blog/how-to-build-a-perfect-css-box-shadow-in-5-minutes.md. See https://uttir.com/llms.txt for a site-wide summary.
