Smooth shadows
Five-layer shadows that fall off like real light. Size, intensity, tint and a hairline ring.
size 0.5
size 1
size 2
size 3 · hairline
Installation
$ pnpm dlx shadcn@latest add @ssntl/shadow-smoothUsage
/* app/globals.css */
@import "../styles/ssntl/shadow-smooth.css";
/* In your JSX */
<div class="shadow-smooth">…</div>
<div class="shadow-smooth shadow-size-2 shadow-intensity-150">…</div>
<div class="shadow-smooth shadow-tint-blue-600 shadow-hairline">…</div>- Five layers that double in distance and blur: the shadow falls off like real light, not one blur.
- Two numbers control it: shadow-size-{n} scales the stack and shadow-intensity-{n} its opacity in %.
- In dark mode the stack is 3× stronger so it still reads; it follows data-theme and the OS.
- Only for what floats. Areas are separated by tone, not shadow.
Code
/* Smooth shadows: five layers that double in distance and blur, so the shadow
falls off like real light instead of one hard blur.
shadow-smooth the stack, size 1, intensity 100
shadow-size-{n} scale the stack (0.5, 2, 3…)
shadow-intensity-{n} opacity in % (0–300)
shadow-tint-{color} tint the stack with a palette color
shadow-hairline add a hairline ring (0.5px on retina)
Dark surfaces need more shadow to read, so the stack is 3× stronger in dark. */
:root,
[data-theme="light"],
.light {
--shadow-strength: 1;
}
[data-theme="dark"],
.dark {
--shadow-strength: 3;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"], .light) {
--shadow-strength: 3;
}
}
@utility shadow-smooth {
--_s: var(--shadow-size, 1);
--_a: calc(3% * var(--shadow-intensity, 1) * var(--shadow-strength, 1));
--_c: var(--shadow-color, #000);
box-shadow:
0 0 0 var(--shadow-ring, 0px)
color-mix(in oklab, var(--_c) calc(var(--_a) * 3), transparent),
0 calc(1px * var(--_s)) calc(2px * var(--_s))
color-mix(in oklab, var(--_c) var(--_a), transparent),
0 calc(2px * var(--_s)) calc(4px * var(--_s))
color-mix(in oklab, var(--_c) var(--_a), transparent),
0 calc(4px * var(--_s)) calc(8px * var(--_s))
color-mix(in oklab, var(--_c) var(--_a), transparent),
0 calc(8px * var(--_s)) calc(16px * var(--_s))
color-mix(in oklab, var(--_c) var(--_a), transparent),
0 calc(16px * var(--_s)) calc(32px * var(--_s))
color-mix(in oklab, var(--_c) var(--_a), transparent);
}
@utility shadow-size-* {
--shadow-size: --value(number);
}
@utility shadow-intensity-* {
--shadow-intensity: calc(--value(integer) / 100);
}
@utility shadow-tint-* {
--shadow-color: --value(--color-*, [color]);
}
@utility shadow-hairline {
--shadow-ring: 1px;
@media (min-resolution: 2dppx) {
--shadow-ring: 0.5px;
}
}