Skip to content
Boomspot
  • Home
Loading...
Boomspot

Daily tech news, software development coverage, Apple reporting, and the gear behind modern music making.

TwitterLinkedIn

Browse

  • Categories
  • Tags
  • Authors

Company

  • About
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
  • Unsubscribe

© 2026 Boomspot. All rights reserved.

Built by Boomspot
Updated hourly

AI Content Disclosure: Articles on Boomspot are researched, written, and edited with the assistance of advanced AI systems. We combine software-assisted research with editorial oversight to deliver useful, accurate, and practical technical and music production content. Learn more about our editorial approach.

Browse by Category

Technology673Coding181Linux57SEO55Music Production37Studio Gear36Apple Rumors11

Popular Posts

Raspberry Pi 5 Alternatives After the $77.50 Price Hike

Raspberry Pi 5 Alternatives After the $77.50 Price Hike

6 min read
GamesSoundCon 2026: Worth It for Composers?

GamesSoundCon 2026: Worth It for Composers?

6 min read
ROCm or Vulkan for llama.cpp on AMD GPUs? How to Decide

ROCm or Vulkan for llama.cpp on AMD GPUs? How to Decide

8 min read
Which Ethernet Cable Category Do You Actually Need?

Which Ethernet Cable Category Do You Actually Need?

6 min read
How to Prepare Shorts for Ask YouTube: A Creator Audit

How to Prepare Shorts for Ask YouTube: A Creator Audit

6 min read

Recent Posts

Rivian Ghostbusters Mode: How to Activate It by Nov. 2

Rivian Ghostbusters Mode: How to Activate It by Nov. 2

Oct 31, 2026•7 min
Noindex vs Robots.txt for Shared Pages: How to Audit

Noindex vs Robots.txt for Shared Pages: How to Audit

Oct 31, 2026•7 min
How to Test an AI Agent: A 4-Task Scorecard Before You Pay

How to Test an AI Agent: A 4-Task Scorecard Before You Pay

Oct 31, 2026•8 min
Flip or Fold Phone? How to Pick by Your Daily Routine

Flip or Fold Phone? How to Pick by Your Daily Routine

Oct 31, 2026•7 min
How to Audit Content for ChatGPT Information Seeking

How to Audit Content for ChatGPT Information Seeking

Oct 31, 2026•9 min
  1. Home
  2. Coding
  3. How to Make Layered Box-Shadow CSS (Plus Glass Cards)
coding10 min read

How to Make Layered Box-Shadow CSS (Plus Glass Cards)

One box-shadow looks flat because real light does not fall off in a single curve. Stack five shadows with doubling offsets, add a glass card, and check both in DevTools.

S

Staff

October 31, 2026

Reviewed byDorian

How to Make Layered Box-Shadow CSS (Plus Glass Cards)

A single box-shadow: 0 4px 12px rgba(0,0,0,0.3) looks like a gray smudge glued under a card. Real objects do not cast shadows that way, and your eye knows it. The fix is not a better blur value. The fix is more than one shadow.

This guide explains the mechanism, gives you copy-paste values, and ends with a glassmorphism card you can verify in DevTools. You can write all of it by hand, with no generator.

Why one box-shadow looks flat

A real light source produces a tight, dark shadow where the object nearly touches the surface, plus a wide, faint shadow that fades into the distance. Those are two different falloffs overlapping. A single box-shadow has one blur radius, so it draws one smooth falloff. It cannot be tight and wide at the same time.

Pick a small blur and the shadow looks hard and sticker-like. Pick a large blur and the card looks like it floats in fog with no contact point. Raising opacity to compensate only darkens the smudge.

Stacking shadows solves this because the browser composites each comma-separated layer on top of the next. Short, darker layers define the edge. Long, lighter layers build the ambient glow. Together they approximate a natural falloff that no single blur can produce.

Step 1: Stack five layers with doubling offset and blur

Use this rule of thumb: each layer doubles the previous layer's vertical offset and blur, and each layer is a little less opaque.

Start with a 1px offset and 1px blur. The next layers use 2px, 4px, 8px, and 16px. Opacity falls across the stack, for example 0.10, 0.09, 0.08, 0.06, 0.04. Four to six layers works well, and five keeps the declaration readable.

Two details matter. First, keep the horizontal offset at 0 so the light appears to come from straight above. Second, the first shadow in the list paints on top, so put the tight layer first to keep the contact edge crisp.

The doubling pattern and the opacity numbers are a design heuristic, not a CSS rule. Treat them as starting values and adjust by eye on your own backgrounds.

Step 2: Pick values for small, medium, and large elevations

Elevation is the same recipe scaled up. Buttons sit low, cards sit in the middle, and modals float high. Each column below is five layers, written as y-offset and blur, then alpha. Every layer uses an x-offset of 0 and a black base color.

| Layer | Small (buttons) | Medium (cards) | Large (modals) | | --- | --- | --- | --- | | 1 | 1px 1px / 0.10 | 2px 2px / 0.10 | 4px 4px / 0.08 | | 2 | 2px 2px / 0.09 | 4px 4px / 0.09 | 8px 8px / 0.07 | | 3 | 4px 4px / 0.08 | 8px 8px / 0.08 | 16px 16px / 0.06 | | 4 | 8px 8px / 0.06 | 16px 16px / 0.06 | 32px 32px / 0.05 | | 5 | 16px 16px / 0.04 | 32px 32px / 0.04 | 64px 64px / 0.04 |

These values are illustrative starting points, not measured standards. On dark backgrounds, shadows nearly vanish. Raise the alpha, or lean on a border or a lighter surface color instead.

Step 3: Wrap the values in custom properties

Paste the table into variables once, then reuse the tokens everywhere. This is the reusable snippet:

:root {
  --shadow-color: 0 0 0;

  --shadow-sm:
    0 1px 1px rgb(var(--shadow-color) / 0.10),
    0 2px 2px rgb(var(--shadow-color) / 0.09),
    0 4px 4px rgb(var(--shadow-color) / 0.08),
    0 8px 8px rgb(var(--shadow-color) / 0.06),
    0 16px 16px rgb(var(--shadow-color) / 0.04); We cover related ground in [how to move from quickstart to production (checklist)](/how-to-move-from-quickstart-to-production-checklist).

  --shadow-md:
    0 2px 2px rgb(var(--shadow-color) / 0.10),
    0 4px 4px rgb(var(--shadow-color) / 0.09),
    0 8px 8px rgb(var(--shadow-color) / 0.08),
    0 16px 16px rgb(var(--shadow-color) / 0.06),
    0 32px 32px rgb(var(--shadow-color) / 0.04);

  --shadow-lg:
    0 4px 4px rgb(var(--shadow-color) / 0.08),
    0 8px 8px rgb(var(--shadow-color) / 0.07),
    0 16px 16px rgb(var(--shadow-color) / 0.06),
    0 32px 32px rgb(var(--shadow-color) / 0.05),
    0 64px 64px rgb(var(--shadow-color) / 0.04);
}

.button { box-shadow: var(--shadow-sm); }
.card   { box-shadow: var(--shadow-md); }
.modal  { box-shadow: var(--shadow-lg); }

One gotcha: var(--shadow-color) resolves where the token is defined. If you override --shadow-color on a single card, the --shadow-md it inherits from :root will not change. Redefine the whole token on that element, or define the tokens on a shared parent.

Step 4: Add a glassmorphism card on top

Glassmorphism needs three ingredients: a translucent background, backdrop-filter to blur whatever sits behind the element, and a subtle light border. The layered shadow still works here, because the browser does not paint outer box shadows inside the element's own box. The shadow therefore does not muddy the glass.

Complete minimal example (unexecuted)

This example has no dependencies. It is unexecuted: nobody has run it in a browser, so check the output yourself with the steps below. This pairs well with s3 gateway endpoint vs interface endpoint: which to pick in depth.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Layered shadows and glass</title>
<style>
  :root {
    --shadow-color: 0 0 0;
    --shadow-md:
      0 2px 2px rgb(var(--shadow-color) / 0.10),
      0 4px 4px rgb(var(--shadow-color) / 0.09),
      0 8px 8px rgb(var(--shadow-color) / 0.08),
      0 16px 16px rgb(var(--shadow-color) / 0.06),
      0 32px 32px rgb(var(--shadow-color) / 0.04);
    --shadow-lg:
      0 4px 4px rgb(var(--shadow-color) / 0.08),
      0 8px 8px rgb(var(--shadow-color) / 0.07),
      0 16px 16px rgb(var(--shadow-color) / 0.06),
      0 32px 32px rgb(var(--shadow-color) / 0.05),
      0 64px 64px rgb(var(--shadow-color) / 0.04);
  }
  body {
    margin: 0; padding: 48px 24px; display: grid; gap: 48px;
    justify-items: center; background: #eef0f4; color: #1b1f2a;
    font-family: system-ui, sans-serif;
  }
  .card {
    width: 280px; padding: 24px; border-radius: 16px;
    background: #fff; box-shadow: var(--shadow-md);
  }
  .scene {
    position: relative; padding: 64px; border-radius: 24px; overflow: hidden;
    background: linear-gradient(135deg, #4f46e5, #ec4899);
  }
  .scene::before {
    content: ""; position: absolute; top: 24px; left: 60px;
    width: 140px; height: 140px; border-radius: 50%; background: #fde047;
  }
  .glass {
    position: relative; color: #fff;
    background: rgb(255 255 255 / 0.18);
    border: 1px solid rgb(255 255 255 / 0.35);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    backdrop-filter: blur(14px) saturate(140%);
    box-shadow: var(--shadow-lg);
  }
</style>
</head>
<body>
  <article class="card"><h2>Layered shadow</h2><p>Five stacked layers.</p></article>
  <div class="scene">
    <article class="card glass"><h2>Glass card</h2><p>Blurred backdrop.</p></article>
  </div>
</body>
</html>

Expected visual result

The white card should sit on the light gray page with a soft, even shadow: a faint dark edge hugging the bottom and a wide haze beneath it, with no visible band where one layer ends. In the gradient scene, the glass card should look like frosted glass. The yellow circle behind it should appear as a blurry glow through the card's left portion, while the circle's sharp edge stays visible outside the card.

Browser support notes

The layered box-shadow and rgb(r g b / alpha) syntax work in current evergreen browsers. For backdrop-filter, older Safari versions needed the -webkit- prefix, which is why the example declares both. Support details change, so check a current compatibility table such as caniuse.com for the browsers you must support. Always provide a readable fallback: a translucent background that stays legible even if the blur never applies.

Step 5: Verify both effects in DevTools

Do not trust your eyes alone. Open the page, right-click the white card, and choose Inspect. Then work through this checklist.

  1. In the Styles pane, find box-shadow: var(--shadow-md). Hover over the variable name, or open the Computed tab and search for "box-shadow". You should see five comma-separated layers matching the values in your table.
  2. Untick the box-shadow declaration, then tick it again. The card should flip between flat and elevated. Many browsers also offer a shadow editor swatch beside the value for live tweaking.
  3. Select the glass card and find backdrop-filter. Untick it, and the yellow circle should sharpen behind the card. Tick it again and the blur returns.
  4. If a declaration shows a strikethrough or warning icon, the browser rejected the value. Look for a typo, a missing unit, or a stray semicolon.

When backdrop-filter shows nothing

The typical symptom: the card looks like plain translucent white, with no blur. Toggling the declaration in step 3 above makes no visible difference. Work through these causes in order.

Also read: see google ads app campaign for indie games: avoid junk installs

  1. Nothing is behind the element to blur. Blurring a flat color gives the same flat color. Put a gradient, image, or shape underneath, as the example does.
  2. The background is opaque. A fully opaque background hides everything the filter would blur. Use an alpha value, such as rgb(255 255 255 / 0.18).
  3. The blur is too small or zero. Try blur(14px) to confirm the effect, then tune down.
  4. The declaration is invalid. Check the Styles pane for a struck-through line. Also confirm you wrote backdrop-filter: plain filter blurs the card's own content, not what sits behind it.
  5. An ancestor limits what the filter can see. A parent with its own filter, opacity below 1, a mask, or another backdrop-filter can change what counts as the backdrop. Remove those from the ancestors temporarily and retest. This is an inference from how stacking and compositing generally behave, so confirm it in your own DevTools.
  6. The browser needs the prefix. In older Safari, add -webkit-backdrop-filter.

Turning it into a Tailwind utility

Because the shadows live in CSS variables, Tailwind can use them without rewriting values. An arbitrary property works in any setup that supports arbitrary values:

<div class="[box-shadow:var(--shadow-md)] rounded-2xl bg-white p-6">...</div>

For a named utility, register the value under theme.extend.boxShadow in your Tailwind config, for example soft: 'var(--shadow-md)', then use shadow-soft. Newer Tailwind releases move theme configuration into CSS. Check the documentation for your installed version rather than copying config shapes from older tutorials.

Optional shortcut: generators

If tweaking five layers by hand feels slow, a generator can speed up experimentation. One example is CSSForge, a free, client-side tool built by the author of a dev.to post. According to that post, it previews shadows on dark, light, gradient, and image backgrounds, and exports pure CSS, CSS variables, or Tailwind CSS. The post also says it needs no registration and includes a glassmorphism generator. The tool lives at cssforge-cyan.vercel.app.

Those claims come only from the author's own post and have not been independently tested. Even with a generator, the DevTools checks above still apply, because only the browser tells you what actually renders.

Where to go next

Once the tokens are in place, the next gains come from tuning rather than new syntax. Tint --shadow-color toward your background hue. Add a hover state that moves a card from medium to large elevation. Test the glass card over real photography, since the background changes how strong a shadow reads.

Tags

Web DevelopmentUser ExperienceCoding TutorialsCoding Best PracticesUI Design

Keep reading

Unlocking the Potential of Corner-Shape in CSS
Coding•3 min read

Unlocking the Potential of Corner-Shape in CSS

Explore how corner-shape in CSS transforms web design, enhancing aesthetics and user experience with unique shapes.

Sep 20, 2025

Revolutionize Your UI: The Radio Button Shopping Cart Trick
Coding•3 min read

Revolutionize Your UI: The Radio Button Shopping Cart Trick

Elevate your e-commerce experience with the Radio Button Shopping Cart Trick, offering seamless animations and unlimited scalability.

Sep 10, 2025

WebAssembly: Unleashing Native Speed in Web Browsers
Coding•4 min read

WebAssembly: Unleashing Native Speed in Web Browsers

WebAssembly is transforming web development with near-native performance, enabling more complex and efficient applications.

Sep 6, 2025

More stories for your next project

Get tech, coding, and music production updates in your inbox.

Unsubscribe anytime.