Shadow Perspective
A 150 × 100 px indigo card sitting above its ground shadow, a blurred ellipse squashed flat; on hover, tap or keyboard focus, the card rises 10 px while the shadow stays on the ground, fades by half and spreads (blur 10 to 15 px): the cue of an object lifted off a table, nothing else moves. CSS only, no JavaScript.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →
3 usage examples



How it works
The root .fx-0020-root (#0a0a0f background, at least 300 px tall) centers a 280 × 200 px .demo-3d frame, then .shadow-perspective-container, whose 40 px padding leaves room for the lift and the shadow. The object is .shadow-perspective: 150 × 100 px, #6366f1 → #8b5cf6 gradient, 0.75 rem corners, bold centered text, tabindex="0", a 0.3 s transition on its transform.
The shadow is its ::after pseudo-element: a black ellipse at 30% (rgba(0, 0, 0, .3), border-radius: 50%), 80% of the card's width, 20 px tall, set 20 px below it, blurred at 10 px and laid down by rotateX(80deg) — with no perspective above, that rotation amounts to squashing it to 17% of its height (cos 80°), about 3.5 px before the blur: a flat contact shadow. On hover (or :focus-visible), the card takes translateY(-10px) and the shadow moves to bottom: -30px, 50% opacity and a 15 px blur, in 0.3 s. The card rises 10 px and the shadow's anchor moves 10 px further below it: on screen, the shadow does not move, it stays on the ground, lightens and widens. Measured: 2.4% of pixels change on hover, identically in Chromium 151, Firefox 153 and WebKit 26.5.
The shadow is black at 30%: on the original dark background it barely shows, and the card's lift carries the effect. It really reads on a light or mid-tone background — a table, a shelf, a product page — as in the scenes above. The blur works on an already squashed ellipse: it gives a soft edge without thickening the shadow, which a growing box-shadow cannot do (it surrounds the whole card, not just its underside).
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, a block scoped to.fx-0020-rootremoves the transitions and the lift: the card stays put, the shadow lightens and spreads at once, and a 3 px violet outline (rgba(167, 139, 250, .6)) marks hover and focus, which makes it visible even on the dark background. Measured in Chromium and Firefox: transform unchanged, outline present on hover. - Keyboard: the card has
tabindex="0"and focus gives the same lift as hover, plus a 2 px#c4b5fdoutline (10.7:1 on the background). If the card leads to a page, replace thedivwith a link<a class="shadow-perspective" href="…">: nothing else changes. - Contrast: bold white text at 4.47:1 on the indigo
#6366f1and 4.2:1 on the violet#8b5cf6, under the 4.5:1 threshold for 16 px text: darken the gradient if the card carries text that matters. - Touch: with a finger, the tapped card lifts (measured with Chromium touch emulation and in Firefox) and stays up until the next tap.
Browser compatibility
CSS only (transform, filter: blur() on a pseudo-element, :focus-visible), no dependencies. Measured in Chromium 151, Firefox 153 and WebKit 26.5: same render at rest and on hover (under 0.2% apart), same reduced motion, 0 errors. No stacked 3D planes: the shadow's rotation is a plain squash, rendered identically by WebKit.
Without filter, the shadow becomes a sharp, dark 3.5 px ellipse: the effect still reads, only harder. Without :focus-visible, keyboard focus only gets the browser's ring.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0020-root">
<div class="demo-3d">
<div class="shadow-perspective-container"> <!-- 40 px padding for the lift and the shadow -->
<div class="shadow-perspective" tabindex="0">Hover Me</div> <!-- ::after = ground shadow -->
</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Lift height (CSS — hover translateY(-10px)) |
10 px | To keep the shadow on the ground, move its anchor down by the same amount: a 16 px lift, bottom: -36px on hover. |
Resting shadow (CSS — ::after: color, bottom, width, filter) |
rgba(0, 0, 0, .3); 20 px below the object; 80%; 10 px blur | On a light background, a warm tint (brown at 40-60%) looks more natural than black. The width follows the object's base: 70% for a bowl, 80% for a card. |
Lifted shadow (CSS — hover ::after: bottom, opacity, filter) |
30 px; 0.5; 15 px blur | The higher the object, the paler and wider the shadow should get: with a 20 px lift, try 0.35 and a 20 px blur. |
Shadow squash (CSS — rotateX(80deg)) |
80° | 70° gives a thicker shadow (a table seen from higher up); 85° an almost flat line. |
Object (CSS — size, background, corners of .shadow-perspective) |
150 × 100 px, #6366f1 → #8b5cf6, 0.75 rem | The effect works on any shape: the scenes turn it into a phone, a jar and a bowl. The ::before pseudo-element stays free for a detail (lid, rim). |
Duration (CSS — card and shadow transition) |
0.3 s | Keep the same duration on both, or the shadow arrives before or after the object. |
FAQ
#0a0a0f it almost blends into the background, and what you mostly see is the card rising. The effect is made for a light or mid-tone background, where a contact shadow reads as in real life: a table, a shelf, a product page. The three scenes show it on cream and wood backgrounds.box-shadow surrounds the whole card: it says "card floating above the page". Here the shadow is a flat ellipse set under the object, apart from it: when the object rises, it stays on the ground, fades and spreads, which says "object lifted off a surface". It is the cue product photos give..shadow-perspective (or set it as its background), adjust its size, and match the shadow's width to the object's base. A cut-out image (transparent background) works best: the shadow then shows right under the object.