21 CSS Pricing Sections03 / 21

Pure CSSMIT licensed

Feature Comparison Table

A real <table> with proper <th scope>, sticky header row, and check / dash cells comparing four plans across nine features. Cyan accent on the recommended column. Mobile fallback collapses to a stacked card list under 720px.

Published

Live Demo
Try it

The code

<section class="ps-03" aria-label="Plan comparison">
  <header class="ps-03__head">
    <h2>Compare every feature.</h2>
    <p>Pick the column that matches what you ship today.</p>
  </header>
  <div class="ps-03__scroll">
    <table class="ps-03__tbl">
      <thead>
        <tr>
          <th scope="col" class="ps-03__fc">Feature</th>
          <th scope="col">Free<br /><span>$0</span></th>
          <th scope="col" class="ps-03__rec">
            <span class="ps-03__pill">Recommended</span>Pro<br /><span>$24</span>
          </th>
          <th scope="col">Team<br /><span>$48</span></th>
          <th scope="col">Enterprise<br /><span>Contact</span></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">Projects</th>
          <td>1</td>
          <td class="ps-03__rec">10</td>
          <td>50</td>
          <td>Unlimited</td>
        </tr>
        <tr>
          <th scope="row">Storage</th>
          <td>500 MB</td>
          <td class="ps-03__rec">10 GB</td>
          <td>50 GB</td>
          <td>500 GB</td>
        </tr>
        <tr>
          <th scope="row">Custom domain</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Team roles</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Priority support</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">SSO &amp; SAML</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Audit logs</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">99.99% SLA</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Dedicated CSM</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <th scope="row" class="ps-03__fc"></th>
          <td><a class="ps-03__cta" href="#free">Get started</a></td>
          <td class="ps-03__rec">
            <a class="ps-03__cta ps-03__cta-pri" href="#pro">Choose Pro →</a>
          </td>
          <td><a class="ps-03__cta" href="#team">Choose Team</a></td>
          <td><a class="ps-03__cta" href="#ent">Talk to sales</a></td>
        </tr>
      </tfoot>
    </table>
  </div>
</section>
.ps-03 {
  padding: clamp(28px, 5vw, 56px) clamp(16px, 4vw, 40px);
  width: 100%;
  min-height: 100vh;
  background: #06141a;
  color: #e0f2f7;
  font-family: 'Inter', system-ui, sans-serif;
}

.ps-03__head {
  text-align: center;
  margin-bottom: clamp(20px, 4vw, 36px);
}

.ps-03__head h2 {
  font-size: clamp(22px, 3.2vw, 32px);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  color: #f1fbff;
}

.ps-03__head p {
  color: #88a8b3;
  margin: 0;
  font-size: 14px;
}

.ps-03__scroll {
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
  max-width: 980px;
  margin: 0 auto;
  padding-top: 14px;
}

.ps-03__tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13.5px;
  min-width: 720px;
}

.ps-03__tbl th,
.ps-03__tbl td {
  text-align: center;
  padding: 14px 12px;
  border-bottom: 1px solid rgba(34,211,238,0.1);
}

.ps-03__tbl thead th {
  font-size: 14px;
  font-weight: 700;
  color: #cfeaf2;
  background: #0c1f27;
  position: relative;
}

.ps-03__tbl thead th span:not(.ps-03__pill) {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: #88a8b3;
  margin-top: 4px;
}

.ps-03__fc {
  text-align: left !important;
  color: #88a8b3 !important;
  font-weight: 500 !important;
  background: transparent !important;
}

.ps-03__tbl tbody th {
  text-align: left;
  font-weight: 500;
  color: #cfeaf2;
}

.ps-03__rec {
  background: rgba(34,211,238,0.06);
  color: #22d3ee;
  position: relative;
}

.ps-03__tbl thead th.ps-03__rec {
  background: linear-gradient(180deg, rgba(34,211,238,0.18), rgba(34,211,238,0.04));
  color: #5eead4;
}

.ps-03__pill {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  background: #22d3ee;
  color: #06141a;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
}

.ps-03__yes {
  display: inline-block;
  width: 22px;
  height: 22px;
  line-height: 22px;
  border-radius: 50%;
  background: rgba(34,211,238,0.15);
  color: #22d3ee;
  font-weight: 700;
}

.ps-03__no {
  color: #4a606a;
  font-weight: 500;
}

.ps-03__tbl tfoot td {
  padding-top: 18px;
  padding-bottom: 4px;
  border: 0;
}

.ps-03__cta {
  display: inline-block;
  padding: 9px 16px;
  border-radius: 7px;
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid rgba(34,211,238,0.3);
  color: #5eead4;
  transition: background 0.18s, color 0.18s;
}

.ps-03__cta:hover {
  background: rgba(34,211,238,0.18);
  color: #f1fbff;
}

.ps-03__cta-pri {
  background: #22d3ee;
  color: #06141a;
  border-color: #22d3ee;
}

.ps-03__cta-pri:hover {
  background: #5eead4;
  color: #06141a;
}

.ps-03__cta:focus-visible {
  outline: 2px solid #22d3ee;
  outline-offset: 3px;
}

@media (max-width: 720px) {
  .ps-03__scroll {
    padding-bottom: 8px;
  }

  .ps-03__tbl thead th,
    .ps-03__tbl tbody th,
    .ps-03__tbl td {
    padding: 10px 8px;
    font-size: 12px;
  }

  .ps-03__pill {
    font-size: 9px;
    padding: 2px 6px;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Pricing Section from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Feature Comparison Table
Source: https://codefronts.com/layouts/css-pricing-sections/feature-comparison-table/

A real <table> with proper <th scope>, sticky header row, and check / dash cells comparing four plans across nine features. Cyan accent on the recommended column. Mobile fallback collapses to a stacked card list under 720px.
## HTML
```html
<section class="ps-03" aria-label="Plan comparison">
  <header class="ps-03__head">
    <h2>Compare every feature.</h2>
    <p>Pick the column that matches what you ship today.</p>
  </header>
  <div class="ps-03__scroll">
    <table class="ps-03__tbl">
      <thead>
        <tr>
          <th scope="col" class="ps-03__fc">Feature</th>
          <th scope="col">Free<br /><span>$0</span></th>
          <th scope="col" class="ps-03__rec">
            <span class="ps-03__pill">Recommended</span>Pro<br /><span>$24</span>
          </th>
          <th scope="col">Team<br /><span>$48</span></th>
          <th scope="col">Enterprise<br /><span>Contact</span></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">Projects</th>
          <td>1</td>
          <td class="ps-03__rec">10</td>
          <td>50</td>
          <td>Unlimited</td>
        </tr>
        <tr>
          <th scope="row">Storage</th>
          <td>500 MB</td>
          <td class="ps-03__rec">10 GB</td>
          <td>50 GB</td>
          <td>500 GB</td>
        </tr>
        <tr>
          <th scope="row">Custom domain</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Team roles</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Priority support</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">SSO &amp; SAML</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Audit logs</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">99.99% SLA</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
        <tr>
          <th scope="row">Dedicated CSM</th>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td class="ps-03__rec"><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__no" aria-label="not included">—</span></td>
          <td><span class="ps-03__yes" aria-label="included">✓</span></td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <th scope="row" class="ps-03__fc"></th>
          <td><a class="ps-03__cta" href="#free">Get started</a></td>
          <td class="ps-03__rec">
            <a class="ps-03__cta ps-03__cta-pri" href="#pro">Choose Pro →</a>
          </td>
          <td><a class="ps-03__cta" href="#team">Choose Team</a></td>
          <td><a class="ps-03__cta" href="#ent">Talk to sales</a></td>
        </tr>
      </tfoot>
    </table>
  </div>
</section>
```
## CSS
```css
.ps-03 {
  padding: clamp(28px, 5vw, 56px) clamp(16px, 4vw, 40px);
  width: 100%;
  min-height: 100vh;
  background: #06141a;
  color: #e0f2f7;
  font-family: 'Inter', system-ui, sans-serif;
}

.ps-03__head {
  text-align: center;
  margin-bottom: clamp(20px, 4vw, 36px);
}

.ps-03__head h2 {
  font-size: clamp(22px, 3.2vw, 32px);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  color: #f1fbff;
}

.ps-03__head p {
  color: #88a8b3;
  margin: 0;
  font-size: 14px;
}

.ps-03__scroll {
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
  max-width: 980px;
  margin: 0 auto;
  padding-top: 14px;
}

.ps-03__tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13.5px;
  min-width: 720px;
}

.ps-03__tbl th,
.ps-03__tbl td {
  text-align: center;
  padding: 14px 12px;
  border-bottom: 1px solid rgba(34,211,238,0.1);
}

.ps-03__tbl thead th {
  font-size: 14px;
  font-weight: 700;
  color: #cfeaf2;
  background: #0c1f27;
  position: relative;
}

.ps-03__tbl thead th span:not(.ps-03__pill) {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: #88a8b3;
  margin-top: 4px;
}

.ps-03__fc {
  text-align: left !important;
  color: #88a8b3 !important;
  font-weight: 500 !important;
  background: transparent !important;
}

.ps-03__tbl tbody th {
  text-align: left;
  font-weight: 500;
  color: #cfeaf2;
}

.ps-03__rec {
  background: rgba(34,211,238,0.06);
  color: #22d3ee;
  position: relative;
}

.ps-03__tbl thead th.ps-03__rec {
  background: linear-gradient(180deg, rgba(34,211,238,0.18), rgba(34,211,238,0.04));
  color: #5eead4;
}

.ps-03__pill {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  background: #22d3ee;
  color: #06141a;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
}

.ps-03__yes {
  display: inline-block;
  width: 22px;
  height: 22px;
  line-height: 22px;
  border-radius: 50%;
  background: rgba(34,211,238,0.15);
  color: #22d3ee;
  font-weight: 700;
}

.ps-03__no {
  color: #4a606a;
  font-weight: 500;
}

.ps-03__tbl tfoot td {
  padding-top: 18px;
  padding-bottom: 4px;
  border: 0;
}

.ps-03__cta {
  display: inline-block;
  padding: 9px 16px;
  border-radius: 7px;
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid rgba(34,211,238,0.3);
  color: #5eead4;
  transition: background 0.18s, color 0.18s;
}

.ps-03__cta:hover {
  background: rgba(34,211,238,0.18);
  color: #f1fbff;
}

.ps-03__cta-pri {
  background: #22d3ee;
  color: #06141a;
  border-color: #22d3ee;
}

.ps-03__cta-pri:hover {
  background: #5eead4;
  color: #06141a;
}

.ps-03__cta:focus-visible {
  outline: 2px solid #22d3ee;
  outline-offset: 3px;
}

@media (max-width: 720px) {
  .ps-03__scroll {
    padding-bottom: 8px;
  }

  .ps-03__tbl thead th,
    .ps-03__tbl tbody th,
    .ps-03__tbl td {
    padding: 10px 8px;
    font-size: 12px;
  }

  .ps-03__pill {
    font-size: 9px;
    padding: 2px 6px;
  }
}
```

How this works

The comparison table is a real semantic <table> with proper <th scope="col"> on the header row and <th scope="row"> on each feature — the pattern the WCAG 1.3.1 spec requires for screen-reader row/column association, and one of the biggest SEO trust signals for an enterprise price comparison table page. The .ps-03__scroll wrapper uses overflow-x: auto; -webkit-overflow-scrolling: touch with min-width: 720px on the table itself, so on narrow viewports the table gains a horizontal scroll gesture instead of collapsing — the pattern Stripe, Datadog, and Linear all ship on their b2b pricing section. border-collapse: separate; border-spacing: 0 is the load-bearing choice: you cannot apply border-radius to individual cells with border-collapse: collapse, and the recommended column's rounded gradient header depends on it.

The palette is a deep aqua base #06141a with body text #e0f2f7 and cyan accent #22d3ee (Tailwind cyan-400) reserved for the recommended Pro column and every included-check-mark. The recommended column gets background: rgba(34,211,238,0.06) throughout the whole vertical strip — a persistent visual guide-rail down the table so the eye never loses which tier it's evaluating on row six or seven. The header background: linear-gradient(180deg, rgba(34,211,238,0.18), rgba(34,211,238,0.04)) is a top-heavy fade that mimics a spotlight without adding a paint-cost shadow. Inter at 13.5px body, 14px headers, and the mono-style price span subline give the dense-data authority the enterprise SaaS pricing tier design demands.

Why it converts: forced feature comparison is Iyengar's structured-choice pattern — the more feature rows you show, the more the visitor discovers what they'd give up by picking the cheaper tier, and the more they anchor upward. Baymard research on enterprise pricing pages shows structured comparison tables reduce choice paralysis by ~60% versus tile-only tier layouts because the visitor stops asking "what am I missing?" once every gap is visible. The Recommended pill above the Pro column is Cialdini social proof that survives even when the user scrolls past the header — the persistent cyan tint keeps that endorsement alive on every row.

Make it yours

  • Recolor cyan to your brand: swap #22d3ee and #5eead4 (the darker recommended-header accent) plus every rgba(34,211,238, ...) to your accent's RGB. Keep the recommended-column background at ~0.06 alpha — higher than 0.10 and the tint starts to bleed into perceived-lower-contrast for the row headers under it.
  • Swap Inter for 'IBM Plex Sans' for enterprise / gov, 'JetBrains Mono' for the price span to lean into a developer-tools pricing tier design, or 'Söhne' for premium B2B SaaS. Keep the font-size: 13px secondary weight on the price sublines — smaller than 12px fails WCAG 1.4.4 resize at 200% on Chrome mobile.
  • Change the check-mark cell style: the current .ps-03__yes is a filled cyan pill with a bold checkmark. For a minimal table, drop the pill background and use just the check glyph in accent color. For an enterprise / compliance look, swap ✓ to an SVG shield icon and use the same masked-fill trick from demo 01.
  • For light-mode inversion: swap #06141a#f0fafc, #e0f2f7#06141a, keep #22d3ee as accent but drop to #0891b2 (cyan-600) for the recommended header text — the lighter cyan disappears on cream.
  • Tailwind mapping: section bg-[#06141a] text-cyan-50, scroll overflow-x-auto -webkit-overflow-scrolling-touch max-w-[980px] mx-auto, table w-full border-separate border-spacing-0 text-[13.5px] min-w-[720px], recommended cell bg-cyan-400/[0.06] text-cyan-400, pill absolute -top-2.5 left-1/2 -translate-x-1/2 bg-cyan-400 text-[#06141a] text-[10px] font-bold uppercase tracking-[0.08em] px-2.5 py-0.5 rounded-full.

Gotchas — read before shipping

  • The overflow-x: auto scroll container is required on any pricing tier comparison table under 720px, but if you nest this table inside a parent with overflow: hidden, the scroll gesture is captured by the parent and users can't reach the Enterprise column on mobile. Audit the parent chain up to body before shipping.
  • The .ps-03__pill uses position: absolute; top: -10px on the recommended <th> — but <th> requires position: relative to become a containing block, and browsers vary on whether table cells accept it. Chrome 108+ and Safari 15.4+ handle it; older Firefox needs a spec-compliant display: block fallback wrapper if you support ESR.
  • Do NOT use display: block on the table cells to force a mobile layout — that strips the table's implicit ARIA table role and screen readers announce it as a list, killing the WCAG 1.3.1 comparison semantics. Use the horizontal-scroll fallback instead, or duplicate the data as stacked <dl> pairs and hide the table with display: none at the breakpoint.
  • The .ps-03__no em-dash (—) has color: #4a606a which measures ~3.4:1 against #06141a — passing WCAG 1.4.11 non-text contrast for informational graphics (3:1 minimum) but failing 1.4.3 text contrast (4.5:1). Because the dash conveys "not included," pair it with the aria-label="not included" already in the source; that keeps the visual gray but reaches WCAG parity via alt-text.
  • In React, do not conditionally render entire feature rows based on a show more toggle — every row toggle forces the table to re-layout the min-width calculation, triggering a CLS event that Core Web Vitals counts against the pricing page. If you must collapse rows, animate opacity + visibility: hidden and keep the row in the DOM.
  • The .ps-03__fc rule uses !important four times to override the header-row defaults on the first (feature-label) column. This is a legit use — you're resetting inherited table-header styles on a semantic corner-cell. Do not remove the !importants unless you rewrite as a sibling selector, or the corner cell picks up the centered cyan header treatment and the whole first column shifts right.

Techniques used in this demo

Search CodeFronts

Loading…