← All guides
CSS · 5 min read · 24 November 2025

Smooth CSS shadows: the multi-layer technique

A single box-shadow looks hard-edged and flat. The multi-layer technique — stacking several shadows at increasing blur and offset — produces the smooth, atmospheric depth that feels natural.

Advertisement uicorn — fabled tools for designers and art directors uicorn — fabled tools for designers and art directors

Why single-layer shadows look wrong

Natural shadows are not uniform blobs of darkness. As distance from the caster increases, a shadow diffuses — it becomes less opaque at the edges and more opaque close to the object. A single box-shadow cannot model this behaviour: it has one blur radius and one opacity, producing a shadow that looks the same at every distance from the edge.

The fix is to stack multiple shadows, each representing a different zone of diffusion.

The multi-layer formula

A well-designed multi-layer shadow uses three to five layers. Each subsequent layer is:

  • Larger in offset (further from the object)
  • Larger in blur radius (more diffused)
  • Lower in opacity (lighter)
/* A three-layer example */
box-shadow:
  0 1px 2px   rgba(24, 23, 22, 0.10),
  0 4px 8px   rgba(24, 23, 22, 0.07),
  0 12px 24px rgba(24, 23, 22, 0.05);

The combined effect reads as a single, smooth shadow. Each individual layer is subtle enough that the stacking is invisible — only the result is perceived.

Three shadow layers shown individually and combined
Fig 1 — Each layer covers a different diffusion zone. Together they read as one smooth, physically plausible shadow — no single layer is ever visible on its own.

Color tinting with OKLCH

Real shadows are not grey. In natural light, shadows pick up the complementary color of the light source — a warm light casts a cool shadow. On a cream background, the shadow will have a slight warm-neutral tone.

Using OKLCH lets you specify shadow color as a tinted dark:

/* Shadow tinted to match warm background */
box-shadow:
  0 1px 3px   oklch(20% 0.04 60 / 0.12),
  0 4px 12px  oklch(20% 0.04 60 / 0.08),
  0 16px 32px oklch(20% 0.04 60 / 0.05);

The OKLCH values here say: very dark (L=20%), slightly warm (C=0.04, H=60 is amber direction), at varying opacity. The result is a shadow that feels physically accurate for a warm-background design system.

Elevation tokens

In a design system, shadows are not one-off decisions — they represent elevation levels. A floating button is at a higher elevation than a card, which is at a higher elevation than a flat list item.

Define four or five elevation tokens:

:root {
  --shadow-sm:  0 1px 2px rgba(24,23,22,.08);
  --shadow-md:  0 1px 3px rgba(24,23,22,.08), 0 4px 12px rgba(24,23,22,.06);
  --shadow-lg:  0 1px 4px rgba(24,23,22,.08), 0 6px 16px rgba(24,23,22,.06), 0 20px 40px rgba(24,23,22,.05);
  --shadow-xl:  /* modal level */;
  --shadow-2xl: /* tooltip/popover level */;
}

Components reference tokens, not raw values. When you change the design system’s shadow style, every component updates.

Hover elevation

Interactive elements often move to a higher elevation on hover. A card at --shadow-md might rise to --shadow-lg on hover. This reads as the card lifting toward the user — a physically intuitive interaction signal.

.card {
  box-shadow: var(--shadow-md);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.card:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-2px);
}

The 2px translateY reinforces the elevation change visually. Keep transform values small — the shadow is doing most of the work.

Using the Shadow Studio

The tool works backwards from a result. Set the shadow angle, the maximum offset, and the color tint — and it generates the full multi-layer stack that produces that result. You do not need to calculate individual layers.

The elevation token export is the most useful output for design system work: five named levels (sm through 2xl), each a multi-layer stack, ready to drop into CSS custom properties or Tailwind’s boxShadow config. Components reference tokens, not raw values. When you change the shadow language system-wide, one edit updates everything.

The hover pair export gives you the resting shadow and its elevated counterpart together, including the translateY value that reinforces the lift visually.


Disclaimer The tools on uicorn.com are provided for informational and design-assistance purposes only. All outputs are generated algorithmically and are provided without warranty of any kind, express or implied — including without limitation any warranty of accuracy, completeness, or fitness for a particular purpose. uicorn and its operator accept no liability for any errors, inaccuracies, or any direct, indirect, or consequential loss or damage arising from the use of, or reliance on, any tool or output on this site. You are solely responsible for verifying all values, measurements, and specifications before use in any professional, commercial, or production context. Use of these tools and reliance on their output is entirely at your own risk.

Generate a full elevation system with the Shadow & Elevation Studio — export CSS custom properties or Tailwind config.

Try it yourself

Shadow & Elevation Studio

Open Shadow & Elevation Studio →