04 / 25
All 25 in 25 Tailwind CSS Button Groups
- 01Tailwind Split Button with Anchor-Positioned Popover Dropdown (No JS)
- 02Tailwind Segmented Control with Motion-Path Comet Indicator
- 03Tailwind Toggle Button Group with View Transitions Morphing Highlight
- 04Tailwind Pricing Toggle — Monthly / Yearly Billing Switch (No JS)
- 05Tailwind Segmented Control with Sliding Thumb (iOS Style)
- 06Tailwind Split Button Dropdown with Native Details Menu (Save Options)
- 07Tailwind Icon Button Group Toolbar with Tooltips (Rich Text Editor)
- 08Tailwind Pill Filter Button Group with Sliding Indicator (Anchor Positioning)
- 09Tailwind Toggle Button Group for Text Alignment (Radio Inputs, Live Preview)
- 10Tailwind Web3 Connect Wallet Button Grid (Wallet Sign-In Modal)
- 11Tailwind AI Model Selector Button Group (LLM Model Picker with Badges)
- 12Tailwind Liquid Metal Segmented Control (Animated Chrome Thumb)
- 13Tailwind Tab Button Group with Subgrid Underline Indicator
- 14Tailwind Multi-Select Chip Buttons (Tag Filter Checkboxes with Count)
- 15Tailwind Animated Split Button Deploy Menu (Staggered Dropdown Reveal)
- 16Tailwind Radio Button Group with Conic Fill Animation (Export Format Picker)
- 17Tailwind Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
- 18Tailwind Hold-to-Confirm Button Group with Gradient Progress (Press and Hold)
- 19Tailwind Holographic Icon Segmented Control (Grid / List View Switcher)
- 20Tailwind 3D Vertical Button Group with Hover Depth (Perspective Stack)
- 21Tailwind Floating Action Button with Radial Speed Dial Menu (No JS)
- 22Tailwind Glitch Effect Button Group (Cyberpunk RGB-Split Audio Filter)
- 23Tailwind Animated Gradient Border Pill Buttons (Rotating Conic Ring)
- 24Tailwind Button Group with Ripple Click Effect (Radar Shockwave Feedback)
- 25Tailwind Button Group with Cursor-Following Spotlight Glow (Date Range Presets)
Tailwind Pricing Toggle — Monthly / Yearly Billing Switch (No JS)
The SaaS pricing switch, solved in markup: two radios drive a sliding pill, the prices on every plan card and the savings line beneath them — all through group-has-[#tbg04-y:checked]/card: variants on the elements that change. The inactive price is invisible, not just transparent, so screen readers only ever announce the price you can see. No JavaScript.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tbg-04 w-full min-h-svh grid place-items-center bg-tbg04-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg04-glow),transparent_70%)] px-5 py-14 font-tbg04-sans text-tbg04-ink" aria-label="Monthly yearly pricing toggle demo">
<div class="group/card @container w-full max-w-[36rem] rounded-3xl bg-white px-6 pt-8 pb-7 shadow-tbg04-card sm:px-8">
<div role="radiogroup" aria-label="Billing period" class="relative mx-auto mb-7 grid w-fit grid-cols-2 gap-1 rounded-full bg-tbg04-well p-1">
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-m" class="sr-only" checked>Monthly</label>
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-y" class="sr-only">Yearly <span class="rounded-full bg-white px-2 py-0.5 text-[11px] font-extrabold text-tbg04-acc-d">Save 20%</span></label>
<span aria-hidden="true" class="pointer-events-none absolute top-1 bottom-1 left-1 z-0 w-[calc(50%-0.375rem)] rounded-full bg-tbg04-ink shadow-[0_6px_16px_-6px_rgb(6_60_58/0.7)] transition-transform duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-has-[#tbg04-y:checked]/card:translate-x-[calc(100%+0.25rem)] motion-reduce:transition-none"></span>
</div>
<div class="grid grid-cols-2 gap-3.5 @max-md:grid-cols-1">
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-line p-5">
<p class="text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Starter</p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$12</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$115</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Per seat, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $29 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl border-[1.5px] border-tbg04-line text-[14.5px] font-bold transition hover:border-tbg04-ink focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Start free trial</button>
</div>
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-acc bg-linear-to-b from-tbg04-tint to-white p-5">
<p class="flex items-center justify-between text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Studio <span class="rounded-full bg-tbg04-ink px-2 py-0.5 text-[10.5px] tracking-[0.06em] text-white">Popular</span></p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$29</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$278</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Unlimited projects, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $70 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl bg-tbg04-ink text-[14.5px] font-bold text-white transition hover:brightness-125 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Get Studio</button>
</div>
</div>
</div>
</section><section class="tbg-04 w-full min-h-svh grid place-items-center bg-tbg04-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg04-glow),transparent_70%)] px-5 py-14 font-tbg04-sans text-tbg04-ink" aria-label="Monthly yearly pricing toggle demo">
<div class="group/card @container w-full max-w-[36rem] rounded-3xl bg-white px-6 pt-8 pb-7 shadow-tbg04-card sm:px-8">
<div role="radiogroup" aria-label="Billing period" class="relative mx-auto mb-7 grid w-fit grid-cols-2 gap-1 rounded-full bg-tbg04-well p-1">
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-m" class="sr-only" checked>Monthly</label>
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-y" class="sr-only">Yearly <span class="rounded-full bg-white px-2 py-0.5 text-[11px] font-extrabold text-tbg04-acc-d">Save 20%</span></label>
<span aria-hidden="true" class="pointer-events-none absolute top-1 bottom-1 left-1 z-0 w-[calc(50%-0.375rem)] rounded-full bg-tbg04-ink shadow-[0_6px_16px_-6px_rgb(6_60_58/0.7)] transition-transform duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-has-[#tbg04-y:checked]/card:translate-x-[calc(100%+0.25rem)] motion-reduce:transition-none"></span>
</div>
<div class="grid grid-cols-2 gap-3.5 @max-md:grid-cols-1">
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-line p-5">
<p class="text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Starter</p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$12</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$115</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Per seat, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $29 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl border-[1.5px] border-tbg04-line text-[14.5px] font-bold transition hover:border-tbg04-ink focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Start free trial</button>
</div>
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-acc bg-linear-to-b from-tbg04-tint to-white p-5">
<p class="flex items-center justify-between text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Studio <span class="rounded-full bg-tbg04-ink px-2 py-0.5 text-[10.5px] tracking-[0.06em] text-white">Popular</span></p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$29</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$278</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Unlimited projects, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $70 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl bg-tbg04-ink text-[14.5px] font-bold text-white transition hover:brightness-125 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Get Studio</button>
</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
width: 100%;
min-height: 100svh;
}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-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
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=Manrope:wght@400..800&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=Manrope:wght@400..800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
.btn-04__card:has(#btn-04-y:checked) .btn-04__mo{opacity:0}and its four siblings becamegroup-has-[#tbg04-y:checked]/card:variants on the elements themselves.- The
@media (max-width:520px)stack became@max-md:grid-cols-1on an@containercard, so it re-stacks inside narrow layouts too. - UI upgrade: each plan now shows the real yearly saving and has its own call-to-action; Studio is badged “Popular”.
- Accessibility fix: the hidden price is
invisible, so screen readers no longer announce monthly and yearly prices together; accent text darkened tooklch(0.52)for 4.5:1 on white.
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 Toggle — Monthly / Yearly Billing Switch (No JS)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-pricing-toggle-monthly-yearly/
The SaaS pricing switch, solved in markup: two radios drive a sliding pill, the prices on every plan card and the savings line beneath them — all through group-has-[#tbg04-y:checked]/card: variants on the elements that change. The inactive price is invisible, not just transparent, so screen readers only ever announce the price you can see. No JavaScript.
## HTML
```html
<section class="tbg-04 w-full min-h-svh grid place-items-center bg-tbg04-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg04-glow),transparent_70%)] px-5 py-14 font-tbg04-sans text-tbg04-ink" aria-label="Monthly yearly pricing toggle demo">
<div class="group/card @container w-full max-w-[36rem] rounded-3xl bg-white px-6 pt-8 pb-7 shadow-tbg04-card sm:px-8">
<div role="radiogroup" aria-label="Billing period" class="relative mx-auto mb-7 grid w-fit grid-cols-2 gap-1 rounded-full bg-tbg04-well p-1">
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-m" class="sr-only" checked>Monthly</label>
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-y" class="sr-only">Yearly <span class="rounded-full bg-white px-2 py-0.5 text-[11px] font-extrabold text-tbg04-acc-d">Save 20%</span></label>
<span aria-hidden="true" class="pointer-events-none absolute top-1 bottom-1 left-1 z-0 w-[calc(50%-0.375rem)] rounded-full bg-tbg04-ink shadow-[0_6px_16px_-6px_rgb(6_60_58/0.7)] transition-transform duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-has-[#tbg04-y:checked]/card:translate-x-[calc(100%+0.25rem)] motion-reduce:transition-none"></span>
</div>
<div class="grid grid-cols-2 gap-3.5 @max-md:grid-cols-1">
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-line p-5">
<p class="text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Starter</p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$12</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$115</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Per seat, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $29 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl border-[1.5px] border-tbg04-line text-[14.5px] font-bold transition hover:border-tbg04-ink focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Start free trial</button>
</div>
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-acc bg-linear-to-b from-tbg04-tint to-white p-5">
<p class="flex items-center justify-between text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Studio <span class="rounded-full bg-tbg04-ink px-2 py-0.5 text-[10.5px] tracking-[0.06em] text-white">Popular</span></p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$29</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$278</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Unlimited projects, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $70 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl bg-tbg04-ink text-[14.5px] font-bold text-white transition hover:brightness-125 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Get Studio</button>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
width: 100%;
min-height: 100svh;
}
```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 Toggle — Monthly / Yearly Billing Switch (No JS)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-pricing-toggle-monthly-yearly/
The SaaS pricing switch, solved in markup: two radios drive a sliding pill, the prices on every plan card and the savings line beneath them — all through group-has-[#tbg04-y:checked]/card: variants on the elements that change. The inactive price is invisible, not just transparent, so screen readers only ever announce the price you can see. No JavaScript.
## HTML
```html
<section class="tbg-04 w-full min-h-svh grid place-items-center bg-tbg04-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg04-glow),transparent_70%)] px-5 py-14 font-tbg04-sans text-tbg04-ink" aria-label="Monthly yearly pricing toggle demo">
<div class="group/card @container w-full max-w-[36rem] rounded-3xl bg-white px-6 pt-8 pb-7 shadow-tbg04-card sm:px-8">
<div role="radiogroup" aria-label="Billing period" class="relative mx-auto mb-7 grid w-fit grid-cols-2 gap-1 rounded-full bg-tbg04-well p-1">
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-m" class="sr-only" checked>Monthly</label>
<label class="relative z-1 flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-full px-5 text-[15px] font-semibold whitespace-nowrap text-tbg04-mut transition-colors duration-300 has-checked:text-white has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg04-acc"><input type="radio" name="tbg04" id="tbg04-y" class="sr-only">Yearly <span class="rounded-full bg-white px-2 py-0.5 text-[11px] font-extrabold text-tbg04-acc-d">Save 20%</span></label>
<span aria-hidden="true" class="pointer-events-none absolute top-1 bottom-1 left-1 z-0 w-[calc(50%-0.375rem)] rounded-full bg-tbg04-ink shadow-[0_6px_16px_-6px_rgb(6_60_58/0.7)] transition-transform duration-350 ease-[cubic-bezier(.4,0,.2,1)] group-has-[#tbg04-y:checked]/card:translate-x-[calc(100%+0.25rem)] motion-reduce:transition-none"></span>
</div>
<div class="grid grid-cols-2 gap-3.5 @max-md:grid-cols-1">
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-line p-5">
<p class="text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Starter</p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$12</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$115</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Per seat, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $29 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl border-[1.5px] border-tbg04-line text-[14.5px] font-bold transition hover:border-tbg04-ink focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Start free trial</button>
</div>
<div class="flex flex-col rounded-2xl border-[1.5px] border-tbg04-acc bg-linear-to-b from-tbg04-tint to-white p-5">
<p class="flex items-center justify-between text-[13px] font-extrabold uppercase tracking-[0.1em] text-tbg04-acc-d">Studio <span class="rounded-full bg-tbg04-ink px-2 py-0.5 text-[10.5px] tracking-[0.06em] text-white">Popular</span></p>
<p class="relative mt-2 mb-1 grid h-11 text-[15px] text-tbg04-mut">
<span class="col-start-1 row-start-1 flex items-baseline transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:invisible group-has-[#tbg04-y:checked]/card:opacity-0"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$29</b>/mo</span>
<span class="invisible col-start-1 row-start-1 flex items-baseline opacity-0 transition-[opacity,visibility] duration-300 group-has-[#tbg04-y:checked]/card:visible group-has-[#tbg04-y:checked]/card:opacity-100"><b class="mr-0.5 text-[34px] font-extrabold tracking-[-0.02em] text-tbg04-ink">$278</b>/yr</span>
</p>
<p class="grid text-[13px] leading-normal text-tbg04-mut"><span class="col-start-1 row-start-1 group-has-[#tbg04-y:checked]/card:invisible">Unlimited projects, billed monthly.</span><span class="invisible col-start-1 row-start-1 font-semibold text-tbg04-acc-d group-has-[#tbg04-y:checked]/card:visible">You save $70 a year.</span></p>
<button type="button" class="mt-5 min-h-11 cursor-pointer rounded-xl bg-tbg04-ink text-[14.5px] font-bold text-white transition hover:brightness-125 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg04-acc">Get Studio</button>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbg04-bg: #d6efe9;
--color-tbg04-glow: #f1fcf9;
--color-tbg04-well: #e4f1ee;
--color-tbg04-line: #dbe9e5;
--color-tbg04-tint: #effaf7;
--color-tbg04-ink: #0d2b2e;
--color-tbg04-mut: #4f6c69;
--color-tbg04-mut: oklch(0.49 0.03 190);
--color-tbg04-acc: #1fb5b0;
--color-tbg04-acc: oklch(0.68 0.12 190);
--color-tbg04-acc-d: #0b7f7c;
--color-tbg04-acc-d: oklch(0.52 0.09 192);
--shadow-tbg04-card: 0 30px 70px -40px rgb(6 60 58 / 0.5);
--font-tbg04-sans: 'Manrope', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-04 {
width: 100%;
min-height: 100svh;
}
```How this works
One card-level group does everything. The card is group/card; anything inside can react to the yearly radio:
<span class="group-has-[#tbg04-y:checked]/card:invisible
group-has-[#tbg04-y:checked]/card:opacity-0">$12</span>
<span class="invisible opacity-0
group-has-[#tbg04-y:checked]/card:visible ...">$115</span>Why visibility, not just opacity. The CSS original faded the inactive price to opacity:0 inside an aria-live region, so assistive tech read both prices. invisible removes it; transitioning visibility alongside opacity keeps the cross-fade.
Make it yours
- Change the discount: edit the yearly prices and the “Save 20%” badge text.
- Three tiers: add a plan card and change the grid to
grid-cols-3 @max-2xl:grid-cols-1. - Default to yearly: move
checkedto the#tbg04-yradio.
Gotchas — read before shipping
- IDs in
group-has-[#…]are global — rename them if you render two toggles on one page. - Keep both prices in the DOM for no-JS, but never let both be perceivable at once.
- The thumb uses
translate-x-fullof its own width, so both options must stay the same width (grid-cols-2).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() is Baseline 2023; container queries 2023. The floor is Tailwind v4's sheet.