Hover micro-interactions
Eight small, production-safe hover effects for buttons, cards and links.
Everything animates transform, opacity or color — the properties the
compositor can move without repainting your page.
1. Lift with directed shadow
The shadow stays grounded while the button rises.
Deploy to prod
View CSS
.lift {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.lift:hover {
transform: translateY(-4px);
box-shadow:
0 12px 24px -10px rgba(37, 99, 235, 0.55), /* soft, far */
0 4px 8px -4px rgba(2, 6, 23, 0.6); /* tight, near */
}
.lift:active { transform: translateY(-1px); }
2. Center-out underline
scaleX from the middle — cheaper and nicer than animating width.
View CSS
.grow-underline { position: relative; }
.grow-underline::after {
content: '';
position: absolute;
left: 0; right: 0; bottom: 0;
height: 2px;
background: linear-gradient(90deg, #2563EB, #38BDF8);
transform: scaleX(0);
transform-origin: center; /* bloom outward */
transition: transform 0.25s ease;
}
.grow-underline:hover::after { transform: scaleX(1); }
3. Shine sweep
A skewed light band crosses the button once per hover.
Claim your invite
View CSS
.shine { position: relative; overflow: hidden; }
.shine::before {
content: '';
position: absolute;
top: 0; bottom: 0; left: -50%;
width: 50%;
background: linear-gradient(90deg,
transparent, rgba(241, 245, 249, 0.28), transparent);
/* animate transform, not `left`: stays on the compositor */
transform: skewX(-20deg) translateX(-100%);
transition: transform 0.55s ease;
}
.shine:hover::before {
transform: skewX(-20deg) translateX(420%);
}
4. Rotating gradient border
@property makes the conic angle animatable. Hover to spin.
View CSS
@property --angle {
syntax: '<angle>'; /* registered = animatable */
initial-value: 0deg;
inherits: false;
}
.border-spin {
border: 1px solid transparent;
border-radius: 10px;
background:
linear-gradient(#0F1626, #0F1626) padding-box,
conic-gradient(from var(--angle),
#2563EB, #38BDF8 25%, #1E293B 55%, #2563EB) border-box;
}
.border-spin:hover {
animation: rotate-border 2.4s linear infinite;
}
@keyframes rotate-border { to { --angle: 360deg; } }
5. Icon nudge
Only the arrow moves — small, directional, readable.
View CSS
.nudge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.nudge svg { transition: transform 0.2s ease; }
.nudge:hover svg { transform: translateX(4px); }
6. Subtle 3D tilt (no JS)
Move across the card — four hover zones drive the tilt.
3D tilt
quadrant hover zones, zero JS
View CSS
/* 4 invisible quadrants over the card; the sibling
combinator maps hovered zone -> tilt direction. */
.tilt { position: relative; perspective: 700px; }
.tilt .zone {
position: absolute;
width: 50%; height: 50%;
z-index: 2;
}
.tilt-body {
transform-style: preserve-3d;
transition: transform 0.25s ease;
}
.tilt-body strong { transform: translateZ(24px); }
.z1:hover ~ .tilt-body { transform: rotateX(7deg) rotateY(-7deg); }
.z2:hover ~ .tilt-body { transform: rotateX(7deg) rotateY(7deg); }
.z3:hover ~ .tilt-body { transform: rotateX(-7deg) rotateY(-7deg); }
.z4:hover ~ .tilt-body { transform: rotateX(-7deg) rotateY(7deg); }
7. Springy scale (linear() easing)
The scale overshoots and settles — a spring, in pure CSS.
Boing
View CSS
/* linear() samples a damped spring curve: values above 1
are the overshoot, later points settle back to rest. */
.springy {
transition: transform 0.6s linear(
0, 0.009 1.5%, 0.035 3%, 0.141 6.1%, 0.281 8.6%,
0.723 14.9%, 0.938 18.7%, 1.017 21.2%, 1.077 24.3%,
1.096 27.1%, 1.088 30.2%, 1.032 37.2%, 0.999 42.3%,
0.988 48%, 0.996 59.6%, 1.001 74.4%, 1
);
}
.springy:hover { transform: scale(1.07); }
.springy:active { transform: scale(0.97); }
8. Ghost fill
The outline button fills from the bottom edge.
Ghost fill
View CSS
.ghost {
position: relative;
isolation: isolate; /* traps z-index:-1 inside the button */
overflow: hidden;
background: transparent;
border: 1px solid #2563EB;
color: #38BDF8;
transition: color 0.25s ease;
}
.ghost::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background: #2563EB;
transform: scaleY(0);
transform-origin: bottom;
transition: transform 0.25s ease;
}
.ghost:hover { color: #fff; }
.ghost:hover::before { transform: scaleY(1); }