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.
The preview is live. The source is Pro.
One purchase, lifetime access: every pro component's source, CLI install with your own registry token, and all future drops.
Installation
CLI
npx shadcn@latest add @crucible/bursarManual — install dependencies, then copy the source
npm install motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| plan | BursarSubscription | The current subscription. Uncontrolled after mount (re-syncs when the object changes). | |
| plans | BursarPlan[] | Plans offered by the inline switcher (2–4 recommended). | |
| usage | BursarMeter[] | Usage meters for the current period (3 recommended). A meter with id: "seats" tracks the subscription's seats and plan limit. | |
| paymentMethod | BursarPaymentMethod | Card on file plus billing contact. | |
| invoices | BursarInvoice[] | Invoice history, newest first. | |
| pageSize | number | 5 | Invoices shown before "Load more" (and per page after). |
| cancelOffer | BursarCancelOffer | null | 30% off for 3 months | Retention 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. | |
| currency | string | "USD" | ISO 4217 currency for every amount. |
| locale | string | "en-US" | BCP 47 locale for numbers and dates. |
| accentColor | string | "#86efac" | Signature accent (primary buttons, focus rings, bars). Text on accent fills auto-picks black/white. |
| container | HTMLElement | null | document.body (full-viewport modal) | Portal target for the cancel dialog. |
| ref | React.Ref<BursarHandle> | Imperative handle (replay, openPlans). React 19 ref-as-prop. | |
| className | string | Extra classes for the root. |
Honors prefers-reduced-motion with a designed static fallback, and passes className through.