KQ-1251 · FOLLOW/KNOT

Follow Knot

A follow button whose plus is one rope: pressed, its free end sweeps round on a tween into a loop that crosses over the standing part, and the loop cinches into a knot at the vertex of a check on the snap spring, thrown hard enough by tension that its one overshoot reads as the knot pulling past tight. The label slides from Follow to Following on snap inside a slot as wide as the longest word, and the follower figure rolls one on, units first; hovering or keyboard-focusing a followed button loosens and turns the knot and offers Unfollow in danger ink, and a press unties it rope-first before the words and figure follow. It is a button with aria-pressed and a constant name, Space and Enter press it, and under reduced motion the rope swaps between plus and check with a short fade while the digits change in place.

KQ-1251 · FOLLOW/KNOT

Fieldline Journal

Weather and soil notes from the field

not following · 1,299 followers

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/follow-knot

Props

pressed / defaultPressed
boolean
default false
Whether the viewer follows, controlled or initial.
onPressedChange
(pressed: boolean) => void
default —
Fires from the press that changed it, with the new state; the rope moves once the state actually changes, so a refused press never shows a false knot.
label
string
default "Follow"
The word before following, and the start of the accessible name.
pressedLabel
string
default "Following"
The word once following; the slot keeps the longest word's width.
releaseLabel
string
default "Unfollow"
The word while a followed button is hovered or keyboard-focused: what a press will do.
target
string
default —
Who or what is followed, appended to the accessible name ("Follow Fieldline Journal").
count
number
default —
Followers without this viewer; the button adds one while followed. Omit to hide the figure.
formatCount
(count: number) => string
default en-US grouping
How the figure is written; digits roll, other characters swap.
describeCount
(count: number) => string
default "1,300 followers"
The figure as spoken, read as the button's description.
tension
number
default 0.5
How hard the knot is pulled, 0 to 1: a loose eye that settles softly, or a hard bead that snaps past tight.
untieAngle
number
default 8
How far the knot turns as it loosens under a hover, in degrees.
tieDuration
number
default 240
How long the free end takes to sweep its loop, in ms; the cinch starts 60% of the way in.
previewDelay
number
default 80
How long a pointer rests on a followed button before it offers to unfollow, in ms.
compact
boolean
default false
Icon only: the knot alone in a round button, the words in its accessible name.
size
"sm" | "md" | "lg"
default "md"
32, 36 or 44px tall.
accent
string
default "var(--accent)"
The unfollowed pill's fill, any CSS colour.
accentInk
string
default "var(--primary-foreground)"
Words and rope on that fill.
danger
string
default "var(--danger)"
Ink of the unfollow preview.
sound
boolean
default false
Play a tick as the free end crosses over and a snap as the knot pulls tight; only ever for the visitor's own press.
disabled
boolean
default false
Not pressable, drawn at half strength.
className
string
default —
Classes for the button.