Shadow Composer

Real-world shadows are rarely a single blur. Designers at companies like Material Design and Tailwind stack multiple box-shadows to create convincing depth — a tight, dark shadow for the edge contact, and a softer, wider shadow for the ambient light. This composer lets you layer as many shadows as you need, each with its own offset, blur, spread, and color. Perfect for designing elevation systems.

What is box-shadow layering?

The CSS box-shadow property accepts a comma-separated list of shadow definitions. Each shadow layer has its own x-offset, y-offset, blur radius, spread radius, and color. By stacking multiple layers — for instance, a sharp close shadow combined with a soft distant shadow — you can simulate realistic lighting that a single shadow can't achieve. This technique is used by design systems like Material Design's elevation scale and Tailwind's shadow utilities.

In the physical world, shadows are never a single uniform blur. An object sitting on a surface has a tight, dark "contact shadow" right at its base, plus a softer, broader "ambient shadow" cast by the surrounding light. Replicating this in CSS requires layering: the first shadow provides the sharp edge contact, and additional layers add the softer, more diffused glow. Some advanced designs use three or more layers, adding subtle colored tints or inset highlights to simulate light reflecting off nearby surfaces.

The order of shadows in the comma-separated list matters. The first shadow in the list is rendered on top (closest to the viewer), and each subsequent shadow is painted behind the previous one. This stacking order lets you combine a small, dark, sharp shadow with a large, light, diffused shadow without the two interfering. Understanding this rendering order is key to composing convincing depth effects.

How to use this tool

Each layer card in the controls panel has sliders for X offset (horizontal displacement), Y offset (vertical displacement), blur radius (softness), and spread radius (expansion or contraction). Click the color input to choose a shadow color — use an RGBA or HSLA color with transparency for more realistic results, since real-world shadows are never fully opaque. Toggle the "Inset" checkbox to create inner shadows that appear inside the element's boundaries.

Add new layers with the "+ Add Shadow Layer" button at the bottom, or remove layers with the x button on each card. The white preview box updates instantly as you adjust values, so you can fine-tune the visual result without switching between code and browser. When you are satisfied, copy the Direct CSS output for a ready-to-paste box-shadow declaration, or use the CSS Variables format to integrate the shadow values into your design token system.

Practical examples

Material Design elevation scale (levels 1-5)

Material Design defines elevation levels using layered shadows that increase in offset, blur, and transparency as the element rises higher. Here is a simplified version of levels 1 through 5 that you can use as a starting point for your own elevation system.

/* Elevation 1 - Cards at rest */
.elevation-1 {
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.12),
    0 1px 2px rgba(0, 0, 0, 0.24);
}

/* Elevation 2 - Raised cards */
.elevation-2 {
  box-shadow:
    0 3px 6px rgba(0, 0, 0, 0.15),
    0 2px 4px rgba(0, 0, 0, 0.12);
}

/* Elevation 3 - Floating action buttons */
.elevation-3 {
  box-shadow:
    0 10px 20px rgba(0, 0, 0, 0.15),
    0 3px 6px rgba(0, 0, 0, 0.10);
}

/* Elevation 4 - Navigation bars, dialogs */
.elevation-4 {
  box-shadow:
    0 14px 28px rgba(0, 0, 0, 0.18),
    0 5px 10px rgba(0, 0, 0, 0.10);
}

/* Elevation 5 - Modals, popovers */
.elevation-5 {
  box-shadow:
    0 19px 38px rgba(0, 0, 0, 0.20),
    0 7px 15px rgba(0, 0, 0, 0.10);
}

Neumorphic (soft UI) effect with inset shadows

Neumorphism creates a soft, extruded look by combining a light shadow on one side with a dark shadow on the opposite side. Both the background color and the shadow colors are derived from the same base hue, creating the illusion that the element is pressed into or raised out of the surface.

.neumorphic {
  background: #e0e5ec;
  border-radius: 16px;
  box-shadow:
    8px 8px 16px #b8bec7,
    -8px -8px 16px #ffffff;
}

/* Pressed / inset variant */
.neumorphic-inset {
  background: #e0e5ec;
  border-radius: 16px;
  box-shadow:
    inset 4px 4px 8px #b8bec7,
    inset -4px -4px 8px #ffffff;
}

Colored glow shadow for cards

A colored glow uses a large blur radius with a saturated, semi-transparent color to create a halo effect around a card. This technique works well for featured items, call-to-action sections, or interactive card hover states.

.glow-card {
  background: #fff;
  border-radius: 12px;
  box-shadow:
    0 4px 6px rgba(0, 0, 0, 0.07),
    0 0 40px rgba(99, 102, 241, 0.15);
  transition: box-shadow 0.3s ease;
}

.glow-card:hover {
  box-shadow:
    0 8px 16px rgba(0, 0, 0, 0.10),
    0 0 60px rgba(99, 102, 241, 0.30);
}

Common patterns and best practices

Shadows are one of the most important visual cues for conveying hierarchy and depth in a user interface. Following these best practices will help you create shadows that look natural and perform well.

Browser support

The box-shadow property with multiple comma-separated layers is supported in all modern browsers and has been since IE9. There are no compatibility concerns for current development. The inset keyword has the same level of support. No vendor prefixes are needed for any current browser.

For more advanced use cases, the filter: drop-shadow() function is supported in all modern browsers since 2016 and provides an alternative that follows the element's alpha channel rather than its bounding box. The upcoming CSS box-shadow Level 2 specification may introduce additional features like spread radius for individual sides, but this is not yet implemented in any browser.

FAQ

How many shadows can I layer?

There is no hard limit on the number of box-shadow layers in CSS. Browsers handle dozens of layers without visible performance impact. However, for practical design purposes, 2-4 layers usually produce the best results — one for the contact shadow, one for the ambient shadow, and optionally one or two more for highlights or inset effects.

What's the difference between blur and spread?

Blur radius controls how soft or sharp the shadow edge is — a blur of 0 produces a hard-edged shadow, while higher values create a smoother gradient. Spread radius expands or contracts the shadow before blurring: positive values make the shadow larger than the element, negative values make it smaller, which is useful for creating tight, focused shadows.

What's the difference between box-shadow and filter: drop-shadow()?

box-shadow always applies to the element's rectangular box shape, including the border-radius but ignoring any transparency within the element. filter: drop-shadow(), on the other hand, follows the element's alpha channel. This means it will outline the visible shape of transparent PNGs, SVGs, and elements with complex clip-paths. Use drop-shadow() when you need the shadow to match a non-rectangular shape, and box-shadow when you need features like inset, spread, or multiple layers (drop-shadow does not support any of these).

Do shadows affect page performance?

Shadows are painted by the browser's rendering engine and can trigger repaints when their values change. A few static shadows have negligible impact, but multiple shadows with large blur radii on many elements can add up, especially during scrolling or animation. For animated shadows, a common optimization technique is to apply the shadow to an ::after pseudo-element with its full target values, then animate only the pseudo-element's opacity. This way, the browser only composites a layer change rather than recalculating and repainting the shadow on every frame.

Can I use CSS variables inside box-shadow?

Yes, CSS custom properties work for individual shadow values like offset, blur, spread, and color. This makes it straightforward to create theme-based elevation systems where switching from light mode to dark mode only requires updating the shadow color variables. For example, you can define --shadow-color: rgba(0, 0, 0, 0.1) and reference it in your box-shadow declaration, then override it to rgba(0, 0, 0, 0.4) in a dark theme context.

Related tools