11 / 21
All 21 in 21 Tailwind CSS Pricing Tables
- 01Tailwind CSS Pricing Table with Monthly / Annual Toggle
- 02Tailwind Pricing Table with Currency Switcher (USD, GBP, EUR, AUD)
- 03Tailwind CSS Pricing Comparison Table with Expandable Features
- 04Tailwind Dark Mode Pricing Cards with Light / Dark Toggle
- 05Tailwind Pricing Cards with Hover Scale Effect
- 06Tailwind "Most Popular" Pricing Card with Animated Border Beam
- 07Tailwind Pricing Cards with Animated Discount Badges
- 08Tailwind Pricing Table CTA Button Hover Animation
- 09Tailwind Pricing Cards with Color-Changing Hover Themes
- 10Tailwind CSS 3D Flip Pricing Cards
- 11Tailwind Pricing Slider Calculator for Per-Seat Pricing
- 12Tailwind Tabbed Pricing Table for Multiple Products
- 13Tailwind Pricing Cards with Usage Meters & Progress Bars
- 14Tailwind Bundle Pricing Calculator with Checkboxes
- 15Glassmorphism Pricing Cards in Tailwind CSS
- 16Tailwind Pricing Card with Animated Gradient Border on Hover
- 17Tailwind Pricing Cards with Corner Ribbon & Bookmark Banner
- 18Neumorphic Pricing Table in Tailwind CSS (Soft UI)
- 19Mobile Swipe Pricing Carousel with Tailwind Scroll Snap
- 20Responsive Tailwind Pricing Table that Stacks into an Accordion
- 21Tailwind Pricing Cards with Skeleton Loading Shimmer
Tailwind Pricing Slider Calculator for Per-Seat Pricing
The usage-based calculator SaaS teams actually ship: drag a team-size slider and every tier's monthly total recalculates, the per-seat maths updates and the “Best value” badge jumps to whichever plan is now cheapest. The range thumb and track are styled with arbitrary pseudo-element variants ([&::-webkit-slider-thumb]:), and the script marks the winner with a data-best attribute that data-best: utilities restyle.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="tpt-11 w-full min-h-svh grid place-items-center bg-tpt11-bg bg-[radial-gradient(70%_50%_at_50%_0%,var(--color-tpt11-glow),transparent)] px-5 py-16 font-tpt11-sans text-tpt11-ink antialiased" aria-label="Per-seat pricing calculator" data-tpt11-root>
<div class="grid w-full max-w-5xl justify-items-center gap-10">
<header class="grid w-full max-w-md justify-items-center gap-6 text-center">
<h2 class="text-[clamp(26px,3.8vw,40px)] font-bold tracking-[-0.03em]">How big is your team?</h2>
<label for="tpt11-range" class="text-sm font-medium text-tpt11-mut"><output data-out class="mr-1 font-tpt11-mono text-2xl font-semibold text-tpt11-acc tabular-nums">10</output> seats</label>
<div class="grid w-full gap-2">
<input type="range" id="tpt11-range" min="1" max="100" step="1" value="10" list="tpt11-ticks" data-range class="h-2 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(to_right,var(--color-tpt11-acc)_var(--pct),var(--color-tpt11-track)_var(--pct))] [--pct:9%] focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tpt11-acc [&::-moz-range-thumb]:size-6 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-[5px] [&::-moz-range-thumb]:border-tpt11-acc [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-6 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-[5px] [&::-webkit-slider-thumb]:border-tpt11-acc [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_4px_12px_oklch(0_0_0/0.45)] [&::-webkit-slider-thumb]:transition-transform hover:[&::-webkit-slider-thumb]:scale-115 active:[&::-webkit-slider-thumb]:scale-125">
<datalist id="tpt11-ticks"><option value="1"></option><option value="25"></option><option value="50"></option><option value="75"></option><option value="100"></option></datalist>
<div aria-hidden="true" class="flex justify-between font-tpt11-mono text-[11px] text-tpt11-mut"><span>1</span><span>25</span><span>50</span><span>75</span><span>100</span></div>
</div>
</header>
<div class="grid w-full gap-5 md:grid-cols-3">
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Starter</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$70</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$7 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Core features</li><li>Standard support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Starter</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Team</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$99</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$49 base + $5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Starter</li><li>Roles & permissions</li><li>Priority support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Team</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Business</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$184</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$149 base + $3.5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Team</li><li>SSO & audit log</li><li>99.9% uptime SLA</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Business</a>
</article>
</div>
<p data-live aria-live="polite" class="sr-only"></p>
<p aria-hidden="true" class="rounded-full px-3.5 py-2 font-tpt11-mono text-[11.5px] text-tpt11-mut ring-1 ring-white/10">[&::-webkit-slider-thumb]: · data-best: · group-data-best/card:</p>
</div>
</section><section class="tpt-11 w-full min-h-svh grid place-items-center bg-tpt11-bg bg-[radial-gradient(70%_50%_at_50%_0%,var(--color-tpt11-glow),transparent)] px-5 py-16 font-tpt11-sans text-tpt11-ink antialiased" aria-label="Per-seat pricing calculator" data-tpt11-root>
<div class="grid w-full max-w-5xl justify-items-center gap-10">
<header class="grid w-full max-w-md justify-items-center gap-6 text-center">
<h2 class="text-[clamp(26px,3.8vw,40px)] font-bold tracking-[-0.03em]">How big is your team?</h2>
<label for="tpt11-range" class="text-sm font-medium text-tpt11-mut"><output data-out class="mr-1 font-tpt11-mono text-2xl font-semibold text-tpt11-acc tabular-nums">10</output> seats</label>
<div class="grid w-full gap-2">
<input type="range" id="tpt11-range" min="1" max="100" step="1" value="10" list="tpt11-ticks" data-range class="h-2 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(to_right,var(--color-tpt11-acc)_var(--pct),var(--color-tpt11-track)_var(--pct))] [--pct:9%] focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tpt11-acc [&::-moz-range-thumb]:size-6 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-[5px] [&::-moz-range-thumb]:border-tpt11-acc [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-6 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-[5px] [&::-webkit-slider-thumb]:border-tpt11-acc [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_4px_12px_oklch(0_0_0/0.45)] [&::-webkit-slider-thumb]:transition-transform hover:[&::-webkit-slider-thumb]:scale-115 active:[&::-webkit-slider-thumb]:scale-125">
<datalist id="tpt11-ticks"><option value="1"></option><option value="25"></option><option value="50"></option><option value="75"></option><option value="100"></option></datalist>
<div aria-hidden="true" class="flex justify-between font-tpt11-mono text-[11px] text-tpt11-mut"><span>1</span><span>25</span><span>50</span><span>75</span><span>100</span></div>
</div>
</header>
<div class="grid w-full gap-5 md:grid-cols-3">
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Starter</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$70</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$7 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Core features</li><li>Standard support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Starter</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Team</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$99</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$49 base + $5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Starter</li><li>Roles & permissions</li><li>Priority support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Team</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Business</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$184</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$149 base + $3.5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Team</li><li>SSO & audit log</li><li>99.9% uptime SLA</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Business</a>
</article>
</div>
<p data-live aria-live="polite" class="sr-only"></p>
<p aria-hidden="true" class="rounded-full px-3.5 py-2 font-tpt11-mono text-[11.5px] text-tpt11-mut ring-1 ring-white/10">[&::-webkit-slider-thumb]: · data-best: · group-data-best/card:</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}(() => {
document.querySelectorAll('[data-tpt11-root]').forEach((root) => {
if (root.dataset.wired) return;
root.dataset.wired = '1';
const range = root.querySelector('[data-range]');
const out = root.querySelector('[data-out]');
const live = root.querySelector('[data-live]');
const cards = [...root.querySelectorAll('[data-card]')];
const TIERS = [
{ name: 'Starter', base: 0, seat: 7 },
{ name: 'Team', base: 49, seat: 5 },
{ name: 'Business', base: 149, seat: 3.5 },
];
const ANNOUNCE_MS = 600;
let announce = 0;
const update = () => {
const seats = +range.value;
out.textContent = seats;
range.style.setProperty('--pct', ((seats - range.min) / (range.max - range.min)) * 100 + '%');
let best = 0, bestPrice = Infinity;
TIERS.forEach((t, i) => {
const total = Math.round(t.base + t.seat * seats);
cards[i].querySelector('[data-amount]').textContent = '$' + total;
cards[i].querySelector('[data-math]').textContent = (t.base ? '$' + t.base + ' base + ' : '') + '$' + t.seat + ' × ' + seats + (seats === 1 ? ' seat' : ' seats');
if (total < bestPrice) { bestPrice = total; best = i; }
});
cards.forEach((c, i) => c.toggleAttribute('data-best', i === best));
clearTimeout(announce);
announce = setTimeout(() => {
live.textContent = seats + ' seats: best value is ' + TIERS[best].name + ' at $' + bestPrice + ' per month.';
}, ANNOUNCE_MS);
};
range.addEventListener('input', update);
update();
});
})();(() => {
document.querySelectorAll('[data-tpt11-root]').forEach((root) => {
if (root.dataset.wired) return;
root.dataset.wired = '1';
const range = root.querySelector('[data-range]');
const out = root.querySelector('[data-out]');
const live = root.querySelector('[data-live]');
const cards = [...root.querySelectorAll('[data-card]')];
const TIERS = [
{ name: 'Starter', base: 0, seat: 7 },
{ name: 'Team', base: 49, seat: 5 },
{ name: 'Business', base: 149, seat: 3.5 },
];
const ANNOUNCE_MS = 600;
let announce = 0;
const update = () => {
const seats = +range.value;
out.textContent = seats;
range.style.setProperty('--pct', ((seats - range.min) / (range.max - range.min)) * 100 + '%');
let best = 0, bestPrice = Infinity;
TIERS.forEach((t, i) => {
const total = Math.round(t.base + t.seat * seats);
cards[i].querySelector('[data-amount]').textContent = '$' + total;
cards[i].querySelector('[data-math]').textContent = (t.base ? '$' + t.base + ' base + ' : '') + '$' + t.seat + ' × ' + seats + (seats === 1 ? ' seat' : ' seats');
if (total < bestPrice) { bestPrice = total; best = i; }
});
cards.forEach((c, i) => c.toggleAttribute('data-best', i === best));
clearTimeout(announce);
announce = setTimeout(() => {
live.textContent = seats + ' seats: best value is ' + TIERS[best].name + ' at $' + bestPrice + ' per month.';
}, ANNOUNCE_MS);
};
range.addEventListener('input', update);
update();
});
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400..800&family=Geist+Mono:wght@400..600&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400..800&family=Geist+Mono:wght@400..600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The two range-thumb rule blocks became
[&::-webkit-slider-thumb]:and[&::-moz-range-thumb]:arbitrary variants on the input itself. .prc-11__card.prc-11__best …(four rules) becamedata-best:andgroup-data-best/card:utilities; the script now callstoggleAttribute('data-best')and knows no class names.- UI upgrade from the CSS original: tick labels and a
<datalist>give the slider a visible scale; the thumb grows on hover and press, and Firefox's thumb now matches Chrome's (was 14px + border vs 24px). - UI upgrade: the per-seat maths uses the mono face and says “1 seat” not “1 seats”; muted copy rose from
#7c8c7d(4.3:1) to 6.2:1 on the dark card.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Pricing Slider Calculator for Per-Seat Pricing
Source: https://codefronts.com/components/tailwind-css-pricing-tables/tailwind-pricing-slider-calculator-per-seat/
The usage-based calculator SaaS teams actually ship: drag a team-size slider and every tier's monthly total recalculates, the per-seat maths updates and the “Best value” badge jumps to whichever plan is now cheapest. The range thumb and track are styled with arbitrary pseudo-element variants ([&::-webkit-slider-thumb]:), and the script marks the winner with a data-best attribute that data-best: utilities restyle.
## HTML
```html
<section class="tpt-11 w-full min-h-svh grid place-items-center bg-tpt11-bg bg-[radial-gradient(70%_50%_at_50%_0%,var(--color-tpt11-glow),transparent)] px-5 py-16 font-tpt11-sans text-tpt11-ink antialiased" aria-label="Per-seat pricing calculator" data-tpt11-root>
<div class="grid w-full max-w-5xl justify-items-center gap-10">
<header class="grid w-full max-w-md justify-items-center gap-6 text-center">
<h2 class="text-[clamp(26px,3.8vw,40px)] font-bold tracking-[-0.03em]">How big is your team?</h2>
<label for="tpt11-range" class="text-sm font-medium text-tpt11-mut"><output data-out class="mr-1 font-tpt11-mono text-2xl font-semibold text-tpt11-acc tabular-nums">10</output> seats</label>
<div class="grid w-full gap-2">
<input type="range" id="tpt11-range" min="1" max="100" step="1" value="10" list="tpt11-ticks" data-range class="h-2 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(to_right,var(--color-tpt11-acc)_var(--pct),var(--color-tpt11-track)_var(--pct))] [--pct:9%] focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tpt11-acc [&::-moz-range-thumb]:size-6 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-[5px] [&::-moz-range-thumb]:border-tpt11-acc [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-6 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-[5px] [&::-webkit-slider-thumb]:border-tpt11-acc [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_4px_12px_oklch(0_0_0/0.45)] [&::-webkit-slider-thumb]:transition-transform hover:[&::-webkit-slider-thumb]:scale-115 active:[&::-webkit-slider-thumb]:scale-125">
<datalist id="tpt11-ticks"><option value="1"></option><option value="25"></option><option value="50"></option><option value="75"></option><option value="100"></option></datalist>
<div aria-hidden="true" class="flex justify-between font-tpt11-mono text-[11px] text-tpt11-mut"><span>1</span><span>25</span><span>50</span><span>75</span><span>100</span></div>
</div>
</header>
<div class="grid w-full gap-5 md:grid-cols-3">
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Starter</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$70</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$7 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Core features</li><li>Standard support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Starter</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Team</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$99</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$49 base + $5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Starter</li><li>Roles & permissions</li><li>Priority support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Team</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Business</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$184</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$149 base + $3.5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Team</li><li>SSO & audit log</li><li>99.9% uptime SLA</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Business</a>
</article>
</div>
<p data-live aria-live="polite" class="sr-only"></p>
<p aria-hidden="true" class="rounded-full px-3.5 py-2 font-tpt11-mono text-[11.5px] text-tpt11-mut ring-1 ring-white/10">[&::-webkit-slider-thumb]: · data-best: · group-data-best/card:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('[data-tpt11-root]').forEach((root) => {
if (root.dataset.wired) return;
root.dataset.wired = '1';
const range = root.querySelector('[data-range]');
const out = root.querySelector('[data-out]');
const live = root.querySelector('[data-live]');
const cards = [...root.querySelectorAll('[data-card]')];
const TIERS = [
{ name: 'Starter', base: 0, seat: 7 },
{ name: 'Team', base: 49, seat: 5 },
{ name: 'Business', base: 149, seat: 3.5 },
];
const ANNOUNCE_MS = 600;
let announce = 0;
const update = () => {
const seats = +range.value;
out.textContent = seats;
range.style.setProperty('--pct', ((seats - range.min) / (range.max - range.min)) * 100 + '%');
let best = 0, bestPrice = Infinity;
TIERS.forEach((t, i) => {
const total = Math.round(t.base + t.seat * seats);
cards[i].querySelector('[data-amount]').textContent = '$' + total;
cards[i].querySelector('[data-math]').textContent = (t.base ? '$' + t.base + ' base + ' : '') + '$' + t.seat + ' × ' + seats + (seats === 1 ? ' seat' : ' seats');
if (total < bestPrice) { bestPrice = total; best = i; }
});
cards.forEach((c, i) => c.toggleAttribute('data-best', i === best));
clearTimeout(announce);
announce = setTimeout(() => {
live.textContent = seats + ' seats: best value is ' + TIERS[best].name + ' at $' + bestPrice + ' per month.';
}, ANNOUNCE_MS);
};
range.addEventListener('input', update);
update();
});
})();
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Pricing Slider Calculator for Per-Seat Pricing
Source: https://codefronts.com/components/tailwind-css-pricing-tables/tailwind-pricing-slider-calculator-per-seat/
The usage-based calculator SaaS teams actually ship: drag a team-size slider and every tier's monthly total recalculates, the per-seat maths updates and the “Best value” badge jumps to whichever plan is now cheapest. The range thumb and track are styled with arbitrary pseudo-element variants ([&::-webkit-slider-thumb]:), and the script marks the winner with a data-best attribute that data-best: utilities restyle.
## HTML
```html
<section class="tpt-11 w-full min-h-svh grid place-items-center bg-tpt11-bg bg-[radial-gradient(70%_50%_at_50%_0%,var(--color-tpt11-glow),transparent)] px-5 py-16 font-tpt11-sans text-tpt11-ink antialiased" aria-label="Per-seat pricing calculator" data-tpt11-root>
<div class="grid w-full max-w-5xl justify-items-center gap-10">
<header class="grid w-full max-w-md justify-items-center gap-6 text-center">
<h2 class="text-[clamp(26px,3.8vw,40px)] font-bold tracking-[-0.03em]">How big is your team?</h2>
<label for="tpt11-range" class="text-sm font-medium text-tpt11-mut"><output data-out class="mr-1 font-tpt11-mono text-2xl font-semibold text-tpt11-acc tabular-nums">10</output> seats</label>
<div class="grid w-full gap-2">
<input type="range" id="tpt11-range" min="1" max="100" step="1" value="10" list="tpt11-ticks" data-range class="h-2 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(to_right,var(--color-tpt11-acc)_var(--pct),var(--color-tpt11-track)_var(--pct))] [--pct:9%] focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tpt11-acc [&::-moz-range-thumb]:size-6 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-[5px] [&::-moz-range-thumb]:border-tpt11-acc [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-6 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-[5px] [&::-webkit-slider-thumb]:border-tpt11-acc [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_4px_12px_oklch(0_0_0/0.45)] [&::-webkit-slider-thumb]:transition-transform hover:[&::-webkit-slider-thumb]:scale-115 active:[&::-webkit-slider-thumb]:scale-125">
<datalist id="tpt11-ticks"><option value="1"></option><option value="25"></option><option value="50"></option><option value="75"></option><option value="100"></option></datalist>
<div aria-hidden="true" class="flex justify-between font-tpt11-mono text-[11px] text-tpt11-mut"><span>1</span><span>25</span><span>50</span><span>75</span><span>100</span></div>
</div>
</header>
<div class="grid w-full gap-5 md:grid-cols-3">
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Starter</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$70</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$7 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Core features</li><li>Standard support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Starter</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Team</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$99</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$49 base + $5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Starter</li><li>Roles & permissions</li><li>Priority support</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Team</a>
</article>
<article data-card class="group/card relative flex flex-col gap-2 rounded-[20px] border-[1.5px] border-tpt11-line bg-tpt11-card p-7 transition-[border-color,box-shadow] duration-350 data-best:border-tpt11-acc data-best:shadow-[0_18px_44px_-26px_var(--color-tpt11-acc)]">
<span class="absolute -top-3 right-5 translate-y-1 rounded-full bg-tpt11-acc px-3 py-1 text-[10.5px] font-bold tracking-[0.08em] text-tpt11-on-acc uppercase opacity-0 transition duration-350 group-data-best/card:translate-y-0 group-data-best/card:opacity-100">Best value</span>
<h3 class="text-[13px] font-semibold tracking-[0.08em] text-tpt11-mut uppercase">Business</h3>
<p class="text-[42px] leading-tight font-bold tracking-[-0.035em] tabular-nums"><span data-amount>$184</span><small class="text-[15px] font-medium tracking-normal text-tpt11-mut">/mo</small></p>
<p data-math class="min-h-4 font-tpt11-mono text-xs text-tpt11-mut tabular-nums">$149 base + $3.5 × 10 seats</p>
<ul class="mt-4 mb-6 grid flex-1 content-start gap-2.5 text-sm text-tpt11-body *:flex *:gap-2.5 *:before:font-bold *:before:text-tpt11-acc *:before:content-['✓']"><li>Everything in Team</li><li>SSO & audit log</li><li>99.9% uptime SLA</li></ul>
<a href="#" class="grid min-h-11 place-items-center rounded-xl border border-tpt11-line text-sm font-semibold text-tpt11-ink no-underline transition hover:border-tpt11-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tpt11-acc group-data-best/card:border-tpt11-acc group-data-best/card:bg-tpt11-acc group-data-best/card:text-tpt11-on-acc">Choose Business</a>
</article>
</div>
<p data-live aria-live="polite" class="sr-only"></p>
<p aria-hidden="true" class="rounded-full px-3.5 py-2 font-tpt11-mono text-[11.5px] text-tpt11-mut ring-1 ring-white/10">[&::-webkit-slider-thumb]: · data-best: · group-data-best/card:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpt11-bg: #0f1410;
--color-tpt11-bg: oklch(0.18 0.012 150);
--color-tpt11-glow: #16301c99;
--color-tpt11-glow: oklch(0.29 0.05 150 / 0.6);
--color-tpt11-card: #161d17;
--color-tpt11-card: oklch(0.22 0.015 150);
--color-tpt11-line: #2a352c;
--color-tpt11-line: oklch(0.31 0.02 150);
--color-tpt11-track: #2a342c;
--color-tpt11-track: oklch(0.31 0.018 150);
--color-tpt11-ink: #e9efe9;
--color-tpt11-ink: oklch(0.94 0.01 150);
--color-tpt11-body: #c3cfc3;
--color-tpt11-body: oklch(0.84 0.02 150);
--color-tpt11-mut: #98a699;
--color-tpt11-mut: oklch(0.7 0.02 150);
--color-tpt11-acc: #3fc16a;
--color-tpt11-acc: oklch(0.72 0.17 148);
--color-tpt11-on-acc: #07140b;
--color-tpt11-on-acc: oklch(0.17 0.03 150);
--font-tpt11-sans: 'Geist', system-ui, sans-serif;
--font-tpt11-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-11 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('[data-tpt11-root]').forEach((root) => {
if (root.dataset.wired) return;
root.dataset.wired = '1';
const range = root.querySelector('[data-range]');
const out = root.querySelector('[data-out]');
const live = root.querySelector('[data-live]');
const cards = [...root.querySelectorAll('[data-card]')];
const TIERS = [
{ name: 'Starter', base: 0, seat: 7 },
{ name: 'Team', base: 49, seat: 5 },
{ name: 'Business', base: 149, seat: 3.5 },
];
const ANNOUNCE_MS = 600;
let announce = 0;
const update = () => {
const seats = +range.value;
out.textContent = seats;
range.style.setProperty('--pct', ((seats - range.min) / (range.max - range.min)) * 100 + '%');
let best = 0, bestPrice = Infinity;
TIERS.forEach((t, i) => {
const total = Math.round(t.base + t.seat * seats);
cards[i].querySelector('[data-amount]').textContent = '$' + total;
cards[i].querySelector('[data-math]').textContent = (t.base ? '$' + t.base + ' base + ' : '') + '$' + t.seat + ' × ' + seats + (seats === 1 ? ' seat' : ' seats');
if (total < bestPrice) { bestPrice = total; best = i; }
});
cards.forEach((c, i) => c.toggleAttribute('data-best', i === best));
clearTimeout(announce);
announce = setTimeout(() => {
live.textContent = seats + ' seats: best value is ' + TIERS[best].name + ' at $' + bestPrice + ' per month.';
}, ANNOUNCE_MS);
};
range.addEventListener('input', update);
update();
});
})();
```How this works
Pseudo-elements as variants. Range inputs expose vendor pseudo-elements; v4's arbitrary variants target them without a stylesheet:
[&::-webkit-slider-thumb]:appearance-none
[&::-webkit-slider-thumb]:size-6 [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:border-[5px] [&::-webkit-slider-thumb]:border-tpt11-accRealistic economics. Team and Business carry flat platform fees with cheaper seats, so the best-value tier changes with scale (Starter to 24 seats, Team to 50, Business beyond). JS computes totals, writes them into fixed tabular-nums slots and sets data-best on the winner.
Tick marks under the slider (1 · 25 · 50 · 75 · 100) give the drag a scale, and a <datalist> adds native snapping hints.
Make it yours
- Tier economics live in
TIERS(base fee + per-seat rate). - Change the range with
min/max/stepon the input — the script adapts. - Swap seats for GB, API calls or renders; labels are plain text.
- Announcement delay:
ANNOUNCE_MS.
Gotchas — read before shipping
- WebKit and Gecko thumbs don't share a selector — write both variants or Firefox shows the default thumb.
- Debounce
aria-liveor screen readers read every value you drag past. --pctis set inline by JS; declare a sensible default ([--pct:9%]) so the track isn't empty before the script runs.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Range inputs and custom properties are universal; the floor is Tailwind v4's sheet.