Creating Realistic CSS Shadows

May 28, 20266 min read

Why Most CSS Shadows Look Fake

A single box-shadow on an element produces a flat, uniform shadow — nothing like what happens in the real world. Real shadows have multiple layers: a tight, dark shadow near the object and a softer, lighter shadow that spreads outward. They also respond to the distance between the object and the surface beneath it.

The gap between a box-shadow: 0 4px 6px rgba(0,0,0,0.1) and a realistic shadow is the gap between a clip-art drop shadow and a photograph. The fix is surprisingly simple: use multiple shadow layers.

The Layered Shadow Technique

Instead of one shadow, stack several with increasing offset and blur:

.realistic-shadow {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.07),
    0 2px 4px rgba(0, 0, 0, 0.07),
    0 4px 8px rgba(0, 0, 0, 0.07),
    0 8px 16px rgba(0, 0, 0, 0.07),
    0 16px 32px rgba(0, 0, 0, 0.07),
    0 32px 64px rgba(0, 0, 0, 0.07);
}

Each layer doubles the offset and blur of the previous one while keeping the opacity constant. The result: a shadow that's sharp near the edge and fades gradually outward — exactly how real shadows behave.

Why This Works

In reality, a shadow is sharpest where the object almost touches the surface and softest where it's farthest away. Multiple layers simulate this graduated falloff. A single box-shadow can't produce this effect because it has only one falloff curve.

Shadow Depth Scale

Use elevation levels to keep shadows consistent across your design:

LevelShadowUse
0noneFlat elements, dividers
10 1px 2px rgba(0,0,0,0.05), 0 1px 3px rgba(0,0,0,0.1)Cards at rest
20 3px 6px rgba(0,0,0,0.06), 0 2px 4px rgba(0,0,0,0.08)Raised cards
30 10px 20px rgba(0,0,0,0.06), 0 3px 6px rgba(0,0,0,0.08)Dropdowns
40 20px 40px rgba(0,0,0,0.08), 0 5px 10px rgba(0,0,0,0.06)Modals
50 30px 60px rgba(0,0,0,0.1), 0 10px 20px rgba(0,0,0,0.08)Overlays

Tailwind Utility Classes

.shadow-1 { box-shadow: 0 1px 2px rgba(0,0,0,0.05), 0 1px 3px rgba(0,0,0,0.1); }
.shadow-2 { box-shadow: 0 3px 6px rgba(0,0,0,0.06), 0 2px 4px rgba(0,0,0,0.08); }
.shadow-3 { box-shadow: 0 10px 20px rgba(0,0,0,0.06), 0 3px 6px rgba(0,0,0,0.08); }
.shadow-4 { box-shadow: 0 20px 40px rgba(0,0,0,0.08), 0 5px 10px rgba(0,0,0,0.06); }
.shadow-5 { box-shadow: 0 30px 60px rgba(0,0,0,0.1), 0 10px 20px rgba(0,0,0,0.08); }

Directional Light Shadows

Set a consistent light source direction for all shadows in your design. The most common convention is top-left lighting (matching natural light from a window):

/* Top-left light direction */
.top-left-shadow {
  box-shadow:
    -2px 2px 4px rgba(0, 0, 0, 0.05),
    -4px 4px 8px rgba(0, 0, 0, 0.05),
    -8px 8px 16px rgba(0, 0, 0, 0.05);
}

Once you commit to a light direction, apply it consistently across cards, dropdowns, modals, and tooltips. Mixed shadow directions break spatial coherence.

Dark Mode Shadows

Shadows behave differently on dark backgrounds. A shadow that reads clearly on white becomes invisible on dark surfaces because there's less contrast between the shadow and the background.

Approach 1: Colored Shadows

Use a darker version of the background color instead of black:

.dark .card {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.3),
    0 4px 8px rgba(0, 0, 0, 0.2),
    0 12px 24px rgba(0, 0, 0, 0.15);
}

The opacity values are inverted relative to light mode — dark mode shadows need higher opacity to be visible.

Approach 2: Inset Borders

On very dark surfaces, subtle inset borders can replace shadows entirely:

.dark .card {
  box-shadow: none;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

This approach is often more reliable than shadows on near-black backgrounds.

Colored Shadows for Brand Accents

Match shadow color to the element's background for a cohesive glow effect:

.blue-button {
  background: #3b82f6;
  box-shadow:
    0 4px 6px rgba(59, 130, 246, 0.2),
    0 10px 20px rgba(59, 130, 246, 0.15);
}

.green-card {
  background: white;
  border-top: 3px solid #10b981;
  box-shadow:
    0 4px 6px rgba(16, 185, 129, 0.1),
    0 10px 20px rgba(16, 185, 129, 0.08);
}

Colored shadows work best when the tint is subtle — high-opacity colored shadows look artificial.

Hover Transitions

Animate shadows on interaction to create a sense of lifting:

.card {
  transition: box-shadow 0.2s ease, transform 0.2s ease;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 1px 3px rgba(0, 0, 0, 0.1);
}

.card:hover {
  transform: translateY(-2px);
  box-shadow:
    0 4px 8px rgba(0, 0, 0, 0.06),
    0 3px 6px rgba(0, 0, 0, 0.08);
}

The slight translateY reinforces the illusion of lifting away from the surface.

Try It Yourself

Design realistic, layered shadows visually. Adjust offset, blur, spread, and opacity for each layer with our interactive box-shadow generator.

👉 Box Shadow Generator