vaweGitHub

MovesReveal a title

Word swap slot

look: dusk1.8 sword-swap-slot.md

Word swap slot: final frame of the text reveal animation
Tap the clip to play it.

When to use

Use when a held sentence has one open word that changes value: "We make films that feel fast, loud, clean, alive". The word sits in a slot, rolls up through 3 or 4 values and lands on the last one, and the sentence reflows its width as the slot grows and shrinks. Reference: HyperFrames kinetic-type-swap. Clip: word-swap-slot.mp4. Demo: demo/word-swap-slot.html.

The code

css
.slot { position: relative; display: inline-block; vertical-align: baseline;
        clip-path: inset(-0.35em -0.1em -0.35em -0.1em); }   /* window = one 1.7em row */
.col { position: absolute; left: 0; top: -0.35em; }
.col b { display: block; height: 1.7em; line-height: 1.7em; font-weight: inherit; }

Easing and timing

The clip runs 1.8 s. The look is dusk.

The clip uses 2 named eases from core/motion/presets.js. Each is a pair of After Effects handles: influence is how far along the segment the handle reaches, and speed is how fast the value moves at the key, as a multiple of the segment's average speed. Motion in the docs explains both.

EaseLeaving a keyArriving at a key
pop12% influence, speed 4.835% influence, speed -1.5
swap75% influence, speed 075% influence, speed 0

Notes

js
const w = words.map((b) => { const r = document.createRange(); r.selectNodeContents(b); return r.getBoundingClientRect().width / px; });
const seg = [380, 380, 650], total = 1410, at = [0, 0.27, 0.54, 1];   // cumulative share of total
const easing = [EASE.swap, EASE.swap, EASE.pop];
const opts = { delay: 200, duration: total, fill: 'both', easing: 'linear' };
col.animate(at.map((o, i) => ({ offset: o, translate: `0 ${-i * 1.7}em`, easing: easing[i] })), opts);
slot.animate(at.map((o, i) => ({ offset: o, width: `${w[i]}em`, easing: easing[i] })), opts);

Each word is measured with a Range after await document.fonts.load(...), so the width in em is exact. <span> in the slot holds a zero-width space so the slot has a baseline.

The numbers that make it look expensive

  • Each word dwells about 0.25 s and then rolls: the roll easing EASE.swap is slow at the start and fast at the end, so the eye reads every value.
  • The last segment uses EASE.pop: the final word drops in, dips 8 percent past its row and settles. The last word takes the accent colour.
  • The width animates on the same keyframes and the same easing as the roll, so the words after the slot (or the centred sentence) move with the word, never before or after it.
  • A small blur (0.05em) peaks at the middle of each roll and is gone at rest.

The common mistake

Using overflow: hidden on the slot. It moves the baseline to the bottom edge of the box and cuts descenders. Use clip-path: inset() with a window as tall as one row (1.7em) and rows spaced by the same 1.7em, so the neighbour is never visible at rest and no glyph is cut. And never swap the text in place with a cross-fade: the roll is the whole move.

Looks (ground, type, radius, accent) live in demo/demo.css; this snippet keeps neutral tokens.

Sound

Sound: pluck at 1.15 s into the move, when the last word lands (default gain); no tick per value.