KQ-1677 · PARCEL/SPEC
Parcel Spec
A shipping form beside an isometric box drawn to scale on a floor of squares: length, width and height are springs, so typing a dimension stretches that edge on springs.snap with one crisp overshoot, switching parcels glides the box to the other parcel's shape on springs.glide, and a box that would outgrow the stage zooms the drawing out on glide while the floor's squares grow to say so. The dial's needle swings to the weight on snap against the chosen service's limit, and each service's balance tips on springs.recoil when a parcel is too heavy for it, greying the service with its reason; quotes from onQuote shimmer while they load and roll in on snap. Fields take any unit ("12 in", "1'6\"", "500 g") and settle into the shown one, Up and Down nudge by one (Shift ten, Alt a tenth), parcels are a tablist where Delete removes one, and services are a radiogroup whose arrows skip the ones that can't go.
Ship to Halden Street
2 parcels · 7.7 kg
parcel 1 · 40 × 30 × 20 cm · 6.5 kg · quoting…
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/parcel-spec
Props
- packages / defaultPackages
- ParcelSpecPackage[]
- default — / defaultParcelSpecPackages
- Parcels in cm and kg: { id, contents?, length, width, height, weight }, controlled or initial.
- onChange
- (packages) => void
- default —
- Fires from the keystroke, nudge or button that changed a parcel.
- onActivePackageChange
- (packageId) => void
- default —
- The parcel being drawn changed (a tab, a new parcel, a removal).
- services
- ParcelSpecService[]
- default defaultParcelSpecServices
- { id, name, eta, maxWeight (kg), maxLength (cm), base, perKg }.
- service / defaultService
- string
- default — / "standard"
- The chosen service, controlled or initial.
- onServiceChange
- (serviceId) => void
- default —
- Fires from the click or arrow key that picked a service.
- onQuote
- (packages) => Record | Promise<Record<string, number | null>>
- default —
- Prices the parcels per service (null where it can't go); a promise shimmers until it settles. Omit it and prices come from each service's rates.
- unitSystem
- "metric" | "imperial"
- default "metric"
- The units fields, callouts, limits and floor squares are shown in; typed units always convert.
- boxStyle
- "kraft" | "wire" | "tint"
- default "kraft"
- Cardboard faces with a tape strip, edges only with hidden edges dashed, or accent-tinted glass.
- accent
- string
- default "var(--accent-bright)"
- The chosen service, the needle, callouts and focus — any CSS colour; buttons take its hue at a fixed lightness so white text reads in both themes.
- maxPackages
- number
- default 4
- Most parcels in one shipment.
- volumetricDivisor
- number
- default 5000
- Volumetric weight is L × W × H in cm over this, in kg; the charge uses the greater weight.
- heading
- string
- default "Ship to Halden Street"
- The panel's heading and accessible name.
- currency
- string
- default "USD"
- The quotes' currency.
- locale
- string
- default "en-US"
- The quotes' locale.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading shows a skeleton stage and fields; error shows Try again.
- onRetry
- () => void
- default —
- Try again was pressed after the shipment failed to load.
- height
- number
- default 540
- The panel's height in px.
- disabled
- boolean
- default false
- Nothing can be edited.
- className
- string
- default —
- Classes for the panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| packages / defaultPackages | ParcelSpecPackage[] | — / defaultParcelSpecPackages | Parcels in cm and kg: { id, contents?, length, width, height, weight }, controlled or initial. |
| onChange | (packages) => void | — | Fires from the keystroke, nudge or button that changed a parcel. |
| onActivePackageChange | (packageId) => void | — | The parcel being drawn changed (a tab, a new parcel, a removal). |
| services | ParcelSpecService[] | defaultParcelSpecServices | { id, name, eta, maxWeight (kg), maxLength (cm), base, perKg }. |
| service / defaultService | string | — / "standard" | The chosen service, controlled or initial. |
| onServiceChange | (serviceId) => void | — | Fires from the click or arrow key that picked a service. |
| onQuote | (packages) => Record | Promise<Record<string, number | null>> | — | Prices the parcels per service (null where it can't go); a promise shimmers until it settles. Omit it and prices come from each service's rates. |
| unitSystem | "metric" | "imperial" | "metric" | The units fields, callouts, limits and floor squares are shown in; typed units always convert. |
| boxStyle | "kraft" | "wire" | "tint" | "kraft" | Cardboard faces with a tape strip, edges only with hidden edges dashed, or accent-tinted glass. |
| accent | string | "var(--accent-bright)" | The chosen service, the needle, callouts and focus — any CSS colour; buttons take its hue at a fixed lightness so white text reads in both themes. |
| maxPackages | number | 4 | Most parcels in one shipment. |
| volumetricDivisor | number | 5000 | Volumetric weight is L × W × H in cm over this, in kg; the charge uses the greater weight. |
| heading | string | "Ship to Halden Street" | The panel's heading and accessible name. |
| currency | string | "USD" | The quotes' currency. |
| locale | string | "en-US" | The quotes' locale. |
| status | "ready" | "loading" | "error" | "ready" | Loading shows a skeleton stage and fields; error shows Try again. |
| onRetry | () => void | — | Try again was pressed after the shipment failed to load. |
| height | number | 540 | The panel's height in px. |
| disabled | boolean | false | Nothing can be edited. |
| className | string | — | Classes for the panel. |