← Back to gallery
CSS

Elastic Text Custom Easing Tradeoffs

A transform-only text snap showcase that uses bounded CSS cubic-bezier overshoot while documenting the boundary where true spring physics should move to JavaScript.

elasticspring-easecustom-easingtransformprefers-reduced-motion

Elastic snap · cubic-bezier boundary

Elastic Text Custom Easing

Word-level transforms use a bounded overshoot curve to create elastic text snap without turning the pattern into a JavaScript spring simulation. Three variants demonstrate a tight product badge, a staggered headline, and a subtle toast confirmation.

Tight rebound · short label

Product Badge

A compact badge snaps into place with one visible overshoot and no layout-affecting width change. The transform is isolated to the text run; surrounding layout never recalculates during the snap.

  • cubic-bezier
  • overshoot
  • transform

Word stagger · readable dwell

Headline Snap

Each word resolves in sequence, giving a headline a tactile entrance while the baseline stays stable. Word-level spans (not per-character) keep the phrase readable during the entrance.

  • word stagger
  • baseline stable
  • layout safe

Subtle rebound · feedback tone

Toast Confirmation

A small confirmation label uses a reduced overshoot so it reads as feedback rather than a playful bounce. Safer boundary for production UI: one ease curve, transform-only motion, no repeated rebound.

  • feedback
  • single rebound
  • reduce-safe

Elastic inspector

Product Badge

  • cubic-bezier
  • overshoot
  • transform

A compact badge snaps into place with one visible overshoot and no layout-affecting width change. The transform is isolated to the text run; surrounding layout never recalculates during the snap.

.elastic-phrase {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25em;
  color: #e0f2fe;
  text-shadow: 0 0 22px rgba(103, 232, 249, 0.14);
}

.elastic-word {
  display: inline-block;
  transform-origin: 50% 100%;
  /* Bounded overshoot via cubic-bezier — no JS spring solver needed. */
  animation: elastic-snap 2.60s cubic-bezier(0.22, 1.45, 0.36, 1)
    infinite both;
  animation-delay: calc(var(--elastic-index, 0) * 90ms);
}

@keyframes elastic-snap {
  0%   { opacity: 0; transform: translateY(18px) scale(0.92); }
  35%  { opacity: 1; transform: translateY(-5px) scale(1.05); }
  55%  { opacity: 1; transform: translateY(0) scale(1); }
  88%  { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-6px) scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  .elastic-word {
    animation: none;
    opacity: 1;
    transform: none;
  }
}