13 CSS Tables

10 Pure CSS3 Light JSPublished

13 CSS tables for SaaS pricing comparisons, admin dashboards, shopping carts, crypto trackers, and class timetables — spanning responsive collapse-to-card layouts, sticky headers and pinned first columns, :has()-driven row focus states, zebra glassmorphism and neumorphic surfaces, an FDA Nutrition Facts panel, and a frosted Liquid Glass header. Scoped under .ct-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related21 CSS Pricing Tables21 CSS Pricing Sections30 CSS Grid Layouts13 Tailwind CSS Tables

Premium template pickPartner: Cruip

Affiliate link

Building the whole admin dashboard, not just this layout?

You just browsed 13 table styles. Mosaic by Cruip is a complete Tailwind CSS admin dashboard template, coded in HTML, React, Next.js, Vue and Laravel, with a Figma version too. It costs $69.

View Mosaic dashboard

Mosaic by Cruip. We may earn a commission, at no extra cost to you.

Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

11. Nutrition Facts Label (opens in a new tab)

Published

Pure CSS
A pixel-perfect FDA-replica Nutrition Facts panel built with a nested HTML table, precise border-width hierarchy (thick, medium, thin), and a scanning accent line animation — no images required.

13. Data Table with Glass Header (opens in a new tab)

Published

Pure CSS
A data table whose sticky header is a frosted Liquid Glass panel: scroll the rows and they warp and blur as they pass beneath the header's lens, while the column titles stay pin-sharp. The pattern developers actually search for — glass that keeps dense tabular data readable.
FAQ

Frequently asked questions

How do I make an HTML table responsive without JavaScript?
The canonical pattern uses CSS media queries plus a data-label attribute on each cell. In the markup, you add data-label='Email' to every td that contains an email value (and the same for every other column). Above mobile width, the standard table renders normally with thead headers. Below mobile (@media (max-width: 720px)), you flip table, thead, tbody, tr, td to display: block, visually hide the thead with clip-path: inset(50%), set each td to relative positioning, and pseudo-element each cell with td::before { content: attr(data-label); font-weight: 600; }. The browser pulls the label from the data-label attribute and renders it inline above the value, producing a card-stacked layout on phones. CSS-Tricks coined this in 2016 and it's still the canonical pattern in 2026 — it works in every browser, requires zero JS, and degrades gracefully if the user disables CSS. Demo 01 ships the full recipe.
How do I freeze the first column of a wide data table?
Use position: sticky; left: 0 on the first-column cells. The full recipe: wrap the table in a horizontally-scrollable container (overflow-x: auto), then apply position: sticky; left: 0; background: var(--surface) to both the th:first-child and td:first-child selectors. The background color is critical — without it, the scrolling cells visually 'bleed through' the sticky column as they pass under it. Add z-index: 2 on the sticky header cell (thead th:first-child) so it stays above the regular row cells when both are sticking. Browser support is universal as of 2026 (Chrome 56+, Safari 13+, Firefox 32+). Common gotcha: any ancestor with overflow: hidden breaks sticky positioning — the sticky element needs at least one scrollable ancestor with overflow: auto or overflow: scroll. Demo 07 (Fixed First Column Data Sheet) ships the balance-sheet financial pattern.
How do I add zebra-striped rows in pure CSS?
One line: tbody tr:nth-child(even) { background: rgba(0,0,0,0.04); }. Always use nth-child(even) on rows inside the tbody — not nth-of-type, because nth-of-type targets all sibling tr elements regardless of which tbody they belong to, which breaks if you have multiple tbody sections or a tfoot. For maximum readability, also add tbody tr:hover { background: rgba(124,108,255,0.08); } for a hover preview so users can keep their place across wide rows. For a more atmospheric look, layer zebra striping on top of a glassmorphism surface — alternating rows at 18%/8% white opacity create the depth illusion (Demo 06).
How do I make tables accessible to screen readers?
Six rules. (1) Use semantic markup — proper thead, tbody, and tfoot elements; not divs styled to look like a table. Screen readers announce row/column position only when the markup is real. (2) Every header cell needs scope='col' (column headers) or scope='row' (row headers) so the screen reader knows the relationship. (3) Add a caption element as the FIRST child of the table — it's announced before the table starts and gives blind users context ("Q4 2025 Revenue by Region"). (4) For complex tables with multiple header rows, use headers='id1 id2' on data cells that depend on multiple headers. (5) Avoid using tables for layout — only for tabular data. CSS Grid is the right tool for layout, even when the layout looks grid-like. (6) Provide a brief summary either in caption text or via aria-describedby pointing at a paragraph before the table. WCAG 1.3.1 (Info & Relationships) and 1.3.2 (Meaningful Sequence) both apply. Every demo in this collection ships semantic markup with scope attributes where appropriate.
How do I make a pricing comparison table that scales to 4-6 plans?
Two solid approaches. (1) **CSS Grid layout**: instead of using a real table, use display: grid with grid-template-columns: minmax(180px, 1fr) repeat(auto-fit, minmax(140px, 1fr)). The first column is the feature label (sticky on horizontal scroll if needed), and the auto-fit columns gracefully add/remove based on container width. (2) **Real table with sticky first column**: a semantic table with position: sticky; left: 0 on the first column (see freeze-column FAQ above) for the feature labels. The Stripe / Linear / Vercel pricing pages all use one of these two patterns. Both let you add a 6th plan column without restructuring. Demo 02 (SaaS Pricing Comparison Matrix) ships the table pattern with a highlighted 'Pro' tier via tr.popular { background: var(--accent-soft) } to draw the eye to the recommended plan.
Can I sort an HTML table without a library?
Yes — about 30 lines of vanilla JavaScript. The pattern: (1) Add a click event listener to every th element. (2) On click, read a data-sort-type attribute from the header (values: 'string', 'number', 'date'). (3) Convert the tbody rows to an array via Array.from(tbody.querySelectorAll('tr')). (4) Sort the array by extracting the matching cell's text and comparing per the data-sort-type. (5) Re-append the sorted rows to the tbody (the DOM accepts this without rebuilding). (6) Toggle a data-order='asc' or 'desc' attribute on the th to flip direction on subsequent clicks, and use a CSS rule like th[data-order='asc']::after { content: ' ↑' } to show the sort indicator. That's it. TanStack Table is 50KB+ gzipped; DataTables is 87KB. For a single sortable table, 30 lines of vanilla code outperforms both on first-paint and works without React/jQuery. Demo 03 (Admin Dashboard Data Grid) ships the recipe.
How do I make a table look good on mobile without horizontal scrolling?
Three approaches, pick by use case. (a) **Card-collapse pattern** (Demo 01) — flip every row to display:block on narrow viewports and use data-label cell prefixes to mimic header text inline. Best when each row is a 'record' the user reads individually (contacts, orders, transactions). (b) **Horizontal scroll with scroll-snap** — wrap the table in overflow-x: auto; scroll-snap-type: x mandatory and add scroll-snap-align: start on key columns. Users swipe horizontally, columns snap into focus. Best for true data grids where comparing across columns matters more than reading per-row (analytics, dashboards). (c) **Display: grid reflow** — change table to a CSS grid container at narrow widths so columns auto-stack vertically. Works for short tables with few columns but breaks for tables with 6+ columns. Demos 01 (card-collapse) and 03 (admin grid with scroll) cover (a) and (b).
How is this different from TanStack Table, DataTables, AG Grid, or Tabulator?
Those four are interactive **data-grid libraries** designed for heavy data manipulation: virtualized rows for 10,000+ records, multi-column sorting, server-side pagination, column resizing, drag-to-reorder, inline editing, Excel-style cell selection. They're 50KB-300KB+ gzipped and require integration code. This collection is something different: **styling for semantic HTML tables**. The 12 designs here are 0KB dependencies, MIT-licensed, copy-paste-ready CSS for visualizing tabular data — responsive layouts, themed surfaces (cyberpunk, glassmorphism, neumorphic, minimalist), domain-specific patterns (nutrition labels, pricing matrices, fitness timetables, shopping carts). If you're rendering 50 rows of static data and want it to look good, this collection is the answer. If you're building Airtable, use AG Grid. The two are complementary, not competing.

Related collections

30 CSS Badges preview

30 CSS Badges

30 hand-coded CSS badges for status indicators, notifications, membership tiers, live-data displays, and SEO / DevOps / financial dashboards — upload progress, typing indicator, transit line status, Core Web Vitals, ECG heartbeat, CI/CD build pipeline, countdown ring, live price ticker, keycap shortcut, wax seal, conference lanyard, and holographic collectibles. Copy-paste HTML and CSS, WCAG 2.2 accessible, MIT licensed.

20 CSS Banners & Alerts preview

20 CSS Banners & Alerts

20 hand-coded CSS banner and alert bars for e-commerce storefronts, SaaS dashboards, marketing sites, and compliance-bound products. Covers GDPR cookie consent with a real preferences panel, promo bars with countdown timers, free-shipping threshold progress, app-install smart banners, maintenance and incident status bars, newsletter signup bars, age verification gates, limited-stock urgency banners, live event announcements, browser upgrade notices, geo and currency switchers, sticky top announcement bars, bottom cookie bars, inline form validation alerts, icon-aligned alert banners, left-border accent alerts, diagonal stripe promos, full-width hero banners, animated gradient border alerts, and a text-wrapping laboratory that shows five strategies for the unbreakable string that breaks more banners than anything else. Every bar is scoped under a .ba-NN prefix for no-collision pasting, guards prefers-reduced-motion, carries the correct aria-live and role semantics, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

25 CSS Blockquotes preview

25 CSS Blockquotes

25 CSS blockquotes for article pull quotes, testimonial cards, docs callout admonitions, statement banners, and social-post embeds. Covers giant decorative quotation marks, minimalist hairline and classic left borders, animated gradient and glowing neon borders, glassmorphism and aurora backgrounds, brutalist cards, speech bubbles, thick accent rails, highlighted mark text, newspaper multi-column layouts, an expandable read-more disclosure, a scroll-snap testimonial slider, and one-click copy.

Search CodeFronts

Loading…