Table

Two elements, both load-bearing: a scroll container and the table. A table without the container overflows its parent on a narrow screen and drags the page layout with it, which is the most common way a data screen breaks on a phone.

Reference

sk-table-wrap, sk-table

border-collapse is separate, not collapse. Collapsed borders are not painted by the cell, so a sticky header loses its bottom rule the moment it detaches and appears to float over nothing. Separate borders plus a border on each cell survives sticking.

<div class="sk-table-wrap">
  <table class="sk-table">
    <caption>What this table is of</caption>
    <thead>
      <tr><th scope="col">Name</th><th scope="col" data-numeric="true">Amount</th></tr>
    </thead>
    <tbody>
      <tr><td>Row</td><td data-numeric="true">1,240</td></tr>
    </tbody>
  </table>
</div>
AttributeTypeDefaultNotes
data-density"compact"comfortableTightens cell padding and drops a type step. A table-level attribute, not a system-wide axis - the values live in the token layer so promoting it later is a rename.
data-sticky"true"-Pins the header. Needs a bounded height on the wrapper, or the header sticks to the page rather than to the table.
data-zebra"true"-Alternating row tint.
data-hoverable"true"-Row hover. Only worth it if rows are clickable.

cells

Alignment and figures. data-numeric implies end alignment as well as tabular figures, because a column of numbers that is not end-aligned cannot be compared by eye no matter what the digits do.

<td data-numeric="true">1,240.00</td>
<td data-align="center">Centre</td>
<td data-col="actions">…</td>
AttributeTypeDefaultNotes
data-align"center" | "end"start
data-numeric"true"-End-aligns and switches on tabular figures.
data-nowrap"true"-
data-col"select" | "actions"-Shrinks the column to its content so the columns carrying meaning get the width.
data-selected"true"-On the <tr>. Drive it from the same value as the row's checkbox.

sk-table-sort

A link, not a button with script. Sorting is a different view of the same resource, so it is a URL - linkable, bookmarkable, back-button-able, and it works with scripting off. Add hx-get and the same markup becomes a partial swap.

<th scope="col" aria-sort="ascending">
  <a class="sk-table-sort" href="?sort=name&amp;dir=desc">
    Name <svg class="sk-icon">…</svg>
  </a>
</th>
AttributeTypeDefaultNotes
aria-sort"ascending" | "descending" | "none""none"On the th, not the link. Exactly one column should be ascending or descending; none still tells assistive technology the column is sortable.

sk-selection-bar

"3 selected" plus what can be done about it. It REPLACES the toolbar rather than joining it: the filters that produced this set are not what the reader wants while three rows are selected, and a bar growing out of a full toolbar wraps to two rows on the first narrow screen. Rendering one or the other is a server-side decision, the same shape every other state in this system takes. role="status" sits on the COUNT, not on the bar - the announcement is the number, and a live region wrapped around the buttons re-announces every one of them each time it changes.

<div class="sk-selection-bar">
  <p class="sk-selection-count" role="status">3 selected</p>
  <div class="sk-selection-actions">…</div>
</div>
AttributeTypeDefaultNotes
SelectCellbinding-Names the checkbox after its row - "Select Ada Lovelace" - with the text visually hidden. A column of checkboxes all announced as "Select" tells the reader nothing about which row they are on, and the row's own text is not connected to the control in any way a screen reader can follow.
SelectAllCellbinding-Defaults to "Select all on this page", deliberately not "Select all": with pagination those are different things, and a control claiming the second while doing the first is how bulk actions go wrong.
indeterminate--Not supported, on purpose. The dash meaning "some rows are selected" is a DOM property rather than an attribute, so it cannot be rendered from a server at all - only set by script. The count in the bar says it in words instead.

Examples

Anatomy

Caption, header, numeric column, status badge, row actions.

Accounts and their current balance
Name Status Balance Actions
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Show markup
<div class="sk-table-wrap">
  <table class="sk-table">
    <caption>
      Accounts and their current balance
    </caption>
    <thead>
      <tr>
        <th scope="col">
          Name
        </th>
        <th scope="col">
          Status
        </th>
        <th scope="col" data-numeric="true">
          Balance
        </th>
        <th scope="col" data-col="actions">
          <span class="sk-visually-hidden">
            Actions
          </span>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
        <td data-col="actions">
          <div class="sk-button-group" role="group" aria-label="Row actions">
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              Edit
            </button>
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              <svg class="sk-icon" viewBox="0 0 24 24" role="img">
                <title>
                  Delete Ada Lovelace
                </title>
                <path d="M4 7h16">
                </path>
                <path d="M10 11v6">
                </path>
                <path d="M14 11v6">
                </path>
                <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
                </path>
                <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
                </path>
              </svg>
            </button>
          </div>
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
        <td data-col="actions">
          <div class="sk-button-group" role="group" aria-label="Row actions">
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              Edit
            </button>
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              <svg class="sk-icon" viewBox="0 0 24 24" role="img">
                <title>
                  Delete Grace Hopper
                </title>
                <path d="M4 7h16">
                </path>
                <path d="M10 11v6">
                </path>
                <path d="M14 11v6">
                </path>
                <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
                </path>
                <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
                </path>
              </svg>
            </button>
          </div>
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
        <td data-col="actions">
          <div class="sk-button-group" role="group" aria-label="Row actions">
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              Edit
            </button>
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              <svg class="sk-icon" viewBox="0 0 24 24" role="img">
                <title>
                  Delete Alan Turing
                </title>
                <path d="M4 7h16">
                </path>
                <path d="M10 11v6">
                </path>
                <path d="M14 11v6">
                </path>
                <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
                </path>
                <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
                </path>
              </svg>
            </button>
          </div>
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
        <td data-col="actions">
          <div class="sk-button-group" role="group" aria-label="Row actions">
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              Edit
            </button>
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              <svg class="sk-icon" viewBox="0 0 24 24" role="img">
                <title>
                  Delete Katherine Johnson
                </title>
                <path d="M4 7h16">
                </path>
                <path d="M10 11v6">
                </path>
                <path d="M14 11v6">
                </path>
                <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
                </path>
                <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
                </path>
              </svg>
            </button>
          </div>
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
        <td data-col="actions">
          <div class="sk-button-group" role="group" aria-label="Row actions">
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              Edit
            </button>
            <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
              <svg class="sk-icon" viewBox="0 0 24 24" role="img">
                <title>
                  Delete Edsger Dijkstra
                </title>
                <path d="M4 7h16">
                </path>
                <path d="M10 11v6">
                </path>
                <path d="M14 11v6">
                </path>
                <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
                </path>
                <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
                </path>
              </svg>
            </button>
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Sortable

The middle column is sorted. Direction indicators only appear on the sorted column and on hover, so a wide table is not a row of arrows competing with the labels.

Name Status Balance
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Show markup
<div class="sk-table-wrap">
  <table class="sk-table" data-hoverable="true">
    <thead>
      <tr>
        <th scope="col" aria-sort="none">
          <a class="sk-table-sort" href="?sort=name">
            Name
            <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
              <path d="m6 9 6 6 6-6">
              </path>
            </svg>
          </a>
        </th>
        <th scope="col" aria-sort="ascending">
          <a class="sk-table-sort" href="?sort=status&amp;dir=desc">
            Status
            <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
              <path d="m18 15-6-6-6 6">
              </path>
            </svg>
          </a>
        </th>
        <th scope="col" aria-sort="none" data-numeric="true">
          <a class="sk-table-sort" href="?sort=amount">
            Balance
            <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
              <path d="m6 9 6 6 6-6">
              </path>
            </svg>
          </a>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
    </tbody>
  </table>
</div>

Compact

What a table is actually for: a lot of rows on one screen.

Name Status Balance
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Show markup
<div class="sk-table-wrap">
  <table class="sk-table" data-density="compact" data-hoverable="true">
    <thead>
      <tr>
        <th scope="col">
          Name
        </th>
        <th scope="col">
          Status
        </th>
        <th scope="col" data-numeric="true">
          Balance
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
    </tbody>
  </table>
</div>

Zebra and hover

Name Status Balance
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Show markup
<div class="sk-table-wrap">
  <table class="sk-table" data-zebra="true" data-hoverable="true">
    <thead>
      <tr>
        <th scope="col">
          Name
        </th>
        <th scope="col">
          Status
        </th>
        <th scope="col" data-numeric="true">
          Balance
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
    </tbody>
  </table>
</div>

Selection

The row attribute and the checkbox are driven by the same value, so the tint cannot disagree with the control. Every checkbox is named after its own row - tab through them and listen, or read the markup.

2 selected

Name Balance
Ada Lovelace 1,240.00
Grace Hopper 980.50
Alan Turing 111.11
Katherine Johnson 17.00
Edsger Dijkstra 2,006.75
Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-selection-bar">
    <p class="sk-selection-count" role="status">
      2 selected
    </p>
    <div class="sk-selection-actions">
      <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
        Export
      </button>
      <button class="sk-button" type="button" data-variant="danger" data-size="sm">
        <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
          <path d="M4 7h16">
          </path>
          <path d="M10 11v6">
          </path>
          <path d="M14 11v6">
          </path>
          <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
          </path>
          <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
          </path>
        </svg>
        Delete
      </button>
    </div>
  </div>
  <div class="sk-table-wrap">
    <table class="sk-table">
      <thead>
        <tr>
          <th scope="col" data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="select-all">
              <span class="sk-visually-hidden">
                Select all on this page
              </span>
            </label>
          </th>
          <th scope="col">
            Name
          </th>
          <th scope="col" data-numeric="true">
            Balance
          </th>
        </tr>
      </thead>
      <tbody>
        <tr data-selected="true">
          <td data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="selected" value="Ada Lovelace" checked>
              <span class="sk-visually-hidden">
                Select Ada Lovelace
              </span>
            </label>
          </td>
          <td>
            Ada Lovelace
          </td>
          <td data-numeric="true">
            1,240.00
          </td>
        </tr>
        <tr data-selected="true">
          <td data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="selected" value="Grace Hopper" checked>
              <span class="sk-visually-hidden">
                Select Grace Hopper
              </span>
            </label>
          </td>
          <td>
            Grace Hopper
          </td>
          <td data-numeric="true">
            980.50
          </td>
        </tr>
        <tr>
          <td data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="selected" value="Alan Turing">
              <span class="sk-visually-hidden">
                Select Alan Turing
              </span>
            </label>
          </td>
          <td>
            Alan Turing
          </td>
          <td data-numeric="true">
            111.11
          </td>
        </tr>
        <tr>
          <td data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="selected" value="Katherine Johnson">
              <span class="sk-visually-hidden">
                Select Katherine Johnson
              </span>
            </label>
          </td>
          <td>
            Katherine Johnson
          </td>
          <td data-numeric="true">
            17.00
          </td>
        </tr>
        <tr>
          <td data-col="select">
            <label class="sk-check">
              <input type="checkbox" name="selected" value="Edsger Dijkstra">
              <span class="sk-visually-hidden">
                Select Edsger Dijkstra
              </span>
            </label>
          </td>
          <td>
            Edsger Dijkstra
          </td>
          <td data-numeric="true">
            2,006.75
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Sticky header

Scroll inside the frame. Needs a bounded height on the wrapper.

Name Status Balance
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Ada Lovelace ada@example.comActive1,240.00
Grace Hopper grace@example.comActive980.50
Alan Turing alan@example.comPending111.11
Katherine Johnson katherine@example.comFailed17.00
Edsger Dijkstra edsger@example.comArchived2,006.75
Show markup
<div class="sk-table-wrap" style="max-block-size:16rem">
  <table class="sk-table" data-density="compact" data-sticky="true" data-hoverable="true">
    <thead>
      <tr>
        <th scope="col">
          Name
        </th>
        <th scope="col">
          Status
        </th>
        <th scope="col" data-numeric="true">
          Balance
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Ada Lovelace
            </span>
            <span class="sk-table-cell-muted">
              ada@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          1,240.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Grace Hopper
            </span>
            <span class="sk-table-cell-muted">
              grace@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="success">
            Active
          </span>
        </td>
        <td data-numeric="true">
          980.50
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Alan Turing
            </span>
            <span class="sk-table-cell-muted">
              alan@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="warning">
            Pending
          </span>
        </td>
        <td data-numeric="true">
          111.11
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Katherine Johnson
            </span>
            <span class="sk-table-cell-muted">
              katherine@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="danger">
            Failed
          </span>
        </td>
        <td data-numeric="true">
          17.00
        </td>
      </tr>
      <tr>
        <td>
          <span class="sk-table-cell-stack">
            <span>
              Edsger Dijkstra
            </span>
            <span class="sk-table-cell-muted">
              edsger@example.com
            </span>
          </span>
        </td>
        <td>
          <span class="sk-badge" data-status="neutral">
            Archived
          </span>
        </td>
        <td data-numeric="true">
          2,006.75
        </td>
      </tr>
    </tbody>
  </table>
</div>

Empty

The empty state goes inside tbody, spanning every column, so the header and surrounding chrome stay put. Swapping the whole table away makes the page jump.

Name Status Balance

No accounts match those filters

Try widening the date range, or clear the status filter.
Show markup
<div class="sk-table-wrap">
  <table class="sk-table">
    <thead>
      <tr>
        <th scope="col">
          Name
        </th>
        <th scope="col">
          Status
        </th>
        <th scope="col" data-numeric="true">
          Balance
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="3">
          <div class="sk-empty" data-size="sm">
            <span class="sk-empty-icon">
              <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                <circle cx="11" cy="11" r="7">
                </circle>
                <path d="m20 20-3.5-3.5">
                </path>
              </svg>
            </span>
            <p class="sk-empty-title">
              No accounts match those filters
            </p>
            <div class="sk-empty-body">
              Try widening the date range, or clear the status filter.
              <div class="sk-empty-actions">
                <button class="sk-button" type="button" data-variant="neutral" data-size="sm">
                  Clear filters
                </button>
              </div>
            </div>
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Tabular figures

The same numbers with and without. Watch the decimal points line up.

data-numeric="true"
1,240.00
980.50
111.11
17.00
2,006.75
proportional figures, end-aligned
1,240.00
980.50
111.11
17.00
2,006.75
Show markup
<div class="sk-flex sk-flex-wrap sk-gap-5">
  <div class="sk-flex sk-flex-col sk-gap-2">
    <code class="sink-caption">
      data-numeric="true"
    </code>
    <div class="sk-table-wrap">
      <table class="sk-table" data-density="compact">
        <tbody>
          <tr>
            <td data-numeric="true">
              1,240.00
            </td>
          </tr>
          <tr>
            <td data-numeric="true">
              980.50
            </td>
          </tr>
          <tr>
            <td data-numeric="true">
              111.11
            </td>
          </tr>
          <tr>
            <td data-numeric="true">
              17.00
            </td>
          </tr>
          <tr>
            <td data-numeric="true">
              2,006.75
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <div class="sk-flex sk-flex-col sk-gap-2">
    <code class="sink-caption">
      proportional figures, end-aligned
    </code>
    <div class="sk-table-wrap">
      <table class="sk-table" data-density="compact">
        <tbody>
          <tr>
            <td data-align="end">
              1,240.00
            </td>
          </tr>
          <tr>
            <td data-align="end">
              980.50
            </td>
          </tr>
          <tr>
            <td data-align="end">
              111.11
            </td>
          </tr>
          <tr>
            <td data-align="end">
              17.00
            </td>
          </tr>
          <tr>
            <td data-align="end">
              2,006.75
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>