Source Code

<div class="fca-wrap">
  <div class="fca-head-row">
    <span class="fca-corner">Compare plans</span>
    <div class="fca-plan-heads">
      <span>Basic</span><span>Pro</span><span class="fca-highlight">Team</span>
    </div>
  </div>

  <div class="fca-table" id="fcaTable">
    <div class="fca-category" data-open="true">
      <button type="button" class="fca-cat-head">
        <span class="fca-cat-title">
          <svg class="fca-chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
          Core features
        </span>
        <span class="fca-cat-count">5/5 in Team</span>
      </button>
      <div class="fca-cat-body">
        <div class="fca-row"><span>Projects</span><b>3</b><b>Unlimited</b><b>Unlimited</b></div>
        <div class="fca-row"><span>Storage</span><b>2 GB</b><b>50 GB</b><b>500 GB</b></div>
        <div class="fca-row"><span>File versioning</span><b data-no></b><b data-yes></b><b data-yes></b></div>
        <div class="fca-row"><span>Custom templates</span><b data-no></b><b data-yes></b><b data-yes></b></div>
        <div class="fca-row"><span>Offline mode</span><b data-yes></b><b data-yes></b><b data-yes></b></div>
      </div>
    </div>

    <div class="fca-category" data-open="false">
      <button type="button" class="fca-cat-head">
        <span class="fca-cat-title">
          <svg class="fca-chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
          Collaboration
        </span>
        <span class="fca-cat-count">3/3 in Team</span>
      </button>
      <div class="fca-cat-body">
        <div class="fca-row"><span>Team members</span><b>1</b><b>5</b><b>Unlimited</b></div>
        <div class="fca-row"><span>Shared workspaces</span><b data-no></b><b data-yes></b><b data-yes></b></div>
        <div class="fca-row"><span>Live commenting</span><b data-no></b><b data-yes></b><b data-yes></b></div>
      </div>
    </div>

    <div class="fca-category" data-open="false">
      <button type="button" class="fca-cat-head">
        <span class="fca-cat-title">
          <svg class="fca-chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
          Security &amp; admin
        </span>
        <span class="fca-cat-count">1/4 in Team</span>
      </button>
      <div class="fca-cat-body">
        <div class="fca-row"><span>SSO / SAML</span><b data-no></b><b data-no></b><b data-yes></b></div>
        <div class="fca-row"><span>Audit logs</span><b data-no></b><b data-no></b><b data-yes></b></div>
        <div class="fca-row"><span>Role-based permissions</span><b data-no></b><b data-no></b><b data-yes></b></div>
        <div class="fca-row"><span>Two-factor auth</span><b data-yes></b><b data-yes></b><b data-yes></b></div>
      </div>
    </div>
  </div>
</div>

Pricing Table with Collapsible Feature Categories — Free HTML CSS JS Snippet

Pricing Table with Collapsible Feature Categories · Pricing · Plain HTML, CSS & JS · Live preview

What's included

Features

Feature rows grouped into named, independently collapsible categories
Each category toggles its own state — multiple categories can stay open at once
Per-category count badge previews plan coverage without opening the section
max-height CSS transition matches the standard accordion pattern, no JS animation
Chevron rotation and open state driven by the same data-open attribute, always in sync
CSS-masked yes/no checkmarks alongside plain text values in the same row format
aria-expanded set correctly on load and kept in sync on every toggle
Responsive — column and padding sizes shrink gracefully on narrow viewports
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons

About this UI Snippet

Pricing Comparison Table Grouped by Feature Category — Collapsible Sections, Not a Flat List

Screenshot of the Pricing Table with Collapsible Feature Categories snippet rendered live

A pricing table with thirty rows in one flat list is exhausting to scan — a buyer who only cares about security features has to wade past storage limits and template settings to find them. This snippet groups every row into named categories (Core features, Collaboration, Security & admin), each collapsed to a single clickable header by default except the first, so the table reads as a short, scannable summary until a visitor opens the category they actually care about.

Independent open/closed state per category, not an accordion

Each .fca-category tracks its own data-open attribute, and clicking its header only ever toggles that one category — there's no "close all others" logic like a single-open accordion. A buyer evaluating both Collaboration and Security limits can have both sections open simultaneously to compare them side by side, which a strict single-open accordion would prevent.

A count badge that previews what's inside without opening it

Each category header shows a badge like "1/4 in Team" — how many of that category's features the highlighted plan actually includes — right in the collapsed header. This means a visitor doesn't have to open Security & admin just to get a rough sense of how security-featured the Team plan is; the badge gives that away before a single click, and the full row-by-row breakdown is only a click away for anyone who wants it.

max-height accordion animation, one detail at a time

Collapsing and expanding a .fca-cat-body uses the same max-height: 0 to fixed-value CSS transition used by a standard accordion FAQ — set high enough (600px) to never clip the tallest category's content, with overflow: hidden doing the actual clipping while collapsed. The chevron icon rotates 180 degrees in sync via the same data-open attribute selector, so the open/closed visual state, the row content, and the chevron direction can never disagree with each other.

Feature cells stay a consistent yes/no/value pattern

Inside each category, rows use either a plain text value (seat counts, storage limits) or the same data-yes/data-no CSS-masked checkmark pattern as a plain feature table — grouping by category changes how rows are organized, not how any individual row is rendered, so it's a drop-in structural upgrade over a flat table rather than a different visual language.

Accessible from the start

Each category header is a real <button> with aria-expanded set on load to match its initial data-open value and kept in sync on every toggle, so a screen reader announces the correct collapsed/expanded state immediately rather than only after the first interaction.

Customizing it

Add a new category by copying a .fca-category block with its own header text, count badge, and .fca-row children — the toggle logic queries all .fca-category elements generically, so no JavaScript changes are needed. Change which category starts open by editing its initial data-open attribute in the HTML.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Instead of guessing why some categories can stay open together while others might seem to close each other in a typical accordion, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how each .fca-category's independent data-open attribute avoids the single-open-at-a-time behavior of a standard FAQ accordion, and why that matters specifically for a comparison table where a buyer might want two categories open side by side. The same assistant can help you extend it — ask it to add a "expand all / collapse all" control above the table, compute each category's count badge dynamically from the actual data-yes/data-no attributes in its rows instead of a hardcoded string, or persist which categories are open in sessionStorage so returning to the page keeps a visitor's previous state. It's also useful for a UX review: ask whether defaulting only the first category open is the right call for your specific feature set, or whether a search box that auto-expands and scrolls to the category containing a matched feature name would serve long tables better. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a pricing comparison table in plain HTML, CSS, and vanilla JavaScript where feature rows are grouped into independently collapsible categories — no accordion library.

Requirements:
- A table-like layout with plan name headers (e.g. Basic, Pro, Team) across the top, and below it several named category sections (e.g. Core features, Collaboration, Security & admin), each collapsed behind a clickable header except one category that starts expanded.
- Each category header must show a small badge summarizing how many of that category's features the top-tier plan includes (e.g. "1/4 in Team"), visible even while the category is collapsed.
- Clicking a category header toggles only that category's own open/closed state — opening or closing one category must never affect any other category's state, so multiple categories can be expanded at the same time (this must NOT behave like a single-open-at-a-time FAQ accordion).
- Each category's body contains several feature rows, and each row must support two cell types across its plan columns: a plain text value (e.g. "50 GB", "5 seats") or a supported/unsupported indicator rendered via a data attribute (not a raw checkmark character) styled as a colored checkmark or a dash.
- Implement the expand/collapse animation using the max-height CSS transition technique (max-height: 0 with overflow hidden collapsed, a fixed large max-height expanded) rather than height: auto, and rotate a chevron icon 180 degrees in sync with the open state using the same attribute the animation reads from.
- Each category header must be a real <button> element with an aria-expanded attribute that is correctly set on page load to match its initial open/closed state and updated on every toggle.
- Keep the table usable on narrow mobile viewports.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Click a category headerCore features starts open by default — click any header to expand or collapse that category.
  2. 2
    Open more than one categoryCategories toggle independently, so you can compare Collaboration and Security open at the same time.
  3. 3
    Read the count badgeEach header shows how many of that category's features the highlighted plan includes, even while collapsed.
  4. 4
    Compare plan columnsEach row shows the value or a check/dash for Basic, Pro, and Team side by side.
  5. 5
    Add a new categoryCopy a .fca-category block with its own header and .fca-row children — no JS changes needed.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Feature-dense B2B pricing pages
Keep a long comparison table scannable by letting visitors open only the categories they care about, instead of scrolling a flat feature table.
Enterprise sales and procurement pages
Let a security or compliance reviewer jump straight to the Security & admin category without reading through unrelated rows.
Learn independent multi-open accordion state
Study how each category tracks its own data-open attribute instead of a single shared open index.
Platform pricing with multiple product areas
Group rows by product module (Core, Analytics, Integrations) so each area reads like its own mini comparison.
Self-serve pricing FAQs
Pair with a pricing FAQ below the table for questions that go beyond feature availability.
Related: Feature Table with Explainer Tooltips
Combine with the Feature Table with Explainer Tooltips pattern inside any category that needs jargon explained.

Got questions?

Frequently Asked Questions

No. Each .fca-category has its own independent data-open attribute, and clicking a header only toggles that category. Any number of categories can be open at the same time, which matters here because a buyer often wants to compare two different categories (like Collaboration and Security) side by side rather than one at a time.

It shows how many of that category's features the highlighted plan (Team, in this example) actually includes out of the category's total row count — e.g. "1/4 in Team" for Security & admin. It gives a rough sense of coverage before a visitor even opens the section.

The same technique as a standard accordion: .fca-cat-body starts at max-height: 0 with overflow: hidden, and gains a fixed max-height (600px, larger than any category is likely to need) when its parent's data-open attribute is "true", animated via a CSS transition on max-height. The chevron icon rotates 180 degrees using the same data-open attribute selector so it never falls out of sync with the actual open state.

Copy an entire .fca-category block — its .fca-cat-head button with a title, chevron icon, and count badge, plus a .fca-cat-body containing .fca-row children — and paste it into #fcaTable. No JavaScript changes are needed since the toggle logic queries all .fca-category elements generically on page load.

Each category header is a real <button> element, and its aria-expanded attribute is set to match the category's initial data-open value when the page loads, then kept updated on every click. This means a screen reader announces the correct expanded or collapsed state immediately, not just after the first time a category is toggled.

Yes — that is the default setup. Rows like "Storage" show a plain text value per plan (e.g. "2 GB", "50 GB"), while rows like "File versioning" use the data-yes/data-no attributes to render a CSS-masked checkmark or a dash. Both row styles can appear in any category without any special handling.