Pricing Table
The plan grid every product page needs. Price formats a single amount; this is the block people choose from.
Basic Usage
-
Free
For trying things out
€0.00 /mo- 1 project
- Community support
-
Pro Most popular
For growing teams
€29.00 /mo- Unlimited projects
- Priority support
- Audit log
-
Enterprise
For the whole company
Let us talk- Everything in Pro
- SSO and SCIM
- A named contact
Amounts are formatted, not typed
Pass the raw amount and a currency. The tier renders it through
Price, so every plan is formatted for the visitor's locale
and no one hard-codes "€29" into copy.
{{-- 1. Raw number in — locale-formatted currency out --}}
<x-wirekit::pricing-tier name="Pro" :price="$plan->price_cents / 100" currency="EUR" period="mo" />
A tier with no number — "Let us talk" — uses priceLabel instead. It never
invents an amount.
The featured plan
featured marks the plan you want chosen. It gets a badge and a ring — not
a tint. A highlight that exists only as a color is invisible to a screen reader
and to anyone who cannot separate your accent from your border.
It is deliberately not scaled up: enlarging one card reflows the row and makes the neighbors look broken on a phone.
Billing period toggle
Drive the amounts from your own state and let Segmented Control switch it:
{{-- 1. One property decides which set of amounts renders --}}
<x-wirekit::segmented-control wire:model.live="billing" :options="['monthly' => 'Monthly', 'yearly' => 'Yearly (-20%)']" />
<x-wirekit::pricing-table>
@foreach($plans as $plan)
{{-- 2. The tier just renders the amount it is handed --}}
<x-wirekit::pricing-tier
:name="$plan->name"
:price="$billing === 'yearly' ? $plan->yearly : $plan->monthly"
currency="EUR"
:period="$billing === 'yearly' ? 'year' : 'mo'"
:featured="$plan->is_popular"
/>
@endforeach
</x-wirekit::pricing-table>
SEO
Emit Offer structured data alongside the table with
Structured Data — but only for the plans the page
actually shows.
Props
pricing-table
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string | 'Pricing plans' |
Accessible name for the list of plans |
scope |
string|null | null |
Scoped personalization name |
pricing-tier
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string | '' |
Plan name |
price |
mixed | null |
Raw amount — formatted by Price |
currency |
string|null | null |
Currency code; falls back to your config |
period |
string|null | null |
mo, year, seat/mo … |
description |
string|null | null |
Copy under the plan name |
featured |
bool | false |
Badge + ring highlight |
featuredLabel |
string | 'Most popular' |
Text for the featured badge |
priceLabel |
string|null | null |
Shown instead of an amount ("Let us talk") |
scope |
string|null | null |
Scoped personalization name |
Slots
| Slot | Description |
|---|---|
features |
The feature list |
action |
The plan's CTA |
Accessibility
- The table is a real
<ul role="list">with a label, and each plan is an<li>— so a screen reader announces "list, 3 items" and the reader knows how many plans they are comparing before they start. - The featured highlight is never color-only (WCAG 1.4.1): the badge states "Most popular" in text; the ring is the visual echo, not the message.
- Every CTA is pinned to the same baseline (
mt-auto), so the row stays scannable however uneven the feature lists are. - Give each CTA a name that says which plan it buys ("Choose Pro"), not just "Choose" — a screen-reader user tabbing the row hears them out of context.
Keyboard Interaction
| Key | Action |
|---|---|
| Tab | Move through the plan CTAs in order |
Pitfalls
- Do not hard-code the currency into copy. Pass
price+currencyand let Price format it. - Do not invent a number for the enterprise tier. Use
priceLabel. - Do not scale the featured plan. It reflows the row on a phone; the ring and badge are enough.
- Do not label every CTA "Choose". Say which plan.
Design Tokens
| Element | Token |
|---|---|
| Plan surface | --color-wk-bg-elevated |
| Plan border | --color-wk-border, --border-wk-width |
| Plan radius | --radius-wk-lg |
| Plan padding | --padding-wk-x-lg |
| Featured ring | --color-wk-accent |
| Grid gap | --gap-wk-md |
| Name / description | --text-wk-lg, --text-wk-sm, --color-wk-text-muted |