Skip to content
Foundationsstable

Motion

Motion should feel like a dancer’s bounce — quick, springy, never slow or floaty. It confirms what happened; it never makes you wait.

Principles

  • Feedback first

    Animate the result of an action — a press, a save, a selection. Don’t animate to decorate.

  • One showpiece per screen

    Ambient loops (breathing, shimmer) belong to the single main CTA. Everything else stays still at rest.

  • Respect reduced motion

    Every loop and large movement switches off under prefers-reduced-motion.

The brand CTA

Hover, press and watch: breathing gradient, shimmer sweep, springy lift, press-down and a click ripple — all from one class. Its timings use the tokens below.

vue
<button class="wd-cta inline-flex items-center gap-2 px-6 py-3 rounded-full text-white text-sm font-bold uppercase tracking-wider">
  Find your next festival
</button>

Durations

Four steps. Anything longer than slow is either an ambient loop (only on the brand CTA) or a bug.

Instant80msduration-wd-instantvar(--wd-duration-instant)Press feedback (active state)
Quick150msduration-wd-quickvar(--wd-duration-quick)Colour and border changes on hover. Same as Tailwind’s default transition duration.
Standard220msduration-wd-standardvar(--wd-duration-standard)Lift, scale, small movements
Slow380msduration-wd-slowvar(--wd-duration-slow)Ripples, entrances, panels opening

Ambient loops on the brand CTA run 4.5–6s and the gradient shift 800ms; they stay literal inside .wd-cta because nothing else should use them.

Easing

Springease-wd-springvar(--wd-ease-spring) · cubic-bezier(0.34, 1.56, 0.64, 1)Playful overshoot — lifts and pops
Outease-wd-outvar(--wd-ease-out) · ease-outThings arriving or settling. The CSS keyword, not Tailwind’s ease-out curve.
In-outease-wd-in-outvar(--wd-ease-in-out) · ease-in-outLoops that go back and forth

See them side by side

Each dot travels the same distance with a different duration and the spring easing.

Instant
Quick
Standard
Slow

Using the tokens

html
<!-- Tailwind -->
<button class="transition-transform duration-wd-standard ease-wd-spring hover:-translate-y-0.5">…</button>
<a class="transition-colors duration-wd-quick hover:text-primary">…</a>

/* CSS */
.thing {
  transition: transform var(--wd-duration-standard) var(--wd-ease-spring);
}
.thing:active { transition-duration: var(--wd-duration-instant); }

Reduced motion

Required for every looping or large animation.

css
@media (prefers-reduced-motion: reduce) {
  .wd-cta,
  .wd-cta::before {
    animation: none !important;
  }
}

<!-- Tailwind -->
<div class="transition-transform duration-wd-standard motion-reduce:transition-none">…</div>