Skip to content

Billing kit

The Billing kit (@qazana/strata/billing) is the in-product subscription surface — the “Billing & Plans” settings area every SaaS has. It’s standalone on base.css and the token foundation: load billing.css + qazana.js, no app.css dependency. All components are scoped under .billing.

It’s deliberately distinct from two neighbours: site.css .pricing is marketing pricing (acquisition), and commerce.css is one-time storefront checkout. Billing is the recurring, logged-in surface. All money is static markup — no currency math, no Intl.NumberFormat.

Current-plan summary, a monthly/annual cycle toggle ([data-billing-cycle]) that swaps pre-rendered prices, and an inline proration confirm panel on upgrade:

Open “Billing — plans” in a new tab ↗

Dunning banner, current plan, metered usage with an over-limit upgrade nudge, seat management, and the retention-framed cancel modal:

Open “Billing — overview” in a new tab ↗

Saved-state cards with a default (server state, not a radio selection), per-card actions, and a neutral brand slot:

Open “Billing — payment methods” in a new tab ↗

History table with five status states and per-row receipt download:

Open “Billing — invoices” in a new tab ↗
ComponentClass / hookNotes
Current plan.bill-summary (+ .trial)plan + .tier badge, renewal, seats
Cycle toggle[data-billing-cycle]radiogroup → flips data-cycle on a named target
Price swap.price[data-cy] · .only-yearCSS shows the active cycle; no math
Plan card.plan.popular/.currentscoped copy of the app .plan
Proration<details class="switch"> + .prorationinline confirm, no modal
Invoices.invoices tableth[scope], tabular-nums amounts
Status badge.badge.paid/.open/.past-due/.refunded/.voidsemantic tokens only
Payment method.pay-method (+ .default)saved state; .brand slot, .pm-* rows
Add method.pay-adddashed affordance (provider form is app-side)
Usage meter.usage-row + .quota.warn/.crit.ur-cost projection
Upgrade nudge.usage-row.crit .ur-nudgesurfaces only when over limit
Dunning.bill-banner.warn/.critrole="alert", static deadline
Seats.seat-row + [data-stepper]per-seat quantity
Cancel flow.modal-scrim#cancel + [data-modal-open].keep alternatives, .reasons chips, muted .cancel-confirm
<!-- billing settings page -->
<link rel="stylesheet" href="@qazana/strata/billing">
<script type="module" src="@qazana/strata"></script>
<body class="billing">
<!-- all billing components live inside .billing -->
</body>