Black Weeks. Full Access for 50% OFF. Use code lex50 at checkout.

You'll get every theme available plus future additions. That's 40 themes total.Unlimited projects. Lifetime updates. One payment.

Get full access

How to build a responsive pricing table with Tailwind CSS and Alpine.js for pricing toggle.

Recreate a three-tier enterprise pricing table with a billing toggle, grouped feature lists, and a desktop-only layout using Tailwind CSS plus a sprinkle of Alpine.js.

Published on November 26, 2025 by Michael Andreuzza

Pricing conversations usually happen on large screens, so this layout hides the table on smaller breakpoints and shows a clear “desktop only” prompt. Alpine.js drives the monthly/annual toggle, while Tailwind handles all the spacing, typography, and highlights.

Table anatomy

  • Toggle component animates between monthly/annual prices with a sliding background.
  • Three tiers (Core, Momentum, Growth) stay left-aligned so long plan names don’t stretch the cells.
  • Table body is grouped into Usage and Features sections via scope="colgroup" rows, making the copy scannable for screen readers.
  • Highlight states rely on bg-blue-50/border-blue-100 and text-white to emphasize the middle tier.

1. Initialize Alpine state and wrapper

Alpine keeps the currently selected billing duration and lets you reuse that value anywhere in the markup.

<div x-data="{ duration: 'monthly' }" class="space-y-6">
  <!-- Prompt + table live here -->
</div>
  • Default to monthly so the toggle renders with the left button active.
  • Use space-y-6 (or gap-y-*) to add breathing room above the table.

2. Desktop-only prompt and heading row

A short message warns mobile visitors that the data is best read on desktop. The header grid aligns the marketing copy on the left and the billing toggle on the right.

<p class="text-center text-zinc-500 lg:hidden">Pricing table for desktop use</p>
<div class="hidden lg:block">
  <div
    class="grid grid-cols-1 gap-12 md:grid-cols-2 md:items-end text-center md:text-left"
  >
    <h1
      class="text-2xl md:text-3xl lg:text-4xl font-medium tracking-tight text-zinc-900 text-balance"
    >
      Equip your business with world class software
    </h1>
    <!-- toggle goes here -->
  </div>
</div>
  • lg:hidden keeps the notice on mobile while the actual table stays hidden.
  • text-balance (Tailwind plugin) prevents awkward wrapping in the main heading.

3. Build the toggle with Alpine bindings

A sliding div indicates the active option. x-text pulls the right price from data-* attributes, so you don’t need conditional logic.

<div
  aria-labelledby="pricing-toggle"
  class="relative z-0 inline-flex justify-center w-full p-1 overflow-hidden bg-white shadow ring-1 ring-zinc-200 ring-offset-2 gap-4 rounded-md max-w-52 lg:ml-auto"
>
  <div
    class="absolute inset-0 bg-zinc-50 rounded-md transition-transform duration-200 ease-in-out"
    :class="duration === 'monthly' ? 'translate-x-0 w-1/2' : 'translate-x-full w-1/2'"
  ></div>
  <button
    id="pricing-toggle"
    class="relative z-10 flex items-center justify-center w-full h-6 px-2 text-xs font-medium transition-colors"
    :class="duration === 'monthly' ? 'text-zinc-600' : 'hover:text-zinc-900'"
    type="button"
    @click="duration = 'monthly'"
    :aria-pressed="duration === 'monthly'"
  >
    Monthly
  </button>
  <button
    class="relative z-10 flex items-center justify-center w-full h-6 px-2 text-xs font-medium transition-colors"
    :class="duration === 'annual' ? 'text-zinc-600' : 'hover:text-zinc-900'"
    type="button"
    @click="duration = 'annual'"
    :aria-pressed="duration === 'annual'"
  >
    Annual
  </button>
</div>
  • The animated background sits absolutely positioned underneath the buttons.
  • Alpine toggles duration with a single click handler and reuses the value across the table.

4. Price columns with data attributes

Each plan uses a span with data-monthly/data-annual values. Alpine swaps the number via x-text, and the smaller copy explains whether the billing unit is monthly or annual.

<span
  data-monthly="$49"
  data-annual="$39"
  x-text="$el.dataset[duration]"
></span>
  • Keep the span inside a flex container so the price aligns next to the billing cadence.
  • Use font-medium + text-4xl to balance weight with the supporting copy below.

5. Usage + features sections

Two major groups break up the dense data. Each th row uses an icon and label on the left, while the plan cells stay consistent with px-2 text-xs font-medium classes. Highlighted cells use bg-blue-50 and border-blue-100 to reinforce the recommended tier.

<tr class="overflow-hidden">
  <th
    scope="colgroup"
    colspan="4"
    class="py-4 text-base font-medium text-left leading-6 text-zinc-900 border-y border-zinc-200"
  >
    Usage
  </th>
</tr>
  • scope="colgroup" tells screen readers the row is descriptive, not data.
  • Add border-t to every following row so the sections stay separated.

6. Copy-and-paste markup

Drop the entire snippet below anywhere inside your Astro/HTML template. Alpine only needs to be initialized once per page.

<div x-data="{ duration: 'monthly' }" class="space-y-6">
  <p class="text-center text-zinc-500 lg:hidden">
    Pricing table for desktop use
  </p>
  <!---- Use only from xl: -->
  <div class="hidden lg:block">
    <div
      class="text-center md:text-left grid grid-cols-1 gap-12 md:grid-cols-2 md:items-end"
    >
      <h1
        class="text-2xl md:text-3xl lg:text-4xl font-medium tracking-tight text-zinc-900 text-balance"
      >
        Equip your business with world class software
      </h1>
      <div
        aria-labelledby="pricing-toggle"
        class="relative z-0 inline-flex justify-center w-full p-1 overflow-hidden bg-white shadow ring-1 ring-zinc-200 ring-offset-2 gap-4 rounded-md max-w-52 lg:ml-auto"
      >
        <div
          class="absolute inset-0 bg-zinc-50 rounded-md transition-transform duration-200 ease-in-out"
          :class="duration === 'monthly' ? 'w-1/2 translate-x-0' : 'w-1/2 translate-x-full'"
        ></div>
        <button
          class="relative z-10 flex items-center justify-center w-full h-6 px-2 text-xs font-medium focus:outline-none transition-colors duration-300"
          :class="duration === 'monthly' ? 'text-zinc-600 ' : 'focus:text-zinc-900 hover:text-zinc-900   '"
          @click="duration = 'monthly'"
          type="button"
          :aria-pressed="duration === 'monthly'"
        >
          Monthly
        </button>
        <button
          class="relative z-10 flex items-center justify-center w-full h-6 px-2 text-xs font-medium focus:outline-none transition-colors duration-300"
          :class="duration === 'annual' ? 'text-zinc-600 ' : 'focus:text-zinc-900 hover:text-zinc-900   '"
          @click="duration = 'annual'"
          type="button"
          :aria-pressed="duration === 'annual'"
        >
          Annual
        </button>
      </div>
    </div>
    <table class="w-full mx-auto mt-8 text-center bg-transparent">
      <thead>
        <tr class="align-top">
          <th class="w-1/4"></th>
          <th class="relative w-1/4 p-1 pr-0 font-normal text-left">
            <div
              aria-hidden="true"
              class="absolute inset-y-0 right-0 hidden w-full"
            ></div>
            <div class="relative pb-4 pr-2">
              <div class="flex items-start justify-between w-full">
                <div>
                  <h3 class="text-base font-semibold text-zinc-900">Core</h3>
                  <p class="text-sm mt-1 text-zinc-500">For individuals</p>
                </div>
              </div>
              <div class="mt-8">
                <p
                  class="text-4xl font-medium sm:text-xl md:text-4xl text-zinc-900"
                ></p>
                <div class="flex items-center gap-x-4">
                  <p
                    class="text-4xl font-medium sm:text-xl md:text-4xl text-zinc-900"
                  >
                    <span
                      data-monthly="$29"
                      data-annual="$19"
                      x-text="$el.dataset[duration]"
                    ></span>
                  </p>
                  <div class="text-xs font-medium capitalize text-zinc-900">
                    <span x-show="duration === 'monthly'">
                      <span x-show="duration === 'monthly'"> month</span></span
                    >
                    <span x-show="duration === 'annual'" style="display: none">
                      annually</span
                    >
                    <p class="text-sm text-zinc-500">USD + Local taxes</p>
                  </div>
                </div>
                <div class="mt-8">
                  <button
                    class="relative flex items-center justify-center text-center font-medium transition-colors duration-200 ease-in-out select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:z-10 justify-center rounded-md text-zinc-700 bg-white outline outline-zinc-200 hover:shadow-sm hover:bg-zinc-50 focus-visible:outline-zinc-900 h-9 px-4 text-sm w-full"
                  >
                    Get Started
                  </button>
                </div>
              </div>
            </div>
          </th>
          <th class="relative w-1/4 p-1 px-0 font-normal text-left">
            <div
              aria-hidden="true"
              class="absolute inset-y-0 right-0 hidden w-full sm:block"
            ></div>
            <div class="relative px-2 pb-4">
              <div>
                <h3 class="text-base font-medium text-zinc-900">Momentum</h3>
                <p class="text-sm mt-1 text-zinc-500">For small businesses</p>
              </div>
              <div class="mt-8">
                <div class="flex items-center gap-x-4">
                  <p
                    class="text-4xl font-medium text-zinc-900 sm:text-xl md:text-4xl"
                  >
                    <span
                      data-monthly="$49"
                      data-annual="$39"
                      x-text="$el.dataset[duration]"
                    ></span>
                  </p>
                  <div class="text-xs font-medium capitalize text-zinc-900">
                    <span x-show="duration === 'monthly'">
                      <span x-show="duration === 'monthly'"> month</span></span
                    >
                    <span x-show="duration === 'annual'" style="display: none">
                      annually</span
                    >
                    <p class="text-sm text-zinc-500">USD + Local taxes</p>
                  </div>
                </div>
                <div class="mt-8">
                  <button
                    class="relative flex items-center justify-center text-center font-medium transition-colors duration-200 ease-in-out select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:z-10 justify-center rounded-md text-white bg-zinc-900 outline outline-zinc-900 hover:bg-zinc-950 focus-visible:outline-zinc-950 h-9 px-4 text-sm w-full"
                  >
                    Get Started
                  </button>
                </div>
              </div>
            </div>
          </th>
          <th class="relative w-1/4 p-1 pl-0 font-normal text-left">
            <div
              aria-hidden="true"
              class="absolute inset-y-0 right-0 hidden w-full rounded-tr-xl sm:block"
            ></div>
            <div class="relative pb-4 pl-2">
              <div class="flex items-start justify-between w-full">
                <div>
                  <h3 class="text-base font-semibold text-zinc-900">Growth</h3>
                  <p class="text-sm mt-1 text-zinc-500">For big companies</p>
                </div>
              </div>
              <div class="mt-8">
                <p
                  class="text-4xl font-medium sm:text-xl md:text-4xl text-zinc-900"
                ></p>
                <div class="flex items-center gap-x-4">
                  <p
                    class="text-4xl font-medium sm:text-xl md:text-4xl text-zinc-900"
                  >
                    <span
                      data-monthly="$99"
                      data-annual="$129"
                      x-text="$el.dataset[duration]"
                    ></span>
                  </p>
                  <div class="text-xs font-medium capitalize text-zinc-900">
                    <span x-show="duration === 'monthly'">
                      <span x-show="duration === 'monthly'"> month</span></span
                    >
                    <span x-show="duration === 'annual'" style="display: none">
                      annually</span
                    >
                    <p class="text-sm text-zinc-500">USD + Local taxes</p>
                  </div>
                </div>
                <div class="mt-8">
                  <button
                    class="relative flex items-center justify-center text-center font-medium transition-colors duration-200 ease-in-out select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:z-10 justify-center rounded-md text-zinc-700 bg-white outline outline-zinc-200 hover:shadow-sm hover:bg-zinc-50 focus-visible:outline-zinc-900 h-9 px-4 text-sm w-full"
                  >
                    Get Started
                  </button>
                </div>
              </div>
            </div>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr class="overflow-hidden">
          <th
            scope="colgroup"
            colspan="4"
            class="py-4 text-base font-medium text-left leading-6 text-zinc-900 border-y border-zinc-200"
          >
            Usage
          </th>
        </tr>
        <tr>
          <th class="py-3 text-sm font-medium text-left text-zinc-500">
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path>
              </svg>
              Analytics
            </div>
          </th>
          <td class="px-2 text-xs font-medium text-zinc-900">3</td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            10
          </td>
          <td class="px-2 text-xs font-medium text-zinc-900">Unlimited</td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Accounts
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            5
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            10
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Integrations
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            2
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            YES
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Programs
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            NO
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            1
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Deployments
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            NO
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            YES
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Advanced Checkpoints
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            NO
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            YES
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Reporting tools
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            NO
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            YES
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Support
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Discord community
          </td>
          <td
            class="px-2 text-xs font-medium text-white border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            Live chat
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Unlimited
          </td>
        </tr>
        <tr class="overflow-hidden">
          <th
            scope="colgroup"
            colspan="4"
            class="py-4 text-base font-medium text-left leading-6 text-zinc-900 border-y border-zinc-200"
          >
            Features
          </th>
        </tr>
        <tr>
          <th class="py-3 text-sm font-medium text-left text-zinc-500">
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path>
              </svg>
              Service agreement
            </div>
          </th>
          <td class="px-2 text-xs font-medium text-zinc-900">No</td>
          <td
            class="px-2 text-xs font-medium text-white border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            No
          </td>
          <td class="px-2 text-xs font-medium text-zinc-900">YES</td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Dedicted Manager
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            No
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            Yes
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Yes
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Onboarding sessions
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            No
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            No
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200"
          >
            Yes
          </td>
        </tr>
        <tr>
          <th
            class="py-3 text-sm font-medium text-left border-t text-zinc-900 border-zinc-200"
          >
            <div class="flex items-center gap-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="icon icon-tabler-circle-check size-4"
              >
                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                <path d="M5 12l5 5l10 -10"></path></svg
              >Dedicated support channel
            </div>
          </th>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200 rounded-bl-xl"
          >
            No
          </td>
          <td
            class="px-2 text-xs font-medium border-t border-blue-100 bg-blue-50 text-zinc-900"
          >
            No
          </td>
          <td
            class="px-2 text-xs font-medium border-t text-zinc-900 border-zinc-200 rounded-br-xl"
          >
            Yes
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Finishing touches

  • Keep the pricing copy editable: change the numbers on the data-* attributes and Alpine will update everywhere.
  • If you need a mobile-friendly version, duplicate the plan data into stacked cards inside a lg:hidden block and reuse the same Alpine state.
  • Turn the Get Started buttons into <a> tags when you have specific plan URLs; ensure each one has aria-label text if the button copy stays generic.

/Michael Andreuzza

Did you like this post? Please share it with your friends!