DEV3 Technologies home

Pricing table with monthly and yearly toggle

A 3 plan pricing grid whose prices swap between monthly and yearly billing through one accessible switch.

Tailwind CSSPricingpricingtogglesaasplans

Live preview

Code

<section class="mx-auto max-w-5xl px-4 py-16" aria-labelledby="pricing-title">
  <div class="text-center">
    <h2 id="pricing-title" class="text-3xl font-bold tracking-tight text-slate-900">Simple pricing</h2>
    <div class="mt-6 inline-flex items-center gap-3 text-sm font-medium">
      <span id="bill-monthly-label" class="text-slate-900">Monthly</span>
      <button id="bill-switch" type="button" role="switch" aria-checked="false"
        aria-labelledby="bill-monthly-label bill-yearly-label"
        class="relative h-7 w-12 rounded-full bg-slate-300 transition aria-checked:bg-indigo-600">
        <span id="bill-knob" class="absolute left-1 top-1 size-5 rounded-full bg-white shadow transition-transform"></span>
      </button>
      <span id="bill-yearly-label" class="text-slate-500">Yearly</span>
      <span id="bill-badge" class="hidden rounded-full bg-emerald-100 px-2 py-0.5 text-xs text-emerald-700">Save 20%</span>
    </div>
  </div>

  <div class="mt-10 grid gap-6 md:grid-cols-3">
    <article class="rounded-2xl border border-slate-200 p-6">
      <h3 class="font-semibold">Starter</h3>
      <p class="mt-4 text-4xl font-bold"><span data-price data-monthly="$9" data-yearly="$86">$9</span>
        <span data-unit class="text-base font-normal text-slate-500">/mo</span></p>
      <a href="#" class="mt-6 block rounded-lg bg-slate-900 py-2 text-center text-white">Choose Starter</a>
    </article>
    <article class="rounded-2xl border-2 border-indigo-600 p-6 shadow-lg">
      <h3 class="font-semibold">Pro</h3>
      <p class="mt-4 text-4xl font-bold"><span data-price data-monthly="$29" data-yearly="$278">$29</span>
        <span data-unit class="text-base font-normal text-slate-500">/mo</span></p>
      <a href="#" class="mt-6 block rounded-lg bg-indigo-600 py-2 text-center text-white">Choose Pro</a>
    </article>
    <article class="rounded-2xl border border-slate-200 p-6">
      <h3 class="font-semibold">Scale</h3>
      <p class="mt-4 text-4xl font-bold"><span data-price data-monthly="$79" data-yearly="$758">$79</span>
        <span data-unit class="text-base font-normal text-slate-500">/mo</span></p>
      <a href="#" class="mt-6 block rounded-lg bg-slate-900 py-2 text-center text-white">Choose Scale</a>
    </article>
  </div>
</section>

How it works

What it solves

Most pricing sections need a billing switch, and many implementations rebuild the whole card on every click. This one keeps the markup static and only rewrites the numbers, so the cards never reflow and the page stays crawlable with the monthly prices in the HTML.

How it works

The switch is a real button with role="switch" and aria-checked, so screen readers announce its state. Each price element carries data-monthly and data-yearly attributes. A small script reads the switch state, then writes the matching value into every [data-price] node and updates the unit label ("/mo" or "/yr"). The knob moves with a Tailwind translate-x class toggled from the same state, and the yearly saving badge appears only when the yearly option is active.

Adapting it

Add a plan by copying an article and giving its price element the two data attributes. To change the discount, edit the figures in the markup; nothing in the script is hard coded. If you bill in several currencies, add a third attribute and a second switch.

Pitfalls

  • Keep the monthly price as the visible default so the page is correct without scripts.
  • Do not compute yearly as twelve times monthly in script; marketing discounts rarely match that maths.
  • Give the switch a visible label, not only an icon, or keyboard users lose context.

How to use it

  1. Paste pricing.html into your page inside a Tailwind 4 project (the CDN build works for prototypes).
  2. Save pricing.js and load it with <script src="/js/pricing.js" defer></script>.
  3. Edit plan names, prices and the data-monthly / data-yearly values.
  4. Add or remove article cards; the grid adapts through md:grid-cols-3.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets