Blog 11 min read

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.

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-background 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.

html
<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.

html
<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.

html
<div
  aria-labelledby="pricing-toggle"
  class="relative z-0 inline-flex justify-center w-full p-1 overflow-hidden bg-background 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.

html
<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.

html
<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.

html
<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-background 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-background 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-background 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-background 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-background 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-background 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