# Pricing

> A row of plan cards, light and dark, with mono labels, label and value rows, what is included, and a call to action.

- Name: `pricing`
- Status: beta
- Categories: pricing, plans, marketing
- Files: the install writes `pricing.tsx`, and any part files named `pricing-*.tsx`, to your `components` alias, `@/components` by default. Edit them freely. They are yours.
- Other registry items: `@arbokui/choice-chips`, `@arbokui/morph-label`, `@arbokui/rolling-number`. The install adds them.
- Gallery: https://arbokui.com/website/pricing
- Registry item: https://arbokui.com/r/pricing.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the website component. It also adds every component it uses:

   ```bash
   npx shadcn@latest add @arbokui/pricing
   ```

## Use it for

- A pricing page, or the closing section of a landing page, under a Headline.
- Two to four plans. Make the one you want people to pick solid, the dark card, and the rest soft.
- Use \`details\` for short facts, like seats or projects, and \`features\` for what is included.
- Put one Button in each card's \`action\`. It fills the card's width.
- A contact card works too: leave out the price, and add a \`description\` and a \`footnote\` with your email.
- For monthly and yearly prices, pass \`billing\` to Pricing, and give each card's \`price\` and \`period\` one entry per period id. A plain string stays the same on every period.
- Put a saving, like "Save 20% yearly", in \`billing.note\`, or in the yearly \`period\`, like "a month, billed yearly". Keep the switch's labels to the period's name.
- The headline is set in the theme's \`font-headline\`. Load Geist Serif as \`--font-geist-serif\`, like \`geist/font/serif/variable\` in Next.js, or it falls back to the system serif.

## Avoid it for

- More than four plans. People stop comparing past that. Link to a full table, or use Comparison.
- Prices that change by region or seat count in the card itself. Show the starting price, and explain the rest.
- A plan comparison of many features. Use Comparison.
- A billing switch with more than three periods, or with one. Show a single period as a plain price.

## Props

- `eyebrow`: `string`. PricingCard. A small mono label at the top left.
- `tag`: `string`. PricingCard. A small mono label at the top right.
- `title`: `ReactNode`. PricingCard. The plan's name, large. It is an h3.
- `price`: `string | Record<string, string>`. PricingCard. The price, if you show one. Under a billing switch, one entry per period id: { monthly: "$24", yearly: "$240" }.
- `period`: `string | Record<string, string>`. PricingCard. What the price covers, after it: once, a year. Under a billing switch, it can be one entry per period id too.
- `details`: `{ label: string; value: string }[]`. PricingCard. Short facts as rows under a dashed line, values in the brand color. Labels must be unique.
- `description`: `ReactNode`. PricingCard. A paragraph, like why the plan exists.
- `features`: `string[]`. PricingCard. What is included, one line each, with a plus. Each must be unique.
- `action`: `ReactNode`. PricingCard. The call to action, like a Button. It fills the card's width.
- `footnote`: `ReactNode`. PricingCard. A small line under the action.
- `emphasis`: `"solid" | "soft"`. Default `"solid"`. PricingCard. solid is a dark card that stands out, in both modes. soft is a light gray one.
- `className`: `string`. Extra classes on the root. On Pricing it holds the container query.
- `Pricing`: `ComponentProps<"div">`. Part. The row: cards stack when narrow, and sit side by side at equal widths from 42rem of its own width.
- `billing`: `{ options: { id: string; label: string }[]; defaultValue?: string; value?: string; onValueChange?: (value: string) => void; label?: string; note?: ReactNode; announce?: (option: { id: string; label: string }) => string }`. Pricing. A switch above the cards between billing periods. It starts on defaultValue, or the first option. label names it for screen readers, default "Billing". note is a short line beside it. announce returns what screen readers hear after a change, default "Showing yearly prices" from the option's label; pass it for another language.

## Accessibility

- Each plan name is an h3, so put the row under an h2, like a Headline.
- Details are a description list, so screen readers pair each label with its value.
- A solid card uses the theme's own dark colors, so every pair in it is one the theme checks in dark mode.
- In forced colors a soft card gets a border, so its edge still shows when its fill is painted away.
- Brand values on the soft card use tone text on muted, which the theme checks at 4.5:1.
- Plus marks are aria-hidden. Each feature reads as a plain list item.
- The billing switch is a radio group named by its label, so screen readers say which period is on. The arrow keys move between periods.
- A period is always picked: pressing the picked one keeps it.
- The prices change in place. A hidden polite status beside the switch then says so, like "Showing yearly prices". It is silent on page load, and speaks after a press, an arrow key, or a controlled value change.
- The card keeps room for the longest period, so it does not jump when the period changes. The other periods' prices are hidden from screen readers.

## Examples

### Two plans

```tsx
<Pricing>
  <PricingCard emphasis="soft" eyebrow="Yearly" title="All-Access" price="$129" period="a year"
    features={["Every block and template"]} action={<Button emphasis="outline">Choose yearly</Button>} />
  <PricingCard eyebrow="Lifetime" tag="Best value" title="All-Access" price="$199" period="once"
    features={["Every block and template", "Updates forever"]} action={<Button tone="brand">Choose lifetime</Button>} />
</Pricing>
```

### Monthly or yearly

```tsx
<Pricing
  billing={{
    options: [{ id: "monthly", label: "Monthly" }, { id: "yearly", label: "Yearly" }],
    note: "Save 20% yearly",
  }}
>
  <PricingCard emphasis="soft" title="Starter" price="$0" period="forever" action={<Button emphasis="outline">Start free</Button>} />
  <PricingCard
    title="Team"
    price={{ monthly: "$24", yearly: "$19" }}
    period={{ monthly: "a month", yearly: "a month, billed yearly" }}
    action={<Button tone="brand">Start a trial</Button>}
  />
</Pricing>
```
