Hover Card Lift
Preview: /previews/hover-card.mp4
Constraints:
- Transforms + opacity only (no top/left).
- No layout shift on hover.
- Enter 150–220ms, exit 120–180ms.
Resources:
Goal
Recreate the preview: a card subtly lifts and sharpens shadow on hover/focus.
Acceptance tests
- Uses
transform: translateY()andbox-shadowonly (no reflow). - Enter duration in 150–220ms, exit 120–180ms.
- Easing feels natural (custom
cubic-bezierencouraged). - No layout shift (card’s position in flow is unchanged).
- Keyboard focus produces the same motion as hover.
Starter
<div class="card" tabindex="0">
<img src="/placeholder.jpg" alt="" class="card__img" />
<div class="card__body">
<h3>Card title</h3>
<p>Small description goes here.</p>
</div>
</div>
<style>
:root {
--lift-y: -6px;
--dur-in: 180ms;
--dur-out: 140ms;
--ease: cubic-bezier(.2,.7,.2,1);
--shadow-rest: 0 6px 18px rgba(0,0,0,.06);
--shadow-hover: 0 12px 28px rgba(0,0,0,.12);
}
.card {
display: grid;
gap: .75rem;
padding: 1rem;
border-radius: 14px;
background: white;
box-shadow: var(--shadow-rest);
transform: translateY(0);
transition:
transform var(--dur-in) var(--ease),
box-shadow var(--dur-in) var(--ease),
filter var(--dur-in) var(--ease);
}
.card:focus { outline: none; }
.card:hover,
.card:focus-visible {
transform: translateY(var(--lift-y));
box-shadow: var(--shadow-hover);
filter: saturate(1.02);
transition-duration: var(--dur-in);
}
.card:not(:hover):not(:focus-visible) {
transition-duration: var(--dur-out);
}
/* Motion safety */
@media (prefers-reduced-motion: reduce) {
.card { transition: none; }
}
</style>
Stretch goals
Add a subtle tilt (rotateX/rotateY) at max 1.5°.
Shadow color temperature warms slightly on hover.
Submission
Submit your CodePen/Repo link via the embedded form below. FORM LINK HERE