KQ-1702 · ADD/ON/TRAY

Add-on Tray

An add-on picker: toggle cards above, beside or under a running total tray. Turning a card on drops its chip into the tray on a small arc — one progress value on springs.glide carries it from the card's price to its place, x straight and y lifted — and it lands with two small bounces on springs.recoil as the monthly total rolls to its new value on odometer wheels on springs.snap; turning it off lifts the chip back out toward its card while the tray closes up on glide. Cards are role="checkbox" buttons with two-dimensional arrow-key travel; incompatible add-ons stay focusable, say why in place of their description and bring the reason forward when pressed, prices shimmer while they load, and narrow containers dock the tray at the bottom.

KQ-1702 · ADD/ON/TRAY

Add-ons

Fieldline · Team plan, billed monthly

1 add-on on
Monthly totalTeam plan · …
  • Extra storage…

prices loading · fieldline team

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/add-on-tray

Props

addons
AddOn[]
default defaultAddOns
The add-ons offered: { id, name, description, price, icon?, conflicts?, requires?, note? }.
value / defaultValue
string[]
default — / ["storage"]
The ids turned on, controlled or initial.
onValueChange
(ids: string[]) => void
default —
Fires from the press or key that changed the selection, with every id on.
onToggle
(id: string, on: boolean) => void
default —
One add-on was turned on or off.
onBlocked
(id: string, reason: string) => void
default —
An add-on that cannot be added was pressed, with the reason it shows.
onCheckout
(order: { ids: string[]; total: number }) => void
default —
Checkout was pressed, with the ids and the monthly total.
base
{ name: string; price: number }
default defaultAddOnBase
The plan the add-ons sit on; its price starts the total.
pricesLoading
boolean
default false
Prices are on their way: they shimmer (only while on screen) and checkout is held.
cardStyle
"outline" | "tile" | "row"
default "outline"
Bordered cards with a round check, filled tiles with a large icon and a price pill, or compact list rows with a square check.
trayPosition
"bottom" | "side" | "float"
default "bottom"
A flush panel under the cards, a receipt column beside them, or a floating bar; narrow containers dock every one at the bottom.
accent
"cobalt" | "signal" | "amber" | "graphite" | string
default "cobalt"
Checks, chips, the flight and the chosen cards' wash: a named accent or any CSS colour.
formatPrice
(amount: number) => string
default US dollars
Formats prices; the total's symbol is read from formatPrice(0), so other currencies roll correctly.
period
string
default "mo"
What the prices are per.
title
string
default "Add-ons"
The heading.
checkoutLabel
string
default "Checkout"
The checkout button's word.
status
"ready" | "loading" | "error"
default "ready"
Whether the add-ons have arrived: placeholder cards while loading, a retry on error.
onRetry
() => void
default —
Try again was pressed after the add-ons failed to load.
disabled
boolean
default false
Holds every card and checkout.
className
string
default —
Classes for the surface; give it a height and the cards scroll above the tray.