KQ-1496 · SLINGSHOT
Slingshot
An action button held in an elastic band between the two pegs of a fork: pulling it back follows the hand 1:1 while the band's strands wrap its back in a pouch, thin and warm with the stretch, and a faint aim line reaches to the target, whose ring pops on springs.snap once the draw passes the threshold. Let go armed and the band fires the button home on a stiff underdamped spring that carries the release velocity; the frame it passes rest the icon leaves, flies along the aim line and settles into the target with two bounces, while a short pull just springs back on springs.snap. Enter launches with a full draw on springs.glide, Space held draws and fires on release, and Escape cancels a draw.
Build 482 · production
Gaugeworks
ready · pull back or press enter
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/slingshot
Props
- label
- string
- default "Launch"
- The button's text and accessible name.
- launchedLabel
- string
- default label
- The text while launched; both labels share one cell, so the button's width never changes.
- icon
- ReactNode
- default a horizontal paper plane
- The projectile: what flies out of the button into the target.
- launched / defaultLaunched
- boolean
- default — / false
- Whether the shot is out, controlled or uncontrolled. Controlled, the icon waits in its target until the host sets it false.
- onLaunchedChange
- (launched: boolean) => void
- default —
- Fires from the release or key that launched it, and from the uncontrolled reload.
- onLaunch
- () => void
- default —
- Fires once per launch, at the moment of release.
- resetAfter
- number
- default 1400
- Uncontrolled: how long the icon stays out before the slingshot reloads, in ms; 0 keeps it out.
- tension
- number
- default 0.5
- The band's stiffness, 0 to 1: thicker at rest, harder to draw past the limit, faster to fire.
- pull
- number
- default 72
- The longest draw in px; past it the band rubber-bands. A narrow box shortens it so the lane ahead keeps room.
- distance
- number
- default 140
- How far the icon flies to its target in px, clamped to the room ahead.
- threshold
- number
- default 0.35
- The share of the full draw that arms a launch; a shorter pull springs back.
- aim
- number
- default 14
- How far a shot can be angled up or down by pulling the other way, in degrees; 0 keeps it level.
- color
- string
- default "var(--accent-bright)"
- The projectile, the aim line and the target; any CSS colour.
- bandColor
- string
- default "var(--ink-3)"
- The band at rest; any CSS colour.
- tautColor
- string
- default "var(--warn)"
- The band at full stretch, mixed in oklab as it is drawn; any CSS colour.
- size
- "sm" | "md" | "lg"
- default "md"
- Button height (32, 40 or 48px) and the rig's height (112, 132 or 152px).
- sound
- boolean
- default false
- Play the band's twang on release (pitch and level by the draw) and a whoosh as the icon leaves; only ever in answer to a drag or key.
- disabled
- boolean
- default false
- Disables the drag and the keys; the button stays in the tab order as aria-disabled.
- className
- string
- default —
- Classes for the root, which fills its width and centres the rig.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | "Launch" | The button's text and accessible name. |
| launchedLabel | string | label | The text while launched; both labels share one cell, so the button's width never changes. |
| icon | ReactNode | a horizontal paper plane | The projectile: what flies out of the button into the target. |
| launched / defaultLaunched | boolean | — / false | Whether the shot is out, controlled or uncontrolled. Controlled, the icon waits in its target until the host sets it false. |
| onLaunchedChange | (launched: boolean) => void | — | Fires from the release or key that launched it, and from the uncontrolled reload. |
| onLaunch | () => void | — | Fires once per launch, at the moment of release. |
| resetAfter | number | 1400 | Uncontrolled: how long the icon stays out before the slingshot reloads, in ms; 0 keeps it out. |
| tension | number | 0.5 | The band's stiffness, 0 to 1: thicker at rest, harder to draw past the limit, faster to fire. |
| pull | number | 72 | The longest draw in px; past it the band rubber-bands. A narrow box shortens it so the lane ahead keeps room. |
| distance | number | 140 | How far the icon flies to its target in px, clamped to the room ahead. |
| threshold | number | 0.35 | The share of the full draw that arms a launch; a shorter pull springs back. |
| aim | number | 14 | How far a shot can be angled up or down by pulling the other way, in degrees; 0 keeps it level. |
| color | string | "var(--accent-bright)" | The projectile, the aim line and the target; any CSS colour. |
| bandColor | string | "var(--ink-3)" | The band at rest; any CSS colour. |
| tautColor | string | "var(--warn)" | The band at full stretch, mixed in oklab as it is drawn; any CSS colour. |
| size | "sm" | "md" | "lg" | "md" | Button height (32, 40 or 48px) and the rig's height (112, 132 or 152px). |
| sound | boolean | false | Play the band's twang on release (pitch and level by the draw) and a whoosh as the icon leaves; only ever in answer to a drag or key. |
| disabled | boolean | false | Disables the drag and the keys; the button stays in the tab order as aria-disabled. |
| className | string | — | Classes for the root, which fills its width and centres the rig. |