KQ-1606 · CAPILLARY/CURSOR
Capillary Cursor
Inside its own box the pointer is a single drop of ink, exactly at the pointer and drawn out a little along fast travel. Touch a button, link or field and the ink wicks along the control's real outline — its border box and its own four corner radii — from the point of contact in both directions, the fronts slowing as capillary flow does, then seeps inward from the wet edge at low strength until the control is filled in its exact shape, while the drop shrinks by the ink it gave; the drop's size eases on a rate as quick as springs.flick and the flood on a glide-like ease. Leave and the flood recedes and the outline drains back toward the exit point through a thread of ink that thins as it pulls and pinches off past its reach, and the drop swells again. Keyboard focus moves the ink from one control to the next, and the system cursor returns over text fields and on any key.
Fieldline beta
Read the field notesTrail maps that sync before you lose signal. Forty places left this month.
ink · in the drop · touch a control
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/capillary-cursor
Props
- children
- ReactNode
- default —
- The area the drop lives in; its buttons, links and fields keep every click, focus and key.
- inkColor
- "cobalt" | "iron" | "sepia" | "carmine" | string
- default "cobalt"
- The ink: a named ink or any CSS colour.
- wickSpeed
- number
- default 520
- How fast the ink runs along an outline, px per second.
- dotSize
- number
- default 14
- The drop's diameter when it is full, px.
- strength
- number
- default 0.16
- How strongly the ink floods the inside of a control, 0 to 1.
- lineWidth
- number
- default 1.75
- The wicked outline's thickness, px.
- flood
- boolean
- default true
- Fill the inside of a control, not only its outline.
- stretch
- number
- default 0.5
- How far the drop draws out when it moves fast, 0 to 1.
- cling
- number
- default 0.5
- How far the draining thread stretches before it lets go, 0 to 1.
- targets
- string
- default "button, a[href], [role='button'], input, select, textarea, [data-ink]"
- What the ink fills, as a CSS selector.
- hideCursor
- boolean
- default true
- The drop replaces the system cursor inside the box (never over text fields, never after a key).
- followFocus
- boolean
- default true
- Keyboard focus moves the ink to the focused control.
- interactive
- boolean
- default true
- The pointer leads the drop (mouse and pen; a tap inks the tapped control).
- onTargetChange
- (target: HTMLElement | null) => void
- default —
- The control holding the ink changed: that control, or null.
- className
- string
- default —
- Classes for the box.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | The area the drop lives in; its buttons, links and fields keep every click, focus and key. |
| inkColor | "cobalt" | "iron" | "sepia" | "carmine" | string | "cobalt" | The ink: a named ink or any CSS colour. |
| wickSpeed | number | 520 | How fast the ink runs along an outline, px per second. |
| dotSize | number | 14 | The drop's diameter when it is full, px. |
| strength | number | 0.16 | How strongly the ink floods the inside of a control, 0 to 1. |
| lineWidth | number | 1.75 | The wicked outline's thickness, px. |
| flood | boolean | true | Fill the inside of a control, not only its outline. |
| stretch | number | 0.5 | How far the drop draws out when it moves fast, 0 to 1. |
| cling | number | 0.5 | How far the draining thread stretches before it lets go, 0 to 1. |
| targets | string | "button, a[href], [role='button'], input, select, textarea, [data-ink]" | What the ink fills, as a CSS selector. |
| hideCursor | boolean | true | The drop replaces the system cursor inside the box (never over text fields, never after a key). |
| followFocus | boolean | true | Keyboard focus moves the ink to the focused control. |
| interactive | boolean | true | The pointer leads the drop (mouse and pen; a tap inks the tapped control). |
| onTargetChange | (target: HTMLElement | null) => void | — | The control holding the ink changed: that control, or null. |
| className | string | — | Classes for the box. |