KQ-1132 · GLIDE/CARET
Glide Caret
A native textarea whose caret glides: its place is read from a mirror of the text, a head springs there at the chosen stiffness while a softer, critically damped tail follows, and the caret is drawn as a solid bar with a translucent streak between the two that thins with distance and shrinks back to a bar as the tail catches up. Selecting text draws one highlight per line that springs out from the caret's column, rippling outward on the house cascade, with the caret riding the active end. Everything else — typing, selection, IME, paste, undo, spellcheck, the keyboard — is the textarea's own, and the native caret returns during composition and in forced colours.
Morning reading
Fieldline · 07:40field note · 19 words
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/glide-caret
Props
- label
- string
- default —
- What the text is. Shown above the field and tied to the textarea by id.
- value / defaultValue
- string
- default ""
- The text. Controlled or uncontrolled.
- onValueChange
- (value: string) => void
- default —
- Fires on every edit, with the new text.
- placeholder
- string
- default —
- The textarea's placeholder.
- rows
- number
- default 4
- Visible lines, 2 to 12. The textarea scrolls past them, and the caret and selection move with the text.
- name
- string
- default —
- The form field name, passed to the textarea.
- stiffness
- number
- default 600
- The caret's spring, 150 to 1200: floaty and lagging at the low end, tight at the top. The selection's growing edge uses it too.
- stretch
- number
- default 0.5
- How far the caret's tail lags, 0 to 1: at 0 a plain bar glides; toward 1 it trails a long streak.
- width
- number
- default 2
- The caret's thickness in px, 1 to 4.
- blink
- "smooth" | "step" | "none"
- default "smooth"
- How the caret blinks at rest: a breathing fade, the classic hard on and off, or solid. It restarts solid after every move and runs on the compositor.
- sound
- boolean
- default false
- Faint taps as the caret lands: a tick per character with its own small pitch, a lower one for deletions, a detent for a new line and a lighter tick for a click or arrow jump, panned by the caret's place. Nothing plays, and no audio context exists, unless this is on.
- disabled
- boolean
- default false
- Disables the textarea; the drawn caret never shows.
- className
- string
- default —
- Merged onto the root's classes.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | What the text is. Shown above the field and tied to the textarea by id. |
| value / defaultValue | string | "" | The text. Controlled or uncontrolled. |
| onValueChange | (value: string) => void | — | Fires on every edit, with the new text. |
| placeholder | string | — | The textarea's placeholder. |
| rows | number | 4 | Visible lines, 2 to 12. The textarea scrolls past them, and the caret and selection move with the text. |
| name | string | — | The form field name, passed to the textarea. |
| stiffness | number | 600 | The caret's spring, 150 to 1200: floaty and lagging at the low end, tight at the top. The selection's growing edge uses it too. |
| stretch | number | 0.5 | How far the caret's tail lags, 0 to 1: at 0 a plain bar glides; toward 1 it trails a long streak. |
| width | number | 2 | The caret's thickness in px, 1 to 4. |
| blink | "smooth" | "step" | "none" | "smooth" | How the caret blinks at rest: a breathing fade, the classic hard on and off, or solid. It restarts solid after every move and runs on the compositor. |
| sound | boolean | false | Faint taps as the caret lands: a tick per character with its own small pitch, a lower one for deletions, a detent for a new line and a lighter tick for a click or arrow jump, panned by the caret's place. Nothing plays, and no audio context exists, unless this is on. |
| disabled | boolean | false | Disables the textarea; the drawn caret never shows. |
| className | string | — | Merged onto the root's classes. |