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`.

KQ-1590 · MELT/SWAP

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.