crucıble

Bursar

Billing settings page as one block: a plan card with a seat bar and an inline plan switcher (monthly/annual toggle, Tally-rolled prices, live proration line, async onChangePlan), a retention-flow cancel dialog (reason → discount offer → confirm), usage meters that spring to width with warn/overage states and drawn 30-day sparklines, a CSS payment card that flips in 3D to an edit form with grouped card-number entry and inline billing-contact editing, and an invoice table with expanding line items, per-row download, failed-payment retry and cascading Load more. Presentation only — use a PCI-compliant card element in production.

motionpro

Installation

CLI

npx shadcn@latest add @crucible/bursar

Manual — install dependencies, then copy the source

npm install motion

Props

PropTypeDefaultDescription
planBursarSubscriptionThe current subscription. Uncontrolled after mount (re-syncs when the object changes).
plansBursarPlan[]Plans offered by the inline switcher (2–4 recommended).
usageBursarMeter[]Usage meters for the current period (3 recommended). A meter with id: "seats" tracks the subscription's seats and plan limit.
paymentMethodBursarPaymentMethodCard on file plus billing contact.
invoicesBursarInvoice[]Invoice history, newest first.
pageSizenumber5Invoices shown before "Load more" (and per page after).
cancelOfferBursarCancelOffer | null30% off for 3 monthsRetention offer in the cancel flow; null skips the offer step.
onChangePlan(plan: BursarPlan, cycle: BursarCycle) => void | Promise<unknown>Called on "Switch to …". Resolve to apply, reject (with an Error message) to show it inline.
onCancel(reason: string, acceptedOffer: boolean) => void | Promise<unknown>Called with the chosen reason and whether the retention offer was accepted. Async-aware.
onUpdatePayment(update: BursarPaymentUpdate) => void | Promise<unknown>Called when the card or the billing contact is saved. Async-aware.
onDownload(invoice: BursarInvoice) => void | Promise<unknown>Called by an invoice's Download button. Async-aware (spinner → check).
onRetry(invoice: BursarInvoice) => void | Promise<unknown>Called by "Retry payment" on a failed invoice. Resolve → paid; reject → inline error.
currencystring"USD"ISO 4217 currency for every amount.
localestring"en-US"BCP 47 locale for numbers and dates.
accentColorstring"#86efac"Signature accent (primary buttons, focus rings, bars). Text on accent fills auto-picks black/white.
containerHTMLElement | nulldocument.body (full-viewport modal)Portal target for the cancel dialog.
refReact.Ref<BursarHandle>Imperative handle (replay, openPlans). React 19 ref-as-prop.
classNamestringExtra classes for the root.

Honors prefers-reduced-motion with a designed static fallback, and passes className through.