Designing SaaS Pricing Cards in HTML & Tailwind CSS

How to structure, align and style responsive SaaS pricing tables in HTML and Tailwind CSS using CSS Grid, subgrid alignment, and accessible billing toggles.

Kavi Dev

Designing SaaS Pricing Cards in HTML & Tailwind CSS

A pricing section is one of the most downloaded component categories on FreeDevTemplates, and also one of the easiest layouts to break when real copy replaces placeholder text.

In a mockup, every plan has a one-line description and exactly five short bullet points. In production, the Pro plan has eight bullets, one of them wraps onto three lines, and the CTA buttons end up at three different heights. Here is how to build a pricing grid in HTML and Tailwind CSS that holds its structure regardless of the copy inside it.

Quick summary: Use grid-cols-1 lg:grid-cols-3 gap-8 items-stretch on the container, make each card flex flex-col, and place flex-1 on the <ul> feature list so every call-to-action button sits on the same baseline.

1. Equal-Height Cards with Aligned Buttons

The most visible flaw in amateur pricing tables is ragged button alignment. You do not need fixed pixel heights (h-[520px]) to solve this — fixed heights clip text when a user increases their browser font size. Instead, combine CSS Grid's default stretch behavior with a flex column inside each card:

<section class="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8" aria-labelledby="pricing-heading">
  <div class="mx-auto max-w-2xl text-center">
    <h2 id="pricing-heading" class="text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl">
      Simple, predictable pricing
    </h2>
    <p class="mt-3 text-base text-slate-600">
      Start free for personal projects. Upgrade when you need team workspaces.
    </p>
  </div>

  <div class="mt-12 grid grid-cols-1 gap-8 lg:grid-cols-3 lg:items-stretch">
    <!-- Starter Card -->
    <article class="flex flex-col rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
      <div>
        <h3 class="text-lg font-semibold text-slate-900">Starter</h3>
        <p class="mt-2 text-sm text-slate-600">For solo developers testing an idea.</p>
        <p class="mt-6 flex items-baseline gap-x-1">
          <span class="text-4xl font-bold tracking-tight text-slate-900">$0</span>
          <span class="text-sm font-medium text-slate-500">/month</span>
        </p>
      </div>

      <ul role="list" class="mt-8 flex-1 space-y-3 text-sm leading-6 text-slate-600">
        <li class="flex gap-x-3">✓ 3 active projects</li>
        <li class="flex gap-x-3">✓ Community support</li>
        <li class="flex gap-x-3">✓ Automatic HTTPS</li>
      </ul>

      <a
        href="/signup?plan=starter"
        class="mt-8 block rounded-xl border border-slate-300 px-4 py-3 text-center text-sm font-semibold text-slate-900 hover:bg-slate-50"
      >
        Start for free
      </a>
    </article>

    <!-- Pro Card (Highlighted) -->
    <article class="relative flex flex-col rounded-2xl border-2 border-blue-600 bg-white p-8 shadow-md">
      <span class="absolute -top-3.5 left-8 rounded-full bg-blue-600 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white">
        Most popular
      </span>
      <div>
        <h3 class="text-lg font-semibold text-slate-900">Pro</h3>
        <p class="mt-2 text-sm text-slate-600">For production apps and client projects.</p>
        <p class="mt-6 flex items-baseline gap-x-1">
          <span class="text-4xl font-bold tracking-tight text-slate-900" data-price-m data-price-annual="$15">$19</span>
          <span class="text-sm font-medium text-slate-500">/month</span>
        </p>
      </div>

      <ul role="list" class="mt-8 flex-1 space-y-3 text-sm leading-6 text-slate-600">
        <li class="flex gap-x-3">✓ Unlimited projects</li>
        <li class="flex gap-x-3">✓ Custom domains &amp; redirects</li>
        <li class="flex gap-x-3">✓ Team role permissions</li>
        <li class="flex gap-x-3">✓ Priority email support</li>
        <li class="flex gap-x-3">✓ 30-day build log retention</li>
      </ul>

      <a
        href="/signup?plan=pro"
        class="mt-8 block rounded-xl bg-blue-600 px-4 py-3 text-center text-sm font-semibold text-white hover:bg-blue-700"
      >
        Start 14-day trial
      </a>
    </article>

    <!-- Team Card -->
    <article class="flex flex-col rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
      <div>
        <h3 class="text-lg font-semibold text-slate-900">Team</h3>
        <p class="mt-2 text-sm text-slate-600">For engineering teams with compliance needs.</p>
        <p class="mt-6 flex items-baseline gap-x-1">
          <span class="text-4xl font-bold tracking-tight text-slate-900">$49</span>
          <span class="text-sm font-medium text-slate-500">/month</span>
        </p>
      </div>

      <ul role="list" class="mt-8 flex-1 space-y-3 text-sm leading-6 text-slate-600">
        <li class="flex gap-x-3">✓ Everything in Pro</li>
        <li class="flex gap-x-3">✓ SAML SSO</li>
        <li class="flex gap-x-3">✓ Audit logs &amp; invoice billing</li>
      </ul>

      <a
        href="/contact-sales"
        class="mt-8 block rounded-xl border border-slate-300 px-4 py-3 text-center text-sm font-semibold text-slate-900 hover:bg-slate-50"
      >
        Contact sales
      </a>
    </article>
  </div>
</section>

Because the <div class="grid"> stretches all three <article> cards to the height of the tallest card, and flex-1 on the <ul role="list"> absorbs any leftover vertical space, the <a class="mt-8"> buttons always line up across the bottom on desktop viewports.

2. Highlighting the Recommended Plan Without Breaking Mobile

When emphasizing the middle tier, avoid using negative margins or scale-105 on all screen sizes. On a 375px mobile screen, scale-105 pushes the card 9px past the horizontal padding and causes a sideways scrollbar.

Instead:

  • Use a stronger border (border-2 border-blue-600) and a filled primary button on the highlighted tier while keeping secondary tiers outlined.
  • Anchor the "Most popular" pill with absolute -top-3.5 left-8 and add a slightly larger top margin (mt-12) on the grid so the badge never collides with the subtitle above it.

3. Accessible Monthly / Annual Billing Switch

If your template includes a monthly/annual price toggle, use a two-button segmented control (role="group") or a <button role="switch" aria-checked="false"> rather than an unlabeled <input type="checkbox">. Store both prices in data-price-monthly and data-price-annual attributes on the price <span>:

const billingButt
const priceNodes = document.querySelectorAll('[data-price-monthly]');

billingButtons.forEach((btn) => {
  btn.addEventListener('click', () => {
    const period = btn.getAttribute('data-billing-period'); // "monthly" | "annual"
    billingButtons.forEach((b) => b.setAttribute('aria-pressed', String(b === btn)));

    priceNodes.forEach((node) => {
      node.textC === 'annual'
          ? node.getAttribute('data-price-annual')
          : node.getAttribute('data-price-monthly');
    });
  });
});

Storing the strings in data-* attributes keeps the HTML self-contained when someone downloads the template and edits the numbers directly in their code editor.

Frequently Asked Questions

How do I keep CTA buttons aligned at the bottom of pricing cards with different feature counts?

Make each card a flex column (flex flex-col justify-between) or push the button down by adding mt-auto to the CTA wrapper and flex-1 to the feature list.

How many tiers should a responsive pricing grid have?

Three tiers fit comfortably in a 12-column container (grid-cols-1 lg:grid-cols-3) without cramping line length. If you have a fourth Enterprise tier, render it as a full-width horizontal banner below the three main cards.

Should the highlighted plan use scale-105 on mobile?

Only apply scale transformations at lg breakpoints (lg:scale-105). Scaling a card on mobile causes horizontal overflow or overlaps vertical spacing between stacked cards.

Follow Us

Related articles

Continue with these nearby posts.

No related articles found.

Share Your Feedback

Did you find this article helpful? We'd love to hear your thoughts and suggestions to improve our content.

Share Feedback