CSS Box Shadow Examples & Best Practices

Summary: The CSS box-shadow property adds depth, elevation, and visual hierarchy to any element on the page. This guide covers the full syntax, 15+ copy-paste examples โ€” from subtle cards to neon glow and neumorphism โ€” plus dark mode strategies, Tailwind CSS integration, performance tips, and common mistakes to avoid.

๐Ÿ“– 18 min readยทUpdated March 2026

1. Box-Shadow Syntax Breakdown

The box-shadow property accepts one or more shadow definitions, each with up to six values. Understanding every value gives you precise control over depth and mood.

/* Full syntax */

box-shadow: [inset] <offset-x> <offset-y> [blur-radius] [spread-radius] <color>;

/* Examples */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);           /* basic drop shadow */
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);      /* inset shadow */
box-shadow: 0 4px 6px -2px rgba(0, 0, 0, 0.1);       /* negative spread */
box-shadow: 0 0 0 3px #3B82F6;                        /* ring / outline */
box-shadow:
  0 1px 2px rgba(0,0,0,0.06),
  0 4px 8px rgba(0,0,0,0.1);                          /* multiple shadows */

Here's what each value controls:

  • inset โ€” Optional keyword. Moves the shadow inside the element instead of outside.
  • offset-x โ€” Horizontal displacement. Positive moves right, negative moves left.
  • offset-y โ€” Vertical displacement. Positive moves down, negative moves up.
  • blur-radius โ€” Optional (default 0). Higher values create softer, more diffused shadows. Cannot be negative.
  • spread-radius โ€” Optional (default 0). Positive expands the shadow, negative shrinks it. Crucial for one-sided shadows.
  • color โ€” Shadow color. Use rgba() or hsla() for semi-transparent shadows that look natural against any background.

The order matters when you stack multiple shadows โ€” the first shadow in the list renders on top. This is the opposite of how CSS z-index works, and it trips up beginners regularly. You can experiment with all of these values interactively using Hue's shadow generator tool.

The full syntax is documented on MDN's box-shadow reference, which also covers edge cases around inheritance and computed values.

2. Subtle & Soft Shadows

The best shadows are the ones you barely notice. Subtle shadows create depth without drawing attention to themselves. They're the workhorses of modern UI design โ€” used on every card, dropdown, and modal.

/* Barely there โ€” perfect for flat UI elements */
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);

/* Soft ambient shadow */
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08),
            0 1px 2px rgba(0, 0, 0, 0.06);

/* Medium elevation โ€” modals, popovers */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.07),
            0 2px 4px -1px rgba(0, 0, 0, 0.04);

/* Diffused cloud shadow โ€” high blur, very low opacity */
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.06);

/* Ultra-soft for large hero cards */
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15);

The trick to natural-looking shadows is using very low opacity (0.04โ€“0.12) and generous blur radii. Real-world shadows are diffused and subtle โ€” they don't have hard edges or high opacity. The second shadow in the two-shadow stack handles the close ambient occlusion, while the first handles the broader diffused shadow. This mimics how light actually wraps around objects.

Material Design popularised this two-shadow approach in their elevation system, and it's now the standard approach across virtually all design systems. When building your own shadow scale, define 5-6 levels from "flat" to "floating" and use them consistently.

3. Card Shadows

Cards are the most common UI pattern that relies on box-shadow for visual separation. The shadow defines the card's perceived elevation above the page surface. Here are battle-tested card shadow recipes.

/* Flat card with subtle lift */
.card-sm {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08),
              0 1px 2px rgba(0, 0, 0, 0.06);
}

/* Standard elevated card */
.card-md {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.08),
              0 2px 4px -1px rgba(0, 0, 0, 0.04);
}

/* Floating card (dropdowns, popovers) */
.card-lg {
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08),
              0 4px 6px -2px rgba(0, 0, 0, 0.04);
}

/* Hero spotlight card */
.card-xl {
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.08),
              0 10px 10px -5px rgba(0, 0, 0, 0.03);
}

/* Card with hover lift effect */
.card-hover {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.card-hover:hover {
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08),
              0 4px 6px -2px rgba(0, 0, 0, 0.04);
  transform: translateY(-2px);
}

The hover lift pattern is especially effective โ€” combining transform: translateY(-2px) with an increased shadow sells the illusion that the card is physically rising off the page. Keep the transition snappy (200-300ms) and use ease or ease-out timing for a natural feel. The shadow increase should be proportional to the transform distance โ€” if the card moves up 2px, the shadow should grow by roughly 8-12px of blur.

4. Button & Hover Effects

Shadows on buttons communicate interactivity. A resting shadow implies the button is pressable; removing it on active/pressed states simulates physical depression.

/* Elevated button with press effect */
.btn {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15),
              0 1px 2px rgba(0, 0, 0, 0.1);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.btn:hover {
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15),
              0 2px 4px rgba(0, 0, 0, 0.1);
  transform: translateY(-1px);
}
.btn:active {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
  transform: translateY(0);
}

/* Focus ring using box-shadow (accessible) */
.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5),
              0 2px 4px rgba(0, 0, 0, 0.15);
}

/* Soft glow button hover */
.btn-glow:hover {
  box-shadow: 0 0 20px rgba(245, 158, 11, 0.3),
              0 0 6px rgba(245, 158, 11, 0.2);
}

The focus ring using box-shadow is a widely-used pattern because it respects border-radius (unlike outline in older browsers) and can be combined with existing shadows. The :focus-visible pseudo-class ensures the ring only appears for keyboard navigation, not mouse clicks. Always check that your focus ring colour passes contrast requirements โ€” Hue's contrast checker can verify this.

5. Layered (Stacked) Shadows

Layered shadows create the most realistic depth effects. Instead of one heavy shadow, you stack 3-5 progressively larger shadows with decreasing opacity. This mimics how real-world light creates multiple penumbra zones around objects.

/* Smooth layered shadow (4 layers) */
.layered-shadow {
  box-shadow:
    0 1px 1px rgba(0,0,0,0.08),
    0 2px 2px rgba(0,0,0,0.06),
    0 4px 4px rgba(0,0,0,0.04),
    0 8px 8px rgba(0,0,0,0.02);
}

/* Deep layered shadow (5 layers) */
.deep-shadow {
  box-shadow:
    0 1px 1px rgba(0,0,0,0.11),
    0 2px 2px rgba(0,0,0,0.09),
    0 4px 4px rgba(0,0,0,0.07),
    0 8px 8px rgba(0,0,0,0.05),
    0 16px 16px rgba(0,0,0,0.03);
}

/* Dreamy elevated shadow */
.dreamy-shadow {
  box-shadow:
    0 2.8px 2.2px rgba(0,0,0,0.02),
    0 6.7px 5.3px rgba(0,0,0,0.028),
    0 12.5px 10px rgba(0,0,0,0.035),
    0 22.3px 17.9px rgba(0,0,0,0.042),
    0 41.8px 33.4px rgba(0,0,0,0.05),
    0 100px 80px rgba(0,0,0,0.07);
}

The "dreamy shadow" pattern uses non-round numbers because they're generated from a mathematical progression that distributes blur more naturally. CSS-Tricks has explored several approaches to layered shadow generation. The general formula: double the offset and blur for each successive layer while halving the opacity.

6. One-Sided Shadows

Sometimes you want a shadow on only one side of an element โ€” a bottom shadow on a sticky header, a right shadow on a sidebar, or a top shadow on a bottom sheet. The spread-radius is the key.

/* Bottom-only shadow (sticky headers) */
box-shadow: 0 4px 6px -4px rgba(0, 0, 0, 0.2);

/* Top-only shadow (bottom sheets, footers) */
box-shadow: 0 -4px 6px -4px rgba(0, 0, 0, 0.2);

/* Right-only shadow (sidebars) */
box-shadow: 4px 0 6px -4px rgba(0, 0, 0, 0.2);

/* Left-only shadow */
box-shadow: -4px 0 6px -4px rgba(0, 0, 0, 0.2);

/* Bottom shadow with more diffusion */
box-shadow: 0 8px 16px -8px rgba(0, 0, 0, 0.15);

The trick: set a negative spread-radius equal to (or slightly less than) the blur-radius. This shrinks the shadow on all sides, and the offset then pushes the visible portion in one direction. For example, with 0 4px 6px -4px, the 6px blur creates a 6px shadow, the -4px spread shrinks it by 4px, and the 4px y-offset shifts the remaining visible shadow below the element. The result is a clean, directional shadow with no bleed on the other sides.

7. Inset Shadows

The inset keyword moves the shadow inside the element, creating the illusion of a recessed or pressed surface. Inset shadows are used for pressed button states, input fields, progress bars, and neumorphic designs.

/* Pressed / recessed surface */
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);

/* Input field inner shadow */
.input-field {
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
}
.input-field:focus {
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1),
              0 0 0 3px rgba(59, 130, 246, 0.3);
}

/* Inner glow effect */
box-shadow: inset 0 0 20px rgba(245, 158, 11, 0.15);

/* Combined inset + outer shadow */
box-shadow: inset 0 -2px 4px rgba(0, 0, 0, 0.1),
            0 2px 4px rgba(0, 0, 0, 0.1);

/* Progress bar recessed track */
.progress-track {
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
  border-radius: 9999px;
  background: var(--surface-2);
}

Inset shadows and outer shadows can coexist on the same element โ€” just comma-separate them. The input field example above uses this pattern to maintain the inner depth while adding a focus ring. This is a cleaner solution than toggling between shadow states on focus.

8. Neumorphism

Neumorphism (new skeuomorphism) creates a soft, extruded look using two shadows โ€” a light shadow on one side and a dark shadow on the other. The element appears to be raised from the same material as the background, like soft clay.

/* Light theme neumorphism */
.neu-raised {
  background: #e0e5ec;
  box-shadow: 8px 8px 16px #b8bcc2,
              -8px -8px 16px #ffffff;
  border-radius: 16px;
}

/* Neumorphic pressed state */
.neu-pressed {
  background: #e0e5ec;
  box-shadow: inset 4px 4px 8px #b8bcc2,
              inset -4px -4px 8px #ffffff;
  border-radius: 16px;
}

/* Neumorphic button with states */
.neu-button {
  background: #e0e5ec;
  box-shadow: 6px 6px 12px #b8bcc2,
              -6px -6px 12px #ffffff;
  border-radius: 12px;
  transition: box-shadow 0.2s ease;
}
.neu-button:active {
  box-shadow: inset 4px 4px 8px #b8bcc2,
              inset -4px -4px 8px #ffffff;
}

/* Dark theme neumorphism */
.neu-dark {
  background: #1a1a2e;
  box-shadow: 8px 8px 16px #111122,
              -8px -8px 16px #23233a;
  border-radius: 16px;
}

Neumorphism requires the element background to match (or be very close to) the page background โ€” that's what creates the "extruded from the surface" illusion. This constraint limits its flexibility, which is why neumorphism works best for specific UI elements (calculators, music players, settings panels) rather than entire page layouts.

Accessibility warning: Neumorphism's low-contrast nature makes it difficult for users with low vision or contrast sensitivity to distinguish interactive elements. If you use it, add additional visual cues โ€” icons, labels, or colour accents โ€” to ensure elements remain identifiable. Generate accessible neumorphic colour combinations using Hue's palette generator.

9. Glassmorphism Shadows

Glassmorphism creates a frosted-glass effect using backdrop-filter: blur() combined with translucent backgrounds and carefully crafted shadows. The shadow plays a supporting role โ€” adding depth below the glass element while the blur and transparency do the heavy lifting.

/* Glassmorphism card */
.glass-card {
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}

/* Elevated glass panel */
.glass-elevated {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 20px;
  box-shadow:
    0 4px 16px rgba(0, 0, 0, 0.15),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

/* Glass with inner highlight (simulates light refraction) */
.glass-refraction {
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(16px);
  border-radius: 16px;
  box-shadow:
    0 8px 32px rgba(0, 0, 0, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 rgba(0, 0, 0, 0.1);
}

The inset 0 1px 0 rgba(255,255,255,0.1) trick adds a thin highlight along the top edge, simulating how real glass catches light. Combined with a subtle bottom inset shadow, it creates convincing edge lighting. Glassmorphism requires a colourful or gradient background behind the glass element to look good โ€” on a plain white or black background, the effect falls flat. Pair it with a vibrant background from Hue's palette generator.

10. Neon Glow Effects

Neon glow effects use brightly coloured, high-blur shadows to create a luminous aura around elements. They work best on dark backgrounds where the glow is visible, and they're perfect for gaming UIs, music apps, and creative portfolios.

/* Neon blue glow */
.neon-blue {
  box-shadow: 0 0 10px rgba(59, 130, 246, 0.5),
              0 0 20px rgba(59, 130, 246, 0.3),
              0 0 40px rgba(59, 130, 246, 0.15);
}

/* Neon pink glow */
.neon-pink {
  box-shadow: 0 0 10px rgba(236, 72, 153, 0.6),
              0 0 20px rgba(236, 72, 153, 0.4),
              0 0 40px rgba(236, 72, 153, 0.2),
              0 0 80px rgba(236, 72, 153, 0.1);
}

/* Neon text glow (using text-shadow, not box-shadow) */
.neon-text {
  color: #F59E0B;
  text-shadow: 0 0 7px rgba(245, 158, 11, 0.5),
               0 0 10px rgba(245, 158, 11, 0.3),
               0 0 21px rgba(245, 158, 11, 0.2),
               0 0 42px rgba(245, 158, 11, 0.1);
}

/* Neon border effect */
.neon-border {
  border: 1px solid rgba(59, 130, 246, 0.5);
  box-shadow: 0 0 10px rgba(59, 130, 246, 0.3),
              0 0 20px rgba(59, 130, 246, 0.15),
              inset 0 0 10px rgba(59, 130, 246, 0.1);
}

/* Animated neon pulse */
.neon-pulse {
  animation: neon-glow 2s ease-in-out infinite alternate;
}
@keyframes neon-glow {
  from {
    box-shadow: 0 0 5px rgba(59, 130, 246, 0.3),
                0 0 10px rgba(59, 130, 246, 0.2);
  }
  to {
    box-shadow: 0 0 10px rgba(59, 130, 246, 0.6),
                0 0 20px rgba(59, 130, 246, 0.4),
                0 0 40px rgba(59, 130, 246, 0.2);
  }
}

Neon glows use layered shadows at progressively larger blur radii with decreasing opacity โ€” 3-4 layers is the sweet spot. More layers create a more gradual falloff. For neon text, use text-shadow instead of box-shadow because text-shadow follows the text shape. Check that your glow colours work well in both your dark theme and on Hue's dark mode previewer.

11. Colored & Brand Shadows

Standard shadows use black or dark grey. Colored shadows use the element's own colour (or brand colour) to create a glow-like effect that ties the shadow to the element visually. Apple popularised this in their marketing materials.

/* Colored shadow matching the background */
.card-blue {
  background: #3B82F6;
  box-shadow: 0 8px 20px rgba(59, 130, 246, 0.35);
}

.card-amber {
  background: #F59E0B;
  box-shadow: 0 8px 20px rgba(245, 158, 11, 0.35);
}

/* Brand shadow using CSS custom properties */
.branded-card {
  --brand: 59, 130, 246; /* RGB values */
  background: rgb(var(--brand));
  box-shadow: 0 8px 20px rgba(var(--brand), 0.35);
}

/* Gradient card with matching shadow */
.gradient-card {
  background: linear-gradient(135deg, #F59E0B, #EF4444);
  box-shadow: 0 10px 30px rgba(239, 68, 68, 0.3);
}

The CSS custom property approach (--brand: 59, 130, 246) lets you change the shadow colour by updating a single variable, keeping shadow and background in sync. This technique integrates beautifully with design system tokens โ€” generate your brand colour scale with Hue's palette tool, then apply the mid-tone as the shadow colour at 30-40% opacity.

12. Box Shadow vs Drop Shadow Filter

CSS has two shadow mechanisms: the box-shadow property and the filter: drop-shadow() function. They look similar but behave very differently.

/* box-shadow: rectangular, follows the box model */
.box-shadow-demo {
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

/* drop-shadow: follows the rendered shape */
.drop-shadow-demo {
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
}

Key Differences

  • Shape: box-shadow is always rectangular (following the element's box). drop-shadow() follows the actual visible shape โ€” including transparent areas in PNGs, SVGs, and elements with clip-path.
  • Inset: box-shadow supports inset. drop-shadow() does not.
  • Spread: box-shadow supports a spread value. drop-shadow() does not (though it was added to the spec recently, browser support is limited).
  • Multiple shadows: box-shadow supports comma-separated multiples. drop-shadow() requires chaining multiple drop-shadow() functions.
  • Performance: drop-shadow() is a filter and creates a new stacking context. It can be more expensive because it needs to sample the rendered pixels to determine the shape.
  • Border-radius: box-shadow follows border-radius automatically. drop-shadow() follows the visual shape, which includes border-radius but also any clipping or transparency.

When to Use Each

Use box-shadow for most UI elements โ€” cards, buttons, modals, inputs. It's faster, more flexible (inset, spread, multiples), and covers 95% of shadow needs. Use drop-shadow() when you need the shadow to follow a non-rectangular shape: PNG images with transparent backgrounds, SVG icons, elements with clip-path, or CSS shapes.

13. Performance Considerations

Box shadows are rendered by the browser's paint engine. A single shadow on a typical element is negligible โ€” the browser optimises heavily for this common case. However, shadows can become a bottleneck in specific situations.

Expensive Shadow Patterns

  • Large blur radii (50px+) on many elements โ€” each blur is a Gaussian convolution, and the cost scales with blur size.
  • Animating box-shadow directly โ€” changing the shadow values on every frame triggers a repaint. Each frame recomputes and repaints the blur.
  • Shadows on scrolling elements โ€” elements with shadows that enter and leave the viewport during scroll can trigger excessive repaints.
  • Many shadowed elements simultaneously โ€” a page with 100+ cards each with layered shadows adds up.

The Pseudo-Element Trick

Instead of animating box-shadow directly, apply the shadow to a ::after pseudo-element and animate its opacity. Opacity changes are GPU-composited and avoid repaint entirely.

/* Performant shadow animation */
.card {
  position: relative;
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 12px 24px rgba(0,0,0,0.15);
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.card:hover::after {
  opacity: 1;
}

/* will-change for animated shadows */
.card-animate {
  will-change: transform;
  transition: transform 0.2s ease;
}
.card-animate:hover {
  transform: translateY(-4px);
}

This pattern is used by virtually every high-performance design system. The shadow is "always there" โ€” it's just invisible until hover. Fading it in via opacity is a compositor-only operation, which means zero repaints and smooth 60fps animation. For broader page performance analysis including paint profiling, Clarity SEO can identify pages where excessive shadow rendering impacts Core Web Vitals.

GPU Layer Promotion

Adding will-change: transform promotes an element to its own GPU layer, which means its shadow is rendered once and cached in GPU memory. This is beneficial for elements that move (carousels, drag-and-drop) but wasteful for static elements. As always, only promote elements that actually animate. Profile with Chrome DevTools โ†’ Layers panel to visualise compositor layers and their memory cost. Read more about rendering performance on web.dev.

14. Box Shadow in Tailwind CSS

Tailwind CSS provides a set of shadow utility classes that map to a carefully tuned shadow scale. These cover most use cases without custom CSS.

<!-- Tailwind shadow scale -->
<div class="shadow-sm">   <!-- 0 1px 2px rgba(0,0,0,0.05) -->
<div class="shadow">      <!-- 0 1px 3px, 0 1px 2px -->
<div class="shadow-md">   <!-- 0 4px 6px, 0 2px 4px -->
<div class="shadow-lg">   <!-- 0 10px 15px, 0 4px 6px -->
<div class="shadow-xl">   <!-- 0 20px 25px, 0 8px 10px -->
<div class="shadow-2xl">  <!-- 0 25px 50px -12px -->
<div class="shadow-inner"> <!-- inset 0 2px 4px -->
<div class="shadow-none">  <!-- none -->

<!-- Colored shadows (Tailwind v3.1+) -->
<div class="shadow-lg shadow-blue-500/30">
<div class="shadow-lg shadow-amber-500/25">

<!-- Custom shadow via arbitrary value -->
<div class="shadow-[0_8px_30px_rgba(0,0,0,0.12)]">

<!-- Ring utilities (focus rings) -->
<button class="focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">

Tailwind's colored shadow feature (shadow-blue-500/30) is extremely useful โ€” it applies the shadow colour using the CSS --tw-shadow-color variable. The /30 sets the alpha channel. This makes brand-coloured shadows trivial.

The ring utilities (ring-2, ring-blue-500) use box-shadow under the hood โ€” they generate a 0 0 0 Xpx shadow (zero blur, just spread) to create focus rings. This is why ring utilities and shadow utilities can't be combined directly in Tailwind v3 โ€” they share the same CSS property. Tailwind v4 resolves this with separate shadow layers.

Customizing the Shadow Scale

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      boxShadow: {
        'soft': '0 2px 8px rgba(0, 0, 0, 0.06)',
        'card': '0 4px 12px rgba(0, 0, 0, 0.08)',
        'elevated': '0 8px 24px rgba(0, 0, 0, 0.1)',
        'floating': '0 16px 48px rgba(0, 0, 0, 0.12)',
        'neon-blue': '0 0 20px rgba(59, 130, 246, 0.3)',
      }
    }
  }
}

Define your shadow tokens in the Tailwind config and use them consistently across your project. This creates a shadow system that's as systematic as your colour palette and spacing scale. Experiment with values interactively using Hue's box shadow generator before codifying them in your config.

15. Dark Mode Shadow Strategies

Shadows behave fundamentally differently in dark mode. A shadow that looks natural on a white background becomes invisible โ€” or worse, creates a muddy dark halo โ€” on a dark background. You need a completely different strategy.

Why Standard Shadows Fail in Dark Mode

In light mode, shadows work because they're darker than the background. In dark mode, the background is already dark โ€” a rgba(0,0,0,0.1) shadow is barely visible against a #0a0a0b background. You can increase opacity, but beyond 0.3 it starts looking like a black blob rather than a natural shadow.

Strategies That Work

/* Strategy 1: Increase opacity dramatically */
.card-dark {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5),
              0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Strategy 2: Light/white shadows for lift effect */
.card-dark-light {
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.05),
              0 1px 3px rgba(0, 0, 0, 0.3);
}

/* Strategy 3: Border-based depth instead of shadow */
.card-dark-border {
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: none; /* or very subtle */
}

/* Strategy 4: Elevated surface color (Material Design approach) */
.card-dark-elevated {
  background: rgba(255, 255, 255, 0.05); /* lighter than page bg */
  border: 1px solid rgba(255, 255, 255, 0.06);
}

/* Strategy 5: Colored ambient glow */
.card-dark-glow {
  box-shadow: 0 0 20px rgba(59, 130, 246, 0.1);
  border: 1px solid rgba(59, 130, 246, 0.15);
}

/* Combined approach with CSS custom properties */
:root {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.07);
}
[data-theme="dark"] {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.4);
}

Material Design's approach โ€” using elevated surface colours (slightly lighter backgrounds) instead of shadows โ€” is the most robust dark-mode strategy. Each elevation level gets a progressively lighter surface, creating a visual hierarchy without relying on shadows at all. Shadows can supplement this with very high opacity values, but they're no longer the primary depth cue. Preview how your shadows will look in dark mode using Hue's dark mode previewer.

16. Common Mistakes

Even experienced developers make these shadow mistakes. Avoid these pitfalls for cleaner, more professional results.

โŒ Using solid black shadows

box-shadow: 0 4px 8px black looks harsh and unnatural. Real-world shadows are never fully opaque. Always use semi-transparent colours: rgba(0,0,0,0.08) for subtle, rgba(0,0,0,0.15) for medium, rgba(0,0,0,0.25) for strong.

โŒ Same shadow everywhere

Not all elements should have the same elevation. A flat list item, a card, a dropdown, and a modal dialog represent different depths. Use a shadow scale (4-6 levels) and assign shadows based on the element's conceptual distance from the page surface.

โŒ Animating box-shadow on hover

transition: box-shadow 0.3s works but triggers repaints every frame during the transition. Use the pseudo-element opacity trick from Section 13 for smooth, repaint-free shadow animations.

โŒ Forgetting shadows overflow

Box shadows don't affect layout, but they do extend beyond the element's bounds. If the parent has overflow: hidden, shadows get clipped. Add padding to the parent or use overflow: visible where appropriate.

โŒ Using box-shadow for non-rectangular shapes

If your element has a clip-path, transparent PNG, or SVG shape, box-shadow will still draw a rectangular shadow. Use filter: drop-shadow() instead โ€” see Section 12.

โŒ Ignoring dark mode

If your site supports dark mode, your shadows need dark-mode values. A single shadow definition won't work for both themes. Use CSS custom properties or separate light/dark classes โ€” see Section 15.

17. Frequently Asked Questions

What is the difference between box-shadow and drop-shadow?

box-shadow applies a rectangular shadow to an element's box model, including padding and border. The drop-shadow() filter follows the actual rendered shape of the element including transparent areas, making it ideal for PNGs, SVGs, and elements with clip-path. See our shadow generator to experiment with both.

Can you use multiple box shadows on one element?

Yes. Separate multiple shadows with commas. They render in stack order โ€” the first shadow listed appears on top. This technique is used for layered shadow effects, realistic depth, and combining coloured glows with neutral shadows.

Does box-shadow affect layout or element size?

No. Box shadows are purely visual and do not affect the box model, layout flow, or element dimensions. They can overflow their container, however, so you may need overflow: hidden on parent elements or extra padding to prevent clipping.

How do I make box-shadow work in dark mode?

In dark mode, traditional shadows are invisible against dark backgrounds. Use lighter semi-transparent shadows (rgba(255,255,255,0.05)), subtle coloured glows, elevated surface colours, or border-based depth cues instead. Use CSS custom properties to switch between light and dark shadow values.

Is box-shadow bad for performance?

Simple box shadows are very fast. Performance issues arise from large blur radii (50px+), many layered shadows on many elements, or animating box-shadow directly. For animations, transition opacity on a pseudo-element shadow instead of animating the shadow property itself.

How do I create a one-sided box shadow?

Use the fourth value (spread radius) as a negative value to shrink the shadow, combined with an offset in one direction. For example, box-shadow: 0 4px 6px -4px rgba(0,0,0,0.3) creates a bottom-only shadow by pulling the spread inward while the y-offset pushes the visible portion below the element.

๐Ÿ“š Related Guides

๐ŸŽจ Hue Pro coming soon
Brand guide builder, font pairing, mockup generator. Get early access.