Skip to content

Components / Sections

Pricing Cards

Shows pricing plans side by side, with an annual/monthly toggle that rolls every price digit by digit to the other period with zero JavaScript (radios, :has and NumberFlow), the highlighted plan wearing a travelling border beam, cards revealed with a stagger. Plans with no monthly price render without the toggle, each price as plain text.

Live demo

billing

Free

$0/yr

MIT, forever

  • 125 primitives, no account, no token
  • Runtime, GSAP and Lenis adapters
  • Public shadcn registry
  • MCP server
Set up in five minutes

Pro

$129/yr

One person, billed yearly

  • 108 sections and 11 GPU effects, demos never gated
  • Every template: Waitlist, Launch, Agentic, Studio, Agency, Atelier, Gather, Creator, Endpoint, Homescreen, Estate, Practice, and Stay
  • audit_motion and measure_budget, locally and in CI
  • compose_section with a total cost
  • New components monthly, dated changelog
  • Email support within 2 working days
Not open yet: see the plan

Team

$349/yr

Up to 10 people, one invoice

  • Everything in Pro, for the whole team
  • One key for the team, locally and in CI
  • Move up from Pro at any time
  • More than 10 people: email us
Talk to us

Sample plans

Starter

$0/yr$0/mo

For one project

  • Three projects
  • Community support
Start

Plus

$120/yr$12/mo

For a growing team

  • Unlimited projects
  • Priority support
Subscribe

Business

$290/yr$29/mo

For larger teams

  • Everything in Plus
Contact sales

Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.

Variants · each row writes the attribute the prop writes; the section's own CSS answers

tone
density

Measured

JavaScript of its own

0 B

Uses Reveal and the shared runtime (1.9 kB raw, once per site). With those included: 3.0 kB raw.

CSS 15.4 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · newly available

Chrome 125 · Firefox 128 · Safari 16.4; needs has, property, css-math. Elsewhere: a static ring on the highlighted plan; without round() and mod() the price is text that changes in place with the period.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/pricing-cards
src/pages/index.astro
---
import PricingCards from '@/components/moonarc/pro/PricingCards.astro';
---
<PricingCards plans={[
  { name: 'Starter', monthly: 0, annual: 0, features: ['3 projects', 'Community support'], action: { href: '/signup/', label: 'Start' } },
  { name: 'Plus', monthly: 12, annual: 120, features: ['Unlimited projects', 'Priority support'], action: { href: '/checkout/', label: 'Subscribe' }, highlight: true },
]} />

Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: reveal, border-beam, number-flow.

Props

PropTypeDefaultDescription
plans{ name, monthly?, annual, blurb?, features, action, highlight? }[]nonePlans; one can be highlighted. Leave out `monthly` on every plan for yearly-only pricing: the toggle is not rendered and each card shows its yearly price.
currencystring'$'Currency symbol.
priceFormatstring'{currency}{n}'How a price prints: {currency} is the currency prop and {n} the amount, so '{n} {currency}' puts the symbol after it.
localestringunset (the number as given)Intl locale of the amount: its thousands separator, decimal mark and digits. The rolling digits take the same separator and mark; a locale whose digits are not Latin keeps the text swap.
perYearstring'/yr'The small text after the yearly price.
perMonthstring'/mo'The small text after the monthly price.
annualLabelstring'Annual'The toggle's yearly choice.
monthlyLabelstring'Monthly'The toggle's monthly choice.
billingLabelstring'Billing period'Accessible name of the toggle's group.
idstringunique per renderRadio group prefix. Unset, each instance gets its own, so two on one page never share their radios.
tone'surface' | 'muted' | 'ink' | 'brand'unset (paints nothing)The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel. Each paints --background, --ma-tone-muted (over --muted, with a --border hairline), --ma-tone-ink or --ma-tone-brand (over --primary) with the matching text. With theme.css, a painted tone also re-declares --card, --border, --input and --ring inside the section, so cards and fields follow the ground. Decoration is strongest on brand.
density'compact' | 'cozy' | 'roomy''cozy'Card padding and gaps ×0.8 / ×1 / ×1.25 through --ma-density.
level1 | 22Heading level of the headline: 2 under another h1 (a demo, a block further down), 1 when the section opens its page; the plan names step up with it, h3 to h2, so the outline has no gap.
beambooleantrueThe beam that runs around the highlighted plan's border (BorderBeam). false keeps the stronger border alone.

Theming and overrides

The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.

Classes

.ma-pricing.ma-pricing__action.ma-pricing__annual.ma-pricing__aside.ma-pricing__billing.ma-pricing__blurb.ma-pricing__copy.ma-pricing__eyebrow.ma-pricing__features.ma-pricing__head.ma-pricing__headline.ma-pricing__monthly.ma-pricing__name.ma-pricing__per-month.ma-pricing__per-year.ma-pricing__plan.ma-pricing__plans.ma-pricing__price.ma-pricing__roll

Slots

slot="eyebrow"slot="headline"slot="copy"slot="aside"

Reduced motion

Cards fade in without stagger; the beam stops; the prices change without rolling.

With ClientRouter

Inherits Reveal and BorderBeam; the toggle is a radio and resets with the page.

Why it is built this way

Replaces: Pricing sections (Tailwind Plus) · Pricing (shadcnblocks). See the migration table.

Border Beam

Backgrounds

A beam of light travels around the edge of a card.

newly · Chrome 120 · Firefox 128 · Safari 16.4always

Entrance on scroll that plays once and stays, staggers its children, scrubs with the scroll position when asked, honours reduced motion, and keeps working after every ClientRouter navigation.

every browserscroll

Compares your product with others in an "us versus them" table.

newly · Chrome 85 · Firefox 128 · Safari 16.4scroll

A feature-by-plan comparison: the highlighted plan's column lit, rows revealed with a 40 ms stagger as the table scrolls into view, and a header row that sticks when you cap the table's height.

every browserscroll

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown