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.

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.

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.

Gradient border
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.

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.

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); }