# Badge trail

Tiny coloured badges of design terms that slam in behind the pointer and flick away. Live at https://design.how/vault#badge-trail.

Install the skill, which carries the template, the brief and a check script:

```sh
npx skills add https://design.how/vault/badge-trail
```

## What it is

A plain white card. As the pointer moves, tiny badges drop behind it: an uppercase design term in black monospace at 11px on a vivid fill, 1px of vertical and 4px of horizontal padding, a 3px radius. Each lives 830ms. It slams in over 110ms from 5px back along the direction of travel, peaking at 3 percent over full size, holds for 480ms with a sub-pixel wobble and a 1 percent breath, then flicks out over 240ms, shrinking and turning 4 to 9 degrees. Its letters flicker through random characters for the first 130ms and lock left to right.

Badges never overlap. A candidate that would come within 3px of a live badge is skipped for that tick, and a badge drops only when 45ms and 14px have passed since the last, so a still pointer stacks nothing. Each new badge nudges neighbours within 64px up to 3px away; the push eases in and relaxes slowly, so the trail jostles instead of jumping. At most 22 are alive, older ones sit up to 6 percent smaller and 12 percent dimmer, and every fifteenth is an outline in its colour.

The colour walks. A hue advances 24 degrees per badge, so neighbours harmonise, and the pointer's speed sets saturation and lightness: a slow drag lays pale badges, a fast flick lays vivid ones. The words come from a shuffled bag of 24 terms, so none repeats until all have been used.

When nobody is pointing, a ghost cursor takes over 600ms after the pointer leaves, easing in over 420ms onto a broad loop with a faster, off-ratio doodle on each axis, so the card is alive before anyone reaches it. Every length is in design pixels at a 600px card and scales up with a wider one.

## How it works

- Keep the badges in a plain array in a requestAnimationFrame loop and hand a copy to the view each frame; draw each as an absolutely placed span.
- Each frame, follow the real pointer, or after GHOST_IDLE_MS the ghost loop blended in from where the pointer left.
- Drop a badge centred on the point only after DROP_MS and MIN_TRAVEL, clamped inside the card, and skip it if its box inflated by GAP hits a live one.
- Colour it from a hue walker, with saturation and lightness from the point's speed, and shove live neighbours by raising a target their offset chases.
- Drive opacity, scale, rotation and offset from the badge's age alone: slam, hold with a wobble, flick out.
- Measure the monospace advance from the live face once, so the overlap test fits any font.
- Under reduced motion, show four still badges centred and never start the loop.

## What you can change

- `WORDS` (24 design terms): The vocabulary on the badges, in badges.ts.
- `COLORS` (10 vivid fills): The fills for the reduced-motion still, in badges.ts.
- `GROUND` (#ffffff): The card colour.
- `FONT_PX` (11): The badge text size in design pixels.
- `DROP_MS, MIN_TRAVEL` (45, 14): How often and how far apart badges drop.
- `APPEAR_MS, HOLD_MS, FADE_MS` (110, 480, 240): The three phases of a badge's life.
- `MAX_LIVE` (22): The most badges on the card at once.
- `DESIGN_WIDTH` (600): The card width the lengths are tuned at; wider cards scale up.
- `FONT_VAR` (--visual-mono): The CSS variable holding the monospace family.

Last updated: 2026-11-02
