KQ-1336 · BUNDLE/BUILDER

Bundle Builder

A bundle builder where picking a product sends its bag flying into the next empty compartment — a thrown arc, a straight glide or a lift and a drop — and the landing is the event: the bag squashes and recovers on recoil, the box dips and the lid hops, and only then do the lid's savings and the totals roll on snap. Bags can also be dragged 1:1 and thrown at the box, gliding in with the release velocity; taking one out flies it home to its shelf while the bags beside it slide over on glide, and the lid, hinged in perspective, closes in on snap as the box fills. Every product has a real toggle and every filled compartment a real remove button, so the keyboard reaches each end state, and landings are announced with the new total.

KQ-1336 · BUNDLE/BUILDER

Tasting box

Pick three single origins and save 15% on the box.

Box of 3
    0 bagsTotal $0.00.
    • Basin RidgeStone fruit, cocoa
      $18.00
    • Coldbrook ValleyBergamot, honey
      $21.00
    • Gauge PeakRed apple, caramel
      $19.00
    • Fern HollowJasmine, lime
      $24.00
    • Cinder MesaDark chocolate, molasses
      $17.00
    • Juniper FlatsBrown sugar, almond
      $16.00
    • Larch TerraceBlackcurrant, cedar
      $22.00
    • Waylight EstatePlum, black tea
      $23.00

    box empty · pick 3

    Install

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

    $ pnpm dlx shadcn@latest add @kinetiq/bundle-builder

    Props

    slots
    number
    default 3
    Compartments in the box, 2 to 5. A box that shrinks lets go of the bags that no longer fit.
    fly
    "arc" | "direct" | "drop"
    default "arc"
    How a picked bag travels: a thrown arc that turns with its direction, a straight glide, or a lift over the compartment and a fall into it with a bigger squash.
    savings
    "flat" | "tiered" | "free"
    default "flat"
    The deal the lid shows: percent off a full box, a discount that climbs with every bag to percent, or the cheapest bag free once the box is full.
    products
    BundleProduct[]
    default defaultBundleProducts
    The shelf: id, name, tasting notes, price, a label tint, roast 1 to 5, and stock (0 is sold out).
    value / defaultValue
    string[]
    default []
    The picked ids in compartment order. Controlled or uncontrolled.
    onValueChange
    (ids: string[]) => void
    default —
    Fires from the press, tap, drop or remove that changed the box. A controlled host that refuses a pick sees the bag fly home instead of landing.
    percent
    number
    default 15
    The full box's discount for flat, and the top of the ladder for tiered, in percent.
    onSubmit
    (ids: string[]) => void | Promise<unknown>
    default —
    The full box was added to the cart. A returned promise shows a pending ring, then Added with a check on flick, or Try again if it rejects.
    submitLabel
    string
    default "Add box to cart"
    The submit button's words; the total follows them.
    format
    (amount: number) => string
    default —
    Money. Defaults to Intl currency formatting in locale and currency.
    locale
    string
    default "en-US"
    Locale for the default money format.
    currency
    string
    default "USD"
    Currency for the default money format.
    status
    "ready" | "loading" | "error"
    default "ready"
    Whether the shelf has arrived: a skeleton while loading, a message and Try again on error.
    onRetry
    () => void
    default —
    Try again was pressed after the shelf failed to load.
    title
    string
    default "Tasting box"
    The builder's heading.
    subtitle
    string
    default —
    The line under the heading. Defaults to the offer in words, from slots, savings and percent.
    label
    string
    default —
    The region's accessible name. Defaults to the title.
    sound
    boolean
    default false
    A pop as a bag leaves the shelf or the box, a thock timed to its landing (higher as the box fills), softer and lower when it lands home or a full box refuses it.
    disabled
    boolean
    default false
    Look only: nothing can be picked, dragged, removed or added to the cart.
    className
    string
    default —
    Classes for the root; its scroller is at most 560px tall and flights are clipped to the root.