KQ-1375 · BUOYANT/LINE

Buoyant Line

Scroll-float text: as the line scrolls up through its container's viewport, its letters float up from below their places left to right, each on its own share of the scroll and its own spring — from springs.snap toward springs.drift as the lag grows, with a bob at the surface — swaying, tinted and faded while submerged, and they sink back if you scroll up. Progress is read from the nearest scroll container, or set directly, and the element's bottom edge is the waterline; the keyboard path is the container's own scrolling.

KQ-1375 · BUOYANT/LINE
Basinworks · entry 18

Morning readings from the north gauges came in level for the third day running.

Still water carries the whole basin

Every gauge we read sits on one reservoir, so a calm week upstream is a calm week at every tap downstream. The spillway stays shut.

Next readings at dawn. The north gauges report first, then the weir.

rising 0% · scroll the journal

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/buoyant-line

Props

text
string
default —
The line. Read once by assistive technology, and copied as written.
as
"h1" | "h2" | "h3" | "h4" | "p" | "span" | "div"
default "p"
The element the line renders as.
rise
number
default 1
How deep the letters start below their places, em.
lag
number
default 0.5
How strung out the rise is, 0 (together, brisk) to 1 (one after another, slow to follow).
range
number
default 0.5
How much of the viewport's height the line scrolls through while its letters rise, 0.1 to 1.
start
number
default 0.92
Where the rise begins, as a fraction of the viewport's height from its top (1 is the bottom edge).
sway
number
default 8
How far a submerged letter tilts as it sways, degrees.
bob
number
default 0.5
How much the letters bob as they reach the surface, 0 (none) to 1.
tint
string
default "var(--accent-bright)"
The colour a submerged letter takes before it surfaces into ink.
fade
boolean
default true
Fade the letters with depth.
progress
number
default —
Drive the rise yourself, 0 to 1; overrides scrolling.
once
boolean
default false
Once every letter has surfaced, stay surfaced.
scrollContainer
React.RefObject<HTMLElement | null>
default —
The scroll container to read; defaults to the nearest scrollable ancestor, or the window.
onProgressChange
(progress: number) => void
default —
The line's progress through its rise, 0 to 1 in steps of 0.01, as it changes.
onComplete
() => void
default —
Every letter has surfaced (again).
className
string
default —
Classes for the line element; size, weight and colour are inherited.