← Back to gallery
CSS

Card Hover Shadow Depth

Card lifts on hover via transform: translateY + an animated box-shadow that grows in radius and softens. Three product contexts: a dashboard metric tile, a product-catalogue card, and a hero CTA panel.

hover-cardbox-shadowtranslateYtransition-propertydepthfocus-visibleprefers-reduced-motion

Interaction · box-shadow · elevation tiers

Card Hover Shadow Depth

Three elevation tiers driven by rest/hover box-shadow pairs, each tuned to a real product context — a Dashboard Tile (flat → soft ambient), a Catalogue Card (two-layer floating shadow that deepens on hover), and a Hero CTA (pronounced shadow + a translateY lift). Layout stays stable across all three since only box-shadow + transform change. Stage cards JS-cycle the hover state every 2.4s; inspector preview is a real <a> with Tab captured to refocus the demo card.

Flat → Soft · ambient on hover

Dashboard Tile

A metric tile that lives flat at rest and gains a soft ambient shadow on hover. Suits dashboards where elevation should not shout — the rest state has no shadow at all, and the hover state introduces a modest 12px-blur ambient lift without translating the card.

  • dashboard
  • flat rest
  • no lift

Floating · two-layer deepens

Catalogue Card

A product catalogue card that floats at rest with an ambient shadow and deepens on hover with a second contact shadow. Reads more naturally than a single large blur because real shadows have a tight contact layer + a softer ambient layer.

  • catalogue
  • two-layer
  • permanent ambient

Pronounced · shadow + 6px lift

Hero CTA

A featured hero / primary-CTA card with a pronounced hover lift — combines a deep box-shadow change with a 6px translateY so the action reads as unmistakably interactive. Use sparingly; this is the loudest tier and is meant for primary calls-to-action, not list rows.

  • hero
  • CTA
  • translateY 6px

Hover shadow inspector

Dashboard Tile

  • dashboard
  • flat rest
  • no lift

A metric tile that lives flat at rest and gains a soft ambient shadow on hover. Suits dashboards where elevation should not shout — the rest state has no shadow at all, and the hover state introduces a modest 12px-blur ambient lift without translating the card.

Helped you ship something? 🐟 Send my cat a churu

/* Dashboard tile — flat at rest, soft ambient shadow on hover. No lift. */
.hover-card {
  display: block;
  padding: 18px 20px;
  background: rgba(15, 23, 42, 0.85);
  border-radius: 12px;
  text-decoration: none;
  color: inherit;
  box-shadow: 0 0px 0px 0px rgba(0, 0, 0, 0.35);
  transition:
    box-shadow 0.18s ease,
    transform 0.18s ease;
}

.hover-card:hover,
.hover-card:focus-visible {
  box-shadow: 0 4px 12px 0px rgba(0, 0, 0, 0.55);
}

.hover-card:focus-visible {
  outline: 2px solid #7dd3fc;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .hover-card { transition-property: box-shadow; }
  .hover-card:hover,
  .hover-card:focus-visible { transform: none; }
}

How to make this

A CSS card hover shadow effect pairs a calibrated rest shadow with a deeper hover shadow and an optional transform lift, mirrored on focus-visible for keyboard users.

html
1<a class="hover-depth-card" href="#">  <span class="hover-depth-card__eyebrow">Featured</span>  <span class="hover-depth-card__title">Studio Kit</span>  <span class="hover-depth-card__meta">Ships today</span></a> <style>.hover-depth-card {  display: grid;  gap: .45rem;  width: min(100%, 260px);  padding: 1rem;  border-radius: 12px;  color: #f8fafc;  text-decoration: none;  background: linear-gradient(180deg, #111827, #020617);  border: 1px solid rgba(148, 163, 184, .18);18  box-shadow: 0 2px 10px rgba(0, 0, 0, .34);  transform: translateY(0);20  transition:    box-shadow .22s ease,    transform .22s ease,    border-color .22s ease;}.hover-depth-card:hover,.hover-depth-card:focus-visible {27  box-shadow: 0 14px 32px 4px rgba(0, 0, 0, .52);28  transform: translateY(-4px);  border-color: rgba(125, 211, 252, .48);}.hover-depth-card:focus-visible {32  outline: 2px solid #7dd3fc;  outline-offset: 4px;}.hover-depth-card__eyebrow {  color: #7dd3fc;  font: 700 .72rem/1 ui-sans-serif, system-ui;  text-transform: uppercase;  letter-spacing: .08em;}.hover-depth-card__title { font: 700 1.1rem/1.2 ui-sans-serif, system-ui; }.hover-depth-card__meta { color: rgba(203, 213, 225, .72); }43@media (prefers-reduced-motion: reduce) {  .hover-depth-card { transition-property: box-shadow, border-color; }  .hover-depth-card:hover,  .hover-depth-card:focus-visible { transform: none; }}</style>

Annotated snippet

  1. Line 1Use the real interactive element for the card. A link card should stay a link so focus, context menus, and open-in-new-tab behavior are preserved.
  2. Line 18The rest shadow sets the baseline elevation. Starting from zero is fine for flat dashboard tiles, but product cards often need a small ambient shadow before hover.
  3. Line 20Transition only the properties that should change. Avoid transition: all because later design changes can accidentally animate width, padding, or color tokens.
  4. Line 27Hover and focus-visible share the same elevation target. This keeps mouse and keyboard feedback equivalent without adding JS.

    Margin-based lift disturbs nearby layout; transform lift leaves the card footprint stable.

    PitfallWhy mirror card hover styles on focus-visible?

    Keyboard users need the same affordance that pointer users get. Put the elevation and border treatment on :hover and :focus-visible, then add a clear outline so the focus target is unmistakable.

  5. Line 28TranslateY sells the card as rising, while box-shadow sells the light change. Keep both on the same easing so the gesture resolves as one movement.
    PitfallDoes transform hover lift affect layout?

    No. transform moves the painted card without changing its layout footprint, which prevents neighboring cards from shifting. Margin, top, or padding-based lift can create layout movement.

  6. Line 32Focus rings need to clear the enlarged shadow. outline-offset prevents the ring from being swallowed by the hover glow.

    Only outline-offset changes here. Without offset, the focus ring sits flush against the card edge and disappears into the hover shadow; with offset it sits outside the shadow halo and stays visible for keyboard users.

  7. Line 43Reduced motion removes the vertical lift but keeps the shadow and border feedback. Keyboard users still get a visible state change without motion.
    PitfallWhat should reduced motion do for hover depth?

    Remove the translate lift and any springy movement, but keep non-motion cues such as a stronger shadow, border color, or outline. The card should still feel interactive.

Other pitfalls

How much shadow should a hover card use?
Match the product context. Dense dashboards often need a small shadow-only change, catalogue cards can use a two-layer shadow, and primary CTA cards can pair a deeper shadow with a small lift. One shadow recipe should not be reused everywhere.
Is box-shadow animation performant?
Box-shadow can repaint, especially with large blur radii across many cards. Keep hover shadows modest in lists, avoid animating huge spreads, and use transform for the physical lift instead of margin or top.

Notes

Overview

Card hover-lift effect that combines transform: translateY with an expanding box-shadow radius so the card visibly rises from the surface. Three product contexts ship: dashboard metric tile, product-catalogue card with thumbnail, hero CTA panel with arrow.

When to use it

Reach for hover-lift on linkable cards (product grids, dashboard tiles, blog index entries). Skip it for dense list rows — the cumulative translateY noise becomes distracting. Skip it for touch-first contexts; without hover, the lift never reads, so save the design budget for touch-feedback patterns instead.

How it works

At rest the card has a tight box-shadow: 0 2px 4px rgba(0,0,0,0.08) suggesting it sits close to the surface. On :hover, transition to a wider, softer shadow like 0 12px 28px rgba(0,0,0,0.16) alongside a transform: translateY(-4px). The compound effect — shadow growing + element rising — sells the physics. Use a transition duration around 180–240ms with cubic-bezier(.2, .8, .2, 1) for the lift to feel material rather than abrupt. Always also handle :focus-visible with the same lift so keyboard users see the affordance.

Production gotchas

Animating box-shadow is more expensive than animating transform — both trigger paint on the shadow region. For high card counts, swap to a pre-baked dual-shadow approach: layer two box-shadows with different opacities and animate the second layer’s opacity instead of the radii. The translate must not push the card outside any clipping ancestor — check overflow: hidden parents. On macOS, the soft-shadow rendering treats blurs above ~30px as expensive on retina displays.

Accessibility

Cards used as links must be a real <a> element — not a div with onClick — so keyboard users can tab to them. The hover-lift must mirror under :focus-visible or you orphan keyboard users from the affordance. Under prefers-reduced-motion: reduce drop the translate and keep only the shadow change, or drop the animation entirely and use a subtle border change for the focused state.

References

Implementation depth

translateY should carry the lift while box-shadow carries the perceived elevation. Keeping those jobs separate lets the card feel deeper without changing layout or forcing neighboring content to reflow.

Hover depth also needs keyboard parity. Pair hover with focus-visible, make touch devices work without relying on hover, and reduce motion by keeping the elevated shadow but removing the moving lift.