SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/pricing-tables

8 CSS Pricing Tables

6 Pure CSS2 with JSPublished 2026-10-04Free · no sign-up

Eight ways to present prices: three plan cards with a highlighted favourite, a monthly and yearly switch, a feature comparison table, a single premium card, plan rows for settings pages, a usage-based calculator, a minimal two-plan layout and plans with an enterprise contact strip.

01. Three-Tier Pricing Cards

PricingCards

The classic three-plan layout with the middle plan raised and labelled Most popular. Features are real lists, prices use tabular numbers so they line up, and the cards stack into one column on narrow screens without any media query thanks to auto-fit.

View code
<div class="pt3">
  <article><h3>Basic</h3><p class="pr">$12<span>/month</span></p><ul><li>1 online store</li><li>Up to 50 products</li><li>Email support</li></ul><a href="#">Choose Basic</a></article>
  <article class="pop"><p class="tag">Most popular</p><h3>Pro</h3><p class="pr">$32<span>/month</span></p><ul><li>3 online stores</li><li>Unlimited products</li><li>Abandoned cart emails</li><li>Chat support</li></ul><a href="#">Choose Pro</a></article>
  <article><h3>Business</h3><p class="pr">$89<span>/month</span></p><ul><li>10 online stores</li><li>Staff accounts</li><li>Priority phone support</li></ul><a href="#">Choose Business</a></article>
</div>
.pt3 { box-sizing: border-box; max-width: 100%; display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; width: min(680px, 94vw); align-items: start; font-family: Inter, system-ui, sans-serif; }
.pt3 article { padding: 22px 20px; border: 1px solid #e4e7ec; border-radius: 18px; background: #fff; color: #475467; }
.pt3 h3 { margin: 0; color: #101828; font-size: 17px; }
.pr { margin: 8px 0 14px; color: #101828; font-size: 32px; font-weight: 800; font-variant-numeric: tabular-nums; }
.pr span { color: #667085; font-size: 14px; font-weight: 500; }
.pt3 ul { display: grid; gap: 8px; margin: 0 0 18px; padding: 0; list-style: none; font-size: 14px; }
.pt3 li::before { content: "\2713"; margin-right: 8px; color: #15803d; font-weight: 800; }
.pt3 a { display: block; padding: 11px; border: 1.5px solid #d0d5dd; border-radius: 10px; color: #101828; font-weight: 700; font-size: 14px; text-align: center; text-decoration: none; }
.pop { border: 2px solid #4f46e5 !important; box-shadow: 0 18px 40px -20px rgba(79,70,229,.6); transform: translateY(-8px); }
.pop a { border-color: #4f46e5; background: #4f46e5; color: #fff; }
.tag { display: inline-block; margin: 0 0 10px; padding: 3px 10px; border-radius: 999px; background: #eef2ff; color: #4338ca; font-size: 12px; font-weight: 700; }

02. Pricing with Billing Toggle

PricingJavaScript

Two plans with a Monthly and Yearly switch that updates every price. Prices are stored in data attributes, a few lines of JavaScript swap them, and the yearly saving is spelled out in words so the discount is clear without relying on a strike-through.

View code
<div class="ptt">
  <div class="per" role="radiogroup" aria-label="Billing period">
    <button role="radio" aria-checked="true" data-p="m">Monthly</button>
    <button role="radio" aria-checked="false" data-p="y">Yearly (2 months free)</button>
  </div>
  <div class="cards">
    <article><h3>Creator</h3><p class="amt" data-m="$15" data-y="$150">$15</p><p class="unit">per month</p><a href="#">Start free trial</a></article>
    <article><h3>Studio</h3><p class="amt" data-m="$39" data-y="$390">$39</p><p class="unit">per month</p><a href="#">Start free trial</a></article>
  </div>
</div>
.ptt { box-sizing: border-box; max-width: 100%; display: grid; gap: 18px; justify-items: center; width: min(560px, 94vw); font-family: Inter, system-ui, sans-serif; }
.per { display: inline-flex; padding: 4px; border-radius: 999px; background: #1a1f2c; }
.per button { padding: 9px 16px; border: 0; border-radius: 999px; background: none; color: #9aa4b8; font: 600 13px Inter, sans-serif; cursor: pointer; }
.per button[aria-checked="true"] { background: #fff; color: #0e1118; }
.per button:focus-visible { outline: 2px solid #a5b4fc; outline-offset: 2px; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; width: 100%; }
.cards article { padding: 22px; border: 1px solid #262d3e; border-radius: 18px; background: #12161f; color: #aab3c7; }
.cards h3 { margin: 0 0 6px; color: #fff; font-size: 17px; }
.amt { margin: 0; color: #fff; font-size: 36px; font-weight: 800; }
.unit { margin: 2px 0 18px; font-size: 14px; }
.cards a { display: block; padding: 11px; border-radius: 10px; background: #4f46e5; color: #fff; font-weight: 700; font-size: 14px; text-align: center; text-decoration: none; }
const btns = document.querySelectorAll('.per button');
btns.forEach(b => b.addEventListener('click', () => {
  btns.forEach(x => x.setAttribute('aria-checked', String(x === b)));
  const y = b.dataset.p === 'y';
  document.querySelectorAll('.amt').forEach(a => a.textContent = y ? a.dataset.y : a.dataset.m);
  document.querySelectorAll('.unit').forEach(u => u.textContent = y ? 'per year' : 'per month');
}));

03. Feature Comparison Table

TablePricing

A real HTML table comparing three plans feature by feature. Column and row headers use th with scope so screen readers announce which plan and feature each cell belongs to, and included features show a tick with hidden text that says Included.

View code
<table class="cmp">
  <caption>Compare plans</caption>
  <thead><tr><th scope="col">Feature</th><th scope="col">Free</th><th scope="col">Plus</th><th scope="col">Team</th></tr></thead>
  <tbody>
    <tr><th scope="row">Projects</th><td>3</td><td>Unlimited</td><td>Unlimited</td></tr>
    <tr><th scope="row">Custom domain</th><td>&mdash;<span class="sr">Not included</span></td><td>&#10003;<span class="sr">Included</span></td><td>&#10003;<span class="sr">Included</span></td></tr>
    <tr><th scope="row">Team members</th><td>1</td><td>1</td><td>Up to 20</td></tr>
    <tr><th scope="row">Version history</th><td>7 days</td><td>90 days</td><td>Forever</td></tr>
  </tbody>
</table>
.cmp { width: min(560px, 94vw); border-collapse: collapse; overflow: hidden; border-radius: 14px; background: #fff; box-shadow: 0 10px 30px -14px rgba(16,24,40,.3); font: 14px Inter, system-ui, sans-serif; color: #344054; }
.cmp caption { padding: 0 0 12px; color: #101828; font-weight: 800; font-size: 16px; text-align: left; }
.cmp th, .cmp td { padding: 12px 14px; border-bottom: 1px solid #eef0f4; text-align: center; }
.cmp thead th { background: #f9fafb; color: #101828; font-weight: 700; }
.cmp tbody th { color: #101828; font-weight: 600; text-align: left; }
.cmp td:nth-child(3), .cmp thead th:nth-child(3) { background: #f5f3ff; }
.cmp tr:last-child th, .cmp tr:last-child td { border-bottom: 0; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

04. Dark Glow Pricing Card

DarkGlow

A single premium plan card for dark landing pages, with a gradient border made from a background on a wrapper and a soft glow beneath. The price, a short promise and the call to action are kept to a tight vertical rhythm.

View code
<div class="glowcard">
  <article>
    <p class="nm">Lifetime access</p>
    <p class="pc">$249 <span>one payment</span></p>
    <p class="ds">Every template we release, now and later, for one store.</p>
    <ul><li>All 120+ templates</li><li>Future updates included</li><li>Commercial licence</li></ul>
    <a href="#">Get lifetime access</a>
  </article>
</div>
.glowcard { box-sizing: border-box; max-width: 100%; padding: 1.5px; border-radius: 22px; background: linear-gradient(135deg, #22d3ee, #a855f7 60%, #ec4899); box-shadow: 0 30px 60px -30px rgba(168,85,247,.7); font-family: Inter, system-ui, sans-serif; }
.glowcard article { box-sizing: border-box; max-width: 100%; width: min(320px, 86vw); padding: 26px; border-radius: 21px; background: #0f1220; color: #aab3c7; }
.nm { margin: 0; color: #e9d5ff; font-size: 13px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.pc { margin: 10px 0 8px; color: #fff; font-size: 40px; font-weight: 800; }
.pc span { color: #9aa4b8; font-size: 14px; font-weight: 500; }
.ds { margin: 0 0 16px; font-size: 14px; line-height: 1.55; }
.glowcard ul { display: grid; gap: 8px; margin: 0 0 20px; padding: 0; list-style: none; font-size: 14px; }
.glowcard li::before { content: "\2726"; margin-right: 8px; color: #22d3ee; }
.glowcard a { display: block; padding: 12px; border-radius: 12px; background: linear-gradient(90deg, #22d3ee, #a855f7); color: #0b0716; font-weight: 800; font-size: 15px; text-align: center; text-decoration: none; }

05. Horizontal Plan Rows

PricingList

Plans laid out as wide rows instead of cards, which works well inside account settings when someone is changing plan. The current plan is marked with a badge and a disabled button, and the rows collapse into stacked blocks on phones.

View code
<ul class="rows">
  <li><div><h3>Hobby <span class="cur">Current plan</span></h3><p>Personal projects and testing.</p></div><p class="pp">Free</p><button disabled>Selected</button></li>
  <li><div><h3>Pro</h3><p>For freelancers with paying clients.</p></div><p class="pp">$18/mo</p><button>Upgrade</button></li>
  <li><div><h3>Agency</h3><p>Client workspaces and white-label reports.</p></div><p class="pp">$64/mo</p><button>Upgrade</button></li>
</ul>
.rows { display: grid; gap: 10px; width: min(600px, 94vw); margin: 0; padding: 0; list-style: none; font-family: Inter, system-ui, sans-serif; }
.rows li { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 18px; padding: 16px 18px; border: 1px solid #e4e7ec; border-radius: 14px; background: #fff; }
.rows h3 { margin: 0; color: #101828; font-size: 16px; }
.rows p { margin: 2px 0 0; color: #5d6679; font-size: 13.5px; }
.cur { margin-left: 6px; padding: 2px 8px; border-radius: 999px; background: #ecfdf3; color: #067647; font-size: 11px; font-weight: 700; vertical-align: middle; }
.rows .pp { margin: 0; color: #101828; font-weight: 800; font-size: 16px; }
.rows button { padding: 9px 16px; border: 0; border-radius: 10px; background: #101828; color: #fff; font: 700 13.5px Inter, sans-serif; cursor: pointer; }
.rows button:disabled { background: #f2f4f7; color: #5d6679; cursor: default; }
@media (max-width: 520px) { .rows li { grid-template-columns: 1fr; gap: 8px; } }

06. Usage-Based Price Calculator

PricingSlider

Pricing that depends on volume: drag the slider and the monthly cost updates live from a simple tiered formula. The result is an output element linked to the range input, which screen readers treat as a live calculation.

View code
<form class="calc" oninput="o.value = price(+n.value)">
  <label for="n">Orders per month: <b id="nv">1,000</b></label>
  <input id="n" name="n" type="range" min="100" max="10000" step="100" value="1000">
  <p>Estimated cost <output name="o" for="n">$40</output><span>/month</span></p>
  <small>$0.04 per order up to 2,000, then $0.025 per order.</small>
</form>
.calc { box-sizing: border-box; max-width: 100%; display: grid; gap: 12px; width: min(380px, 90vw); padding: 24px; border: 1px solid #262d3e; border-radius: 18px; background: #12161f; color: #aab3c7; font: 14px Inter, system-ui, sans-serif; }
.calc label b { color: #fff; }
.calc input { width: 100%; accent-color: #f59e0b; }
.calc p { margin: 0; }
.calc output { margin-left: 8px; color: #fff; font-size: 32px; font-weight: 800; }
.calc small { color: #8b95a9; }
function price(n) {
  const cost = n <= 2000 ? n * 0.04 : 2000 * 0.04 + (n - 2000) * 0.025;
  document.getElementById('nv').textContent = n.toLocaleString('en-US');
  return '$' + cost.toFixed(0);
}

07. Minimal Two-Plan Pricing

MinimalPricing

Two plans separated by a single vertical rule, with no card boxes at all. Typography does the work: a large price, a one-line summary and a text-style link for the call to action. Useful for portfolio and personal service pages.

View code
<div class="mp">
  <section><h3>One-off</h3><p class="mpp">$450</p><p>A single landing page, designed and built in a week.</p><a href="#">Book a slot &rarr;</a></section>
  <section><h3>Monthly</h3><p class="mpp">$1,200</p><p>Ongoing design and development, up to 30 hours.</p><a href="#">Start a retainer &rarr;</a></section>
</div>
.mp { display: grid; grid-template-columns: 1fr 1fr; width: min(560px, 94vw); font-family: Inter, system-ui, sans-serif; color: #475467; }
.mp section { padding: 6px 28px; }
.mp section + section { border-left: 1px solid #d0d5dd; }
.mp h3 { margin: 0; color: #5d6679; font-size: 13px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }
.mpp { margin: 6px 0 8px; color: #101828; font-size: 38px; font-weight: 800; letter-spacing: -.02em; }
.mp p { font-size: 14.5px; line-height: 1.55; }
.mp a { color: #101828; font-weight: 700; text-decoration: underline; text-underline-offset: 4px; }
@media (max-width: 520px) { .mp { grid-template-columns: 1fr; } .mp section + section { border-left: 0; border-top: 1px solid #d0d5dd; margin-top: 18px; padding-top: 18px; } }

08. Pricing with Enterprise Contact

PricingB2B

Two self-serve plans plus a wide enterprise strip underneath that asks people to talk to sales instead of showing a price. The strip spans the full grid width with grid-column: 1 / -1, and its button leads to a contact page.

View code
<div class="ent">
  <article><h3>Team</h3><p class="v">$20 <span>per seat / month</span></p><a href="#">Start trial</a></article>
  <article><h3>Business</h3><p class="v">$45 <span>per seat / month</span></p><a href="#">Start trial</a></article>
  <article class="wide"><div><h3>Enterprise</h3><p>Single sign-on, audit logs, custom contracts and a named account manager.</p></div><a href="#">Talk to sales</a></article>
</div>
.ent { box-sizing: border-box; max-width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 12px; width: min(580px, 94vw); font-family: Inter, system-ui, sans-serif; }
.ent article { padding: 20px; border: 1px solid #262d3e; border-radius: 16px; background: #12161f; color: #aab3c7; }
.ent h3 { margin: 0 0 6px; color: #fff; font-size: 16px; }
.v { margin: 0 0 16px; color: #fff; font-size: 28px; font-weight: 800; }
.v span { color: #9aa4b8; font-size: 13px; font-weight: 500; }
.ent a { display: inline-block; padding: 10px 16px; border-radius: 10px; background: #fff; color: #0e1118; font-weight: 700; font-size: 14px; text-decoration: none; }
.wide { grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between; gap: 18px; background: linear-gradient(90deg, #172036, #12161f) !important; }
.wide p { margin: 0; font-size: 14px; }
.wide a { flex: none; background: transparent; border: 1.5px solid #fff; color: #fff; }
@media (max-width: 520px) { .ent { grid-template-columns: 1fr; } .wide { flex-direction: column; align-items: flex-start; } }

About these CSS Pricing Tables

Make the choice easy

Most pricing pages show two to four plans and highlight one. Raise or outline the recommended plan, label it with words such as Most popular, and keep the same feature order on every card so people can compare across them.

Prices that line up

font-variant-numeric: tabular-nums gives every digit the same width, so prices align across cards and the numbers do not jump when a billing switch changes them. State the period next to the price, such as per month or per seat.

Comparison tables need table markup

When you compare features across plans, use a real <table> with th scope="col" for plan names and th scope="row" for features. Screen readers then announce the plan and feature for each cell. Give ticks hidden text such as Included.

Responsive without breakpoints

grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)) fits as many cards as there is room for and drops to one column on phones by itself, so the three-tier layout needs no media query.

Frequently asked questions

Which demos use JavaScript?

The billing toggle and the usage calculator. The rest are pure HTML and CSS.

Can I add more plans?

Yes. The card grids use auto-fit, so a fourth card simply joins the row when there is space.

How do I show a discount?

State the saving in words, like 2 months free, rather than relying only on a struck-through price.