KQ-1590 · MELT/SWAP
Melt Swap
A rotating word that swaps by melting: letters the next word does not share sag to the baseline and soften into glossy beads, the beads slide along the baseline and pool where the new letters stand, and the new letters rise out of the pools and firm up with a slight wobble, while letters the two words share keep their substance and glide across on the glide spring. The liquid is one SVG goo filter lit by `gloss`, its springs drawn from glide and recoil and thickened by `viscosity`, in a box reserved to the widest word so the line never shifts. It swaps on a timer, on hover or focus, or when a host sets `value`.
Coldbrook · Single-origin bars
Chocolate,
melted
by hand.
word 1 of 4 · melted
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/melt-swap
Props
- words
- readonly string[]
- default ["melted", "tempered", "poured", "set"]
- The words it swaps between, in order.
- value / defaultValue
- number
- default 0
- The index of the word shown; a new value pours into that word.
- onValueChange
- (index: number) => void
- default —
- Fires as a swap begins — from the timer, a hover or a host — with the new index.
- interval
- number
- default 2400
- Rest between automatic swaps in ms, before pace; 0 swaps only on hover or a new value.
- viscosity
- number
- default 0.5
- How thick the liquid is, 0 to 1: runny beads race and wobble, thick ones crawl and firm up composed.
- gloss
- number
- default 0.6
- How wet the molten letters look: matte to a bright highlight with shaded edges.
- pace
- number
- default 1
- Speed of the whole pour, 0.25 to 2; the rest between swaps scales with it.
- stagger
- number
- default 36
- Delay between letters starting to soften, in ms, from the middle of the change outward.
- delay
- number
- default 600
- Wait before the first swap once triggered, in ms.
- trigger
- "mount" | "inView" | "hover" | "manual"
- default "inView"
- What starts the swaps: on mount, in view, one per hover or focus, or only a new value.
- once
- boolean
- default false
- Stop at the last word instead of wrapping round.
- paused
- boolean
- default false
- Hold the current word; a swap under way finishes.
- align
- "start" | "center" | "end"
- default "center"
- Where each word sits in the box reserved for the widest.
- liquidColor
- string
- default "currentColor"
- The molten letters' colour; they cool to the text colour as they firm.
- announce
- boolean
- default false
- Announce each settled word politely to screen readers.
- as
- React.ElementType
- default "span"
- The element to render.
- onComplete
- (index: number) => void
- default —
- Fires when a swap has settled, with the index now shown.
- className
- string
- default —
- Classes for the word's box; font size and colour inherit.
| Prop | Type | Default | Description |
|---|---|---|---|
| words | readonly string[] | ["melted", "tempered", "poured", "set"] | The words it swaps between, in order. |
| value / defaultValue | number | 0 | The index of the word shown; a new value pours into that word. |
| onValueChange | (index: number) => void | — | Fires as a swap begins — from the timer, a hover or a host — with the new index. |
| interval | number | 2400 | Rest between automatic swaps in ms, before pace; 0 swaps only on hover or a new value. |
| viscosity | number | 0.5 | How thick the liquid is, 0 to 1: runny beads race and wobble, thick ones crawl and firm up composed. |
| gloss | number | 0.6 | How wet the molten letters look: matte to a bright highlight with shaded edges. |
| pace | number | 1 | Speed of the whole pour, 0.25 to 2; the rest between swaps scales with it. |
| stagger | number | 36 | Delay between letters starting to soften, in ms, from the middle of the change outward. |
| delay | number | 600 | Wait before the first swap once triggered, in ms. |
| trigger | "mount" | "inView" | "hover" | "manual" | "inView" | What starts the swaps: on mount, in view, one per hover or focus, or only a new value. |
| once | boolean | false | Stop at the last word instead of wrapping round. |
| paused | boolean | false | Hold the current word; a swap under way finishes. |
| align | "start" | "center" | "end" | "center" | Where each word sits in the box reserved for the widest. |
| liquidColor | string | "currentColor" | The molten letters' colour; they cool to the text colour as they firm. |
| announce | boolean | false | Announce each settled word politely to screen readers. |
| as | React.ElementType | "span" | The element to render. |
| onComplete | (index: number) => void | — | Fires when a swap has settled, with the index now shown. |
| className | string | — | Classes for the word's box; font size and colour inherit. |