KQ-1260 · WISH/TAG
Wish Tag
A save-to-list toggle that hangs a paper price tag from the button's eyelet: saving swings it in from above the frame as a real pendulum on a recoil-family spring whose period and damping you set, with the tag hinged at its hole so it dangles a beat behind the string, while the counter rolls up on snap and bumps on recoil as the tag first comes taut. Removing snips the string, the stub curls back into the eyelet and the tag drops out of the frame on an ease-in fall. Space and Enter toggle it through the native click, aria-pressed reports saved, and a polite live region says what changed and the new count.
Ridgeline trail boot
Fernworks · Moss · UK 9
not on your list · 12 saved
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/wish-tag
Props
- pressed / defaultPressed
- boolean
- default false
- Saved state, controlled or initial; true is on the list.
- onPressedChange
- (saved: boolean) => void
- default —
- Fires from the press that changed it.
- price
- number
- default 129
- The amount the tag prints; the tag widens to fit it.
- format
- (price: number) => string
- default Intl.NumberFormat en-GB GBP
- How the price is printed.
- count
- number
- default 12
- Items on the list without this one; the counter shows one more while saved.
- showCount
- boolean
- default true
- Show the list counter.
- countLabel
- string
- default "saved"
- The word after the counter's number.
- saveLabel
- string
- default "Save"
- Text and accessible name while not saved.
- savedLabel
- string
- default "On your list"
- Text and accessible name while saved; the button keeps the wider label's width.
- compact
- boolean
- default false
- Icon only, with the label as the accessible name and the counter beside it.
- swing
- number
- default 0.5
- How loosely the tag swings, 0 to 1; the middle is the house recoil.
- stringLength
- number
- default 12
- From the eyelet to the tag's hole, in px.
- size
- "sm" | "md" | "lg"
- default "md"
- Button height 32, 40 or 48 px with the tag scaled to match.
- tagColor
- string
- default manila pigment from --warn
- The tag's paper; any CSS colour.
- accent
- string
- default "var(--accent-bright)"
- The saved tint and the tick; any CSS colour.
- sound
- boolean
- default false
- Paper as the tag comes taut, pop with the counter bump, a short snip on removal.
- disabled
- boolean
- default false
- Ignores presses and dims the button.
- className
- string
- default —
- Classes for the root.
| Prop | Type | Default | Description |
|---|---|---|---|
| pressed / defaultPressed | boolean | false | Saved state, controlled or initial; true is on the list. |
| onPressedChange | (saved: boolean) => void | — | Fires from the press that changed it. |
| price | number | 129 | The amount the tag prints; the tag widens to fit it. |
| format | (price: number) => string | Intl.NumberFormat en-GB GBP | How the price is printed. |
| count | number | 12 | Items on the list without this one; the counter shows one more while saved. |
| showCount | boolean | true | Show the list counter. |
| countLabel | string | "saved" | The word after the counter's number. |
| saveLabel | string | "Save" | Text and accessible name while not saved. |
| savedLabel | string | "On your list" | Text and accessible name while saved; the button keeps the wider label's width. |
| compact | boolean | false | Icon only, with the label as the accessible name and the counter beside it. |
| swing | number | 0.5 | How loosely the tag swings, 0 to 1; the middle is the house recoil. |
| stringLength | number | 12 | From the eyelet to the tag's hole, in px. |
| size | "sm" | "md" | "lg" | "md" | Button height 32, 40 or 48 px with the tag scaled to match. |
| tagColor | string | manila pigment from --warn | The tag's paper; any CSS colour. |
| accent | string | "var(--accent-bright)" | The saved tint and the tick; any CSS colour. |
| sound | boolean | false | Paper as the tag comes taut, pop with the counter bump, a short snip on removal. |
| disabled | boolean | false | Ignores presses and dims the button. |
| className | string | — | Classes for the root. |