Skip to content

Table

Style the native <table> with .re-table. Wrap in .re-table-wrap for horizontal scroll. Opt in to behaviours with data-* attributes.

Name Role Status
Ada Lovelace Engineer Active
Alan Turing Researcher Active
Grace Hopper Admiral Away
Live example
<div class="re-table-wrap">
  <table class="re-table">
    <thead>
      <tr>
        <th>Name</th>
        <th>Role</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Ada Lovelace</td>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Alan Turing</td>
        <td>Researcher</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Grace Hopper</td>
        <td>Admiral</td>
        <td>Away</td>
      </tr>
    </tbody>
  </table>
</div>
Name Role Status
Ada Lovelace Engineer Active
Alan Turing Researcher Active
Grace Hopper Admiral Away
Live example
<div class="re-table-wrap">
  <table class="re-table" data-zebra data-hover data-density="compact">
    <thead>
      <tr>
        <th>Name</th>
        <th>Role</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Ada Lovelace</td>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Alan Turing</td>
        <td>Researcher</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Grace Hopper</td>
        <td>Admiral</td>
        <td>Away</td>
      </tr>
    </tbody>
  </table>
</div>

Add data-sticky-header and give .re-table-wrap a constrained height with overflow: auto — the header stays pinned while the body scrolls.

Name Role Status
Ada Lovelace Engineer Active
Alan Turing Researcher Active
Grace Hopper Admiral Away
Katherine Johnson Mathematician Active
Margaret Hamilton Engineer Active
Dennis Ritchie Engineer Away
Barbara Liskov Researcher Active
Donald Knuth Author Active
Linus Torvalds Maintainer Away
Radia Perlman Engineer Active
Live example
<div
  class="re-table-wrap"
  tabindex="0"
  role="region"
  aria-label="Team members"
  style="max-block-size: 12rem; overflow: auto"
>
  <table class="re-table" data-sticky-header data-zebra>
    <thead>
      <tr>
        <th>Name</th>
        <th>Role</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Ada Lovelace</td>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Alan Turing</td>
        <td>Researcher</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Grace Hopper</td>
        <td>Admiral</td>
        <td>Away</td>
      </tr>
      <tr>
        <td>Katherine Johnson</td>
        <td>Mathematician</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Margaret Hamilton</td>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Dennis Ritchie</td>
        <td>Engineer</td>
        <td>Away</td>
      </tr>
      <tr>
        <td>Barbara Liskov</td>
        <td>Researcher</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Donald Knuth</td>
        <td>Author</td>
        <td>Active</td>
      </tr>
      <tr>
        <td>Linus Torvalds</td>
        <td>Maintainer</td>
        <td>Away</td>
      </tr>
      <tr>
        <td>Radia Perlman</td>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
    </tbody>
  </table>
</div>
  • Keyboard — The native <table> is a static data table, not an interactive grid, so it has no special keyboard model. When the wrapper scrolls (horizontal overflow, or the constrained-height sticky-header case), make .re-table-wrap keyboard-operable by giving it tabindex="0" — it then becomes a focus stop that arrow keys can scroll, so keyboard-only users aren’t trapped out of off-screen columns or rows.
  • Focus — A focusable scroll wrapper shows the shared :focus-visible ring; under forced-colors / Windows High Contrast it falls back to a system-color outline. Non-scrolling tables expose no focusable elements.
  • Semantics — Built entirely on native elements, so the table, row, column-header, and cell roles come for free. Keep <th> in <thead> for column headers (use scope if you also add row headers). No ARIA roles are applied — data-zebra, data-hover, data-density, and data-sticky-header are purely visual and don’t change semantics. When the wrapper is focusable, mark it up as role="region" with an aria-label so assistive tech announces the scrollable area by name.
  • Notes — Zebra striping and hover use token background colors that meet contrast against the foreground text; the sticky header keeps a solid --re-color-surface background so scrolled rows never bleed through behind it. See the accessibility guide for the focus-ring and forced-colors model.