Three-Tier Pricing
The canonical Starter / Pro / Enterprise grid, with the middle tier promoted.
6 个框架初级
A seat stepper that recomputes the monthly total and announces it live.
<!-- Stepper buttons are 40x40 tap targets; the live total sits in an <output>
with aria-live so the recomputed price is announced. Vanilla JS keeps the
count clamped between min and max. -->
<section class="mx-auto w-full max-w-md px-4" aria-labelledby="per-seat-heading" data-per-seat="7" data-min="1" data-max="50">
<div class="rounded-2xl border border-gray-200 bg-white p-6 dark:border-gray-800 dark:bg-gray-900">
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100" id="per-seat-heading">Team plan</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">$7 per seat, per month.</p>
<div class="mt-5 flex items-center justify-between gap-4">
<span id="per-seat-label" class="text-sm font-medium text-gray-700 dark:text-gray-300">Seats</span>
<div class="flex items-center gap-2">
<button type="button" data-step="-1" aria-label="Remove one seat" class="flex h-10 w-10 items-center justify-center rounded-lg border border-gray-300 text-lg text-gray-700 transition-colors hover:bg-gray-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 disabled:cursor-not-allowed disabled:opacity-40 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800">−</button>
<span data-seats class="w-10 text-center text-base font-semibold tabular-nums text-gray-900 dark:text-gray-100" aria-live="polite" aria-labelledby="per-seat-label">3</span>
<button type="button" data-step="1" aria-label="Add one seat" class="flex h-10 w-10 items-center justify-center rounded-lg border border-gray-300 text-lg text-gray-700 transition-colors hover:bg-gray-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 disabled:cursor-not-allowed disabled:opacity-40 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800">+</button>
</div>
</div>
<p class="mt-6 flex flex-wrap items-baseline gap-x-2 border-t border-gray-100 pt-5 dark:border-gray-800">
<output data-total class="text-3xl font-bold tracking-tight tabular-nums text-gray-900 dark:text-gray-100" aria-live="polite">$21</output>
<span class="text-sm text-gray-600 dark:text-gray-400">/month</span>
<span data-summary class="basis-full text-xs text-gray-500 dark:text-gray-400">3 seats × $7</span>
</p>
<a href="#" class="mt-5 block rounded-lg bg-blue-600 px-4 py-2.5 text-center text-sm font-semibold text-white transition-colors hover:bg-blue-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-white motion-reduce:transition-none dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-900">Start with this team</a>
</div>
</section>
<script>
document.querySelectorAll('[data-per-seat]').forEach(function (root) {
var per = Number(root.dataset.perSeat), min = Number(root.dataset.min), max = Number(root.dataset.max);
var seatsEl = root.querySelector('[data-seats]'), totalEl = root.querySelector('[data-total]'), summaryEl = root.querySelector('[data-summary]');
var dec = root.querySelector('[data-step="-1"]'), inc = root.querySelector('[data-step="1"]');
var seats = Number(seatsEl.textContent) || min;
function render() {
seatsEl.textContent = String(seats);
totalEl.textContent = '$' + per * seats;
summaryEl.textContent = seats + (seats === 1 ? ' seat' : ' seats') + ' \u00d7 $' + per;
dec.disabled = seats <= min;
inc.disabled = seats >= max;
}
dec.addEventListener('click', function () { seats = Math.max(min, seats - 1); render(); });
inc.addEventListener('click', function () { seats = Math.min(max, seats + 1); render(); });
render();
});
</script>| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
perSeat | number | 7 | Per seat |
minSeats | number | 1 | 滑块允许的最少席位数。 |
maxSeats | number | 50 | 滑块允许的最多席位数。 |
className | string | - | 合并到根元素上的额外类名。 |
Set `perSeat`, `minSeats` and `maxSeats`; the stepper buttons are 40x40 tap targets and the total sits in an `<output>` with `aria-live` so it is announced as the count changes. Move to integer cents before a price gains a decimal - `12.99 * 3` is already `38.97000000000001`.