Skip to content

Billing Toggle

A copy-paste pricing tables component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.

Pricing TablesHTMLCSSJavaScriptany framework

Copy into your project

HTML
<!-- Billing Toggle — pill slides between Monthly / Yearly -->
<div class="nuda-billing">
  <button class="nuda-billing__opt" type="button" data-period="monthly">Monthly</button>
  <button class="nuda-billing__opt is-active" type="button" data-period="yearly">
    Yearly
    <span class="nuda-billing__save">−20%</span>
  </button>
  <span class="nuda-billing__pill"></span>
</div>
CSS
/* Billing Toggle
   Two-state pill switch with savings badge inside the inactive option.
   Customize: --bill-accent */

.nuda-billing {
  --bill-accent: #e4ff54;
  position: relative;
  display: inline-flex;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  padding: 4px;
}

.nuda-billing__opt {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  background: transparent;
  border: none;
  color: #a1a1aa;
  font: 600 0.875rem ui-sans-serif, system-ui, sans-serif;
  cursor: pointer;
  border-radius: 999px;
  transition: color 0.3s;
}

.nuda-billing__opt.is-active { color: #09090b; }

.nuda-billing__save {
  font: 800 0.625rem ui-sans-serif, system-ui, sans-serif;
  color: #09090b;
  background: rgba(9, 9, 11, 0.15);
  padding: 2px 6px;
  border-radius: 999px;
}

.nuda-billing__opt:not(.is-active) .nuda-billing__save {
  color: var(--bill-accent);
  background: rgba(228, 255, 84, 0.15);
}

.nuda-billing__pill {
  position: absolute;
  z-index: 1;
  top: 4px;
  left: 4px;
  width: calc(50% - 2px);
  height: calc(100% - 8px);
  background: var(--bill-accent);
  border-radius: 999px;
  transform: translateX(100%);
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 0 8px rgba(228, 255, 84, 0.4);
}

/* Set transform: translateX(0) for monthly, translateX(100%) for yearly */
JavaScript
/* Billing Toggle — slides the pill and updates prices. */

(function () {
  var group = document.querySelector('.nuda-billing');
  if (!group) return;
  var opts = group.querySelectorAll('.nuda-billing__opt');
  var pill = group.querySelector('.nuda-billing__pill');

  opts.forEach(function (o, i) {
    o.addEventListener('click', function () {
      opts.forEach(function (x) { x.classList.remove('is-active'); });
      o.classList.add('is-active');
      pill.style.transform = 'translateX(' + (i * 100) + '%)';

      // Notify your prices to swap (replace with your own logic).
      document.dispatchEvent(new CustomEvent('billing:change', {
        detail: { period: o.dataset.period }
      }));
    });
  });
})();

How to use Billing Toggle

Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.

Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.

More pricing tables components

← Browse all NudaUI components