Open demo

CSS code

HTML
<h2 class="zt-title" id="zt-title">Quarterly revenue by region</h2>
<p class="zt-sub">Classic striped table — zebra rows, hover highlight, and a totals footer. Pure CSS.</p>
<div class="zt-card" role="region" tabindex="0" aria-label="Quarterly revenue by region, scrollable">
  <table class="zt" aria-labelledby="zt-title">
    <thead><tr><th scope="col">Region</th><th scope="col">Q1</th><th scope="col">Q2</th><th scope="col">Change</th><th scope="col">Share</th></tr></thead>
    <tbody>
      <tr><th scope="row">North America</th><td>$412,800</td><td>$468,200</td><td class="pos">+13.4%</td><td>38%</td></tr>
      <tr><th scope="row">Europe</th><td>$318,400</td><td>$342,100</td><td class="pos">+7.4%</td><td>28%</td></tr>
      <tr><th scope="row">Asia-Pacific</th><td>$256,900</td><td>$249,300</td><td class="neg">−3.0%</td><td>20%</td></tr>
      <tr><th scope="row">Latin America</th><td>$98,200</td><td>$112,600</td><td class="pos">+14.7%</td><td>9%</td></tr>
      <tr><th scope="row">Middle East & Africa</th><td>$54,100</td><td>$61,800</td><td class="pos">+14.2%</td><td>5%</td></tr>
    </tbody>
    <tfoot><tr><th scope="row">Total</th><td>$1,140,400</td><td>$1,234,000</td><td class="pos">+8.2%</td><td>100%</td></tr></tfoot>
  </table>
</div>
CSS
/* Striped table: zebra rows, a hover highlight and a totals footer.
   The card (border, radius, shadow) sits on the scroll wrapper, so a narrow screen scrolls the table, not the page. */
.zt-title{font-size:1.15rem;font-weight:700;letter-spacing:-.01em;margin:0 0 4px}
.zt-sub{color:#5f6a7c;font-size:.87rem;margin:0 0 20px}
.zt-card{overflow-x:auto;background:#fff;border:1px solid #e7ebf0;border-radius:14px;box-shadow:0 1px 2px rgba(20,28,45,.05)}
.zt-card:focus-visible{outline:2px solid #2563eb;outline-offset:2px}
.zt{width:100%;border-collapse:collapse;font-size:.9rem}
.zt th,.zt td{padding:13px 16px;text-align:left;color:#3c4656}
.zt thead th{background:#212936;color:#e8ecf3;font-size:.74rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.zt tbody tr:nth-child(even){background:#f6f8fb}
.zt tbody tr:hover{background:#eef3fa}
.zt tbody th{font-weight:600;color:#212936}
.zt .pos{color:#13773a;font-weight:600}
.zt .neg{color:#b91c1c;font-weight:600}
.zt tfoot th,.zt tfoot td{background:#f0f3f8;font-weight:700;color:#212936;border-top:2px solid #e0e5ee}
.zt tfoot .pos{color:#13773a}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<h2 class="zt-title" id="zt-title">Quarterly revenue by region</h2>
<p class="zt-sub">Classic striped table — zebra rows, hover highlight, and a totals footer. Pure CSS.</p>
<div class="zt-card table-responsive" role="region" tabindex="0" aria-label="Quarterly revenue by region, scrollable">
  <table class="table table-striped table-hover table-borderless zt align-middle mb-0" aria-labelledby="zt-title">
    <thead class="table-dark"><tr><th scope="col">Region</th><th scope="col">Q1</th><th scope="col">Q2</th><th scope="col">Change</th><th scope="col">Share</th></tr></thead>
    <tbody>
      <tr><th scope="row">North America</th><td>$412,800</td><td>$468,200</td><td class="pos">+13.4%</td><td>38%</td></tr>
      <tr><th scope="row">Europe</th><td>$318,400</td><td>$342,100</td><td class="pos">+7.4%</td><td>28%</td></tr>
      <tr><th scope="row">Asia-Pacific</th><td>$256,900</td><td>$249,300</td><td class="neg">−3.0%</td><td>20%</td></tr>
      <tr><th scope="row">Latin America</th><td>$98,200</td><td>$112,600</td><td class="pos">+14.7%</td><td>9%</td></tr>
      <tr><th scope="row">Middle East & Africa</th><td>$54,100</td><td>$61,800</td><td class="pos">+14.2%</td><td>5%</td></tr>
    </tbody>
    <tfoot><tr><th scope="row">Total</th><td>$1,140,400</td><td>$1,234,000</td><td class="pos">+8.2%</td><td>100%</td></tr></tfoot>
  </table>
</div>
CSS
/* Bootstrap .table-striped + .table-hover, recoloured through the --bs-table-* variables.
   Bootstrap stripes odd rows; the original stripes even ones, so the base is the stripe colour and the "striped" rows are white. */
.zt-title{font-size:1.15rem;font-weight:700;letter-spacing:-.01em;line-height:normal;margin:0 0 4px}
.zt-sub{color:#5f6a7c;font-size:.87rem;margin:0 0 20px}
.zt-card{background:#fff;border:1px solid #e7ebf0;border-radius:14px;box-shadow:0 1px 2px rgba(20,28,45,.05)}
.zt-card:focus-visible{outline:2px solid #2563eb;outline-offset:2px}
.zt{--bs-table-bg:#f6f8fb;--bs-table-color:#3c4656;--bs-table-striped-bg:#fff;--bs-table-striped-color:#3c4656;
  --bs-table-hover-bg:#eef3fa;--bs-table-hover-color:#3c4656;font-size:.9rem}
.zt>:not(caption)>*>*{padding:13px 16px}
.zt .table-dark{--bs-table-bg:#212936;--bs-table-color:#e8ecf3}
.zt thead th{font-size:.74rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.zt tbody th{font-weight:600;--bs-table-color:#212936;--bs-table-striped-color:#212936;--bs-table-hover-color:#212936}
.zt .pos{--bs-table-color:#13773a;--bs-table-striped-color:#13773a;--bs-table-hover-color:#13773a;font-weight:600}
.zt .neg{--bs-table-color:#b91c1c;--bs-table-striped-color:#b91c1c;--bs-table-hover-color:#b91c1c;font-weight:600}
.zt tfoot{--bs-table-bg:#f0f3f8;--bs-table-color:#212936}
.zt tfoot>tr>*{font-weight:700;border-top:2px solid #e0e5ee}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<h2 id="zt-title" class="mb-1 text-[1.15rem] font-bold tracking-[-.01em]">Quarterly revenue by region</h2>
<p class="mb-5 text-[.87rem] text-[#5f6a7c]">Classic striped table — zebra rows, hover highlight, and a totals footer. Pure CSS.</p>
<div role="region" tabindex="0" aria-label="Quarterly revenue by region, scrollable" class="overflow-x-auto rounded-[14px] border border-[#e7ebf0] bg-white shadow-[0_1px_2px_rgba(20,28,45,.05)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">
  <table aria-labelledby="zt-title" class="w-full border-collapse text-[.9rem] text-[#3c4656] [&_td]:px-4 [&_td]:py-[13px] [&_th]:px-4 [&_th]:py-[13px] [&_th]:text-left">
    <thead class="bg-[#212936] text-[.74rem] font-bold tracking-[.06em] text-[#e8ecf3] uppercase"><tr><th scope="col">Region</th><th scope="col">Q1</th><th scope="col">Q2</th><th scope="col">Change</th><th scope="col">Share</th></tr></thead>
    <tbody class="[&_th]:font-semibold [&_th]:text-[#212936] [&>tr]:even:bg-[#f6f8fb] [&>tr]:hover:bg-[#eef3fa]">
      <tr><th scope="row">North America</th><td>$412,800</td><td>$468,200</td><td class="font-semibold text-[#13773a]">+13.4%</td><td>38%</td></tr>
      <tr><th scope="row">Europe</th><td>$318,400</td><td>$342,100</td><td class="font-semibold text-[#13773a]">+7.4%</td><td>28%</td></tr>
      <tr><th scope="row">Asia-Pacific</th><td>$256,900</td><td>$249,300</td><td class="font-semibold text-[#b91c1c]">−3.0%</td><td>20%</td></tr>
      <tr><th scope="row">Latin America</th><td>$98,200</td><td>$112,600</td><td class="font-semibold text-[#13773a]">+14.7%</td><td>9%</td></tr>
      <tr><th scope="row">Middle East & Africa</th><td>$54,100</td><td>$61,800</td><td class="font-semibold text-[#13773a]">+14.2%</td><td>5%</td></tr>
    </tbody>
    <tfoot class="bg-[#f0f3f8] font-bold text-[#212936] [&_td]:border-t-2 [&_td]:border-[#e0e5ee] [&_th]:border-t-2 [&_th]:border-[#e0e5ee]"><tr><th scope="row">Total</th><td>$1,140,400</td><td>$1,234,000</td><td class="text-[#13773a]">+8.2%</td><td>100%</td></tr></tfoot>
  </table>
</div>

About this set

A classic report table: a dark header row, zebra-striped body rows, a hover highlight and a totals row in the footer. The example shows quarterly revenue by region with two quarter columns, a change column coloured green or red and a share column. Region names are row headers (th scope=”row”), and the total sits in a real tfoot so it stays attached to the table semantically and prints at the bottom. Use it for financial summaries, reports and any table that ends in a total. The card look, a white box with a thin border, rounded corners and a soft shadow, is drawn on a wrapper around the table instead of the table itself. That wrapper is also the scroll container, so on a narrow phone the table scrolls sideways inside the card while the page stays still, and it is a labelled, focusable region so keyboard users can scroll it with the arrow keys. There is no JavaScript. In the Bootstrap version the stripes and hover come from .table-striped and .table-hover recoloured through the –bs-table-* variables, and the Tailwind version uses even: and hover: utilities.

What’s included

  • Dark header row, zebra stripes and a hover highlight
  • Totals row in a real tfoot
  • Positive and negative changes coloured green and red at 4.5:1 or better
  • Row headers with scope="row" for each region
  • Card wrapper doubles as a keyboard-scrollable region on small screens

Accessibility

The table is named by its heading through aria-labelledby, column headers use scope="col" and region names are row headers. The scroll wrapper is a region with an aria-label and tabindex="0", and it shows a 2px outline when focused. Text colours, including the green and red changes, meet 4.5:1.

Customise it

Edit the .zt rules: header #212936, stripe #f6f8fb, hover #eef3fa, totals #f0f3f8. In Bootstrap set --bs-table-bg (stripe colour), --bs-table-striped-bg and --bs-table-hover-bg on .zt; in Tailwind change the even:, hover: and bg- utilities.