Flux 2.0

Component

Table

Quiet data table with sorting, selection, expandable rows, row actions, a toolbar and sticky columns.

Plain-text source, for agents and clipboard use: /components/source/table.txt

When to use it

Presents rows of comparable records. Flux tables are deliberately quiet — the data carries the visual weight, and the chrome recedes to horizontal hairlines. The same markup scales from a static list to a working data table: add the toolbar, sort buttons, selection and row actions you need, and flux-components.js wires them together.

Not this
Do not use a table for layout. For a small number of records with heterogeneous content, a list of cards reads better and adapts to narrow screens. For a spreadsheet where every cell is editable, a grid with role="grid" is a different component.

Examples

Default

A static table. No script is needed.

Bookings departing in September
Reference Departure Status Pax Total
KB-104928 Reykjavik → Akureyri, 12 Sep Confirmed 4 486,000
KB-104931 Golden Circle, 14 Sep On request 12 1,240,500
KB-104940 South Coast, 19 Sep Awaiting supplier 2 198,000
html
<div class="flux-table-wrap">
  <table class="flux-table">
    <caption class="flux-visually-hidden">Bookings departing in September</caption>
    <thead>
      <tr>
        <th scope="col">Reference</th>
        <th scope="col">Departure</th>
        <th scope="col">Status</th>
        <th scope="col" data-numeric>Pax</th>
        <th scope="col" data-numeric>Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">KB-104928</th>
        <td>Reykjavik &rarr; Akureyri, 12 Sep</td>
        <td><span class="flux-badge flux-badge--confirmed">Confirmed</span></td>
        <td data-numeric>4</td>
        <td data-numeric>486,000</td>
      </tr>
      <tr>
        <th scope="row">KB-104931</th>
        <td>Golden Circle, 14 Sep</td>
        <td><span class="flux-badge flux-badge--pending">On request</span></td>
        <td data-numeric>12</td>
        <td data-numeric>1,240,500</td>
      </tr>
      <tr>
        <th scope="row">KB-104940</th>
        <td>South Coast, 19 Sep</td>
        <td><span class="flux-badge flux-badge--awaiting">Awaiting supplier</span></td>
        <td data-numeric>2</td>
        <td data-numeric>198,000</td>
      </tr>
    </tbody>
  </table>
</div>

Compact

40px rows for dense operational views. Type stays at the same size; only the row height and vertical padding change.

Supplier confirmations
Supplier Service Status Rooms
Hotel Kea Twin, half board Confirmed 8
Fosshotel Hekla Double, breakfast Awaiting supplier 6
Hótel Búðir Single, breakfast Unavailable 2
html
<div class="flux-table-wrap">
  <table class="flux-table flux-table--compact">
    <caption class="flux-visually-hidden">Supplier confirmations</caption>
    <thead>
      <tr>
        <th scope="col">Supplier</th>
        <th scope="col">Service</th>
        <th scope="col">Status</th>
        <th scope="col" data-numeric>Rooms</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">Hotel Kea</th>
        <td>Twin, half board</td>
        <td><span class="flux-badge flux-badge--confirmed">Confirmed</span></td>
        <td data-numeric>8</td>
      </tr>
      <tr>
        <th scope="row">Fosshotel Hekla</th>
        <td>Double, breakfast</td>
        <td><span class="flux-badge flux-badge--awaiting">Awaiting supplier</span></td>
        <td data-numeric>6</td>
      </tr>
      <tr>
        <th scope="row">Hótel Búðir</th>
        <td>Single, breakfast</td>
        <td><span class="flux-badge flux-badge--unavailable">Unavailable</span></td>
        <td data-numeric>2</td>
      </tr>
    </tbody>
  </table>
</div>

Data table

Everything together: search, a status filter, a More filters panel, removable filter chips with Clear all, a column menu, sortable headers, selection with a bulk-action bar, expandable rows, one visible row action with an overflow menu, and a sticky header and leading columns. Requires flux-components.js. Remove any part you do not need; the rest keeps working.

September departures

6 of 6 departures

Status

Details Route Status Actions
KB-104928 Reykjavik → Akureyri 12 Sep 2026 Confirmed 4 486,000
KB-104931 Golden Circle 14 Sep 2026 On request 12 1,240,500
KB-104940 South Coast 19 Sep 2026 Awaiting supplier 2 198,000
KB-104952 Snæfellsnes 21 Sep 2026 Confirmed 16 1,612,000
KB-104957 Westfjords 26 Sep 2026 Cancelled 0 0
KB-104963 Diamond Circle 28 Sep 2026 On request 8 904,000
html
<div data-flux-table>
  <div class="flux-table-toolbar">
    <div class="flux-table-toolbar__summary" data-flux-table-summary>
      <h2 class="flux-table-toolbar__title" id="dt-title">September departures</h2>
      <p class="flux-table-toolbar__count" data-flux-table-count="departures">6 of 6 departures</p>
    </div>
    <div class="flux-table-toolbar__bulk" data-flux-table-bulk hidden>
      <p class="flux-table-toolbar__count"><span data-flux-table-selected>0</span> selected</p>
      <button class="flux-button flux-button--secondary flux-button--sm" type="button">Send manifests</button>
      <button class="flux-button flux-button--ghost flux-button--sm" type="button" data-flux-table-clear>Clear selection</button>
    </div>
    <div class="flux-table-toolbar__controls">
      <div class="flux-field">
        <label class="flux-field__label" for="dt-search">Search departures</label>
        <div class="flux-search" data-flux-search>
          <svg class="flux-search__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7" cy="7" r="4" /><path d="M10 10l3.5 3.5" /></svg>
          <input class="flux-input" id="dt-search" type="search" autocomplete="off" data-flux-table-search />
          <button class="flux-search__clear" type="button" aria-label="Clear search" hidden><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" /></svg></button>
        </div>
      </div>
      <div class="flux-field">
        <span class="flux-field__label" id="dt-status-label">Status</span>
        <div class="flux-select" data-flux-select data-flux-table-filter="status">
          <button class="flux-select__control" id="dt-status-trigger" type="button" aria-haspopup="listbox" aria-expanded="false" aria-controls="dt-status-list" aria-labelledby="dt-status-label dt-status-value">
            <span class="flux-select__value" id="dt-status-value">All statuses</span>
            <svg class="flux-select__chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
          </button>
          <input type="hidden" name="status" value="" />
          <div class="flux-select__panel" id="dt-status-list" role="listbox" aria-labelledby="dt-status-label" hidden>
            <ul class="flux-select__options">
              <li><button class="flux-select__option" type="button" role="option" aria-selected="true" data-value=""><span>All statuses</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Confirmed" tabindex="-1"><span>Confirmed</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="On request" tabindex="-1"><span>On request</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Awaiting supplier" tabindex="-1"><span>Awaiting supplier</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Cancelled" tabindex="-1"><span>Cancelled</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
            </ul>
          </div>
        </div>
      </div>
      <button class="flux-button flux-button--secondary" type="button" popovertarget="dt-more-filters" aria-haspopup="dialog" aria-expanded="false">More filters<svg class="flux-button__chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
      <div class="flux-filter-panel" id="dt-more-filters" popover role="dialog" aria-labelledby="dt-more-filters-title">
        <h3 class="flux-filter-panel__title" id="dt-more-filters-title">More filters</h3>
        <div class="flux-field">
        <span class="flux-field__label" id="dt-route-label">Route</span>
        <div class="flux-select" data-flux-select data-flux-table-filter="route">
          <button class="flux-select__control" id="dt-route-trigger" type="button" aria-haspopup="listbox" aria-expanded="false" aria-controls="dt-route-list" aria-labelledby="dt-route-label dt-route-value">
            <span class="flux-select__value" id="dt-route-value">All routes</span>
            <svg class="flux-select__chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
          </button>
          <input type="hidden" name="route" value="" />
          <div class="flux-select__panel" id="dt-route-list" role="listbox" aria-labelledby="dt-route-label" hidden>
            <ul class="flux-select__options">
              <li><button class="flux-select__option" type="button" role="option" aria-selected="true" data-value=""><span>All routes</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Reykjavik &rarr; Akureyri" tabindex="-1"><span>Reykjavik &rarr; Akureyri</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Golden Circle" tabindex="-1"><span>Golden Circle</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="South Coast" tabindex="-1"><span>South Coast</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Snæfellsnes" tabindex="-1"><span>Snæfellsnes</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Westfjords" tabindex="-1"><span>Westfjords</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Diamond Circle" tabindex="-1"><span>Diamond Circle</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
            </ul>
          </div>
        </div>
      </div>
        <div class="flux-field">
        <span class="flux-field__label" id="dt-guide-label">Guide</span>
        <div class="flux-select" data-flux-select data-flux-table-filter="guide">
          <button class="flux-select__control" id="dt-guide-trigger" type="button" aria-haspopup="listbox" aria-expanded="false" aria-controls="dt-guide-list" aria-labelledby="dt-guide-label dt-guide-value">
            <span class="flux-select__value" id="dt-guide-value">All guides</span>
            <svg class="flux-select__chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
          </button>
          <input type="hidden" name="guide" value="" />
          <div class="flux-select__panel" id="dt-guide-list" role="listbox" aria-labelledby="dt-guide-label" hidden>
            <ul class="flux-select__options">
              <li><button class="flux-select__option" type="button" role="option" aria-selected="true" data-value=""><span>All guides</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Anna Jónsdóttir" tabindex="-1"><span>Anna Jónsdóttir</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Unassigned" tabindex="-1"><span>Unassigned</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Kári Pétursson" tabindex="-1"><span>Kári Pétursson</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
              <li><button class="flux-select__option" type="button" role="option" aria-selected="false" data-value="Sara Björk" tabindex="-1"><span>Sara Björk</span><svg class="flux-select__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></li>
            </ul>
          </div>
        </div>
      </div>
      </div>
      <button class="flux-button flux-button--secondary" type="button" popovertarget="dt-columns" aria-haspopup="menu" aria-expanded="false">Columns<svg class="flux-button__chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
      <div class="flux-menu" id="dt-columns" popover role="menu" aria-label="Visible columns">
        <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true" data-flux-table-column="route"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Route</button>
        <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true" data-flux-table-column="date"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Departure</button>
        <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true" data-flux-table-column="status"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Status</button>
        <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true" data-flux-table-column="pax"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Pax</button>
        <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true" data-flux-table-column="total"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Total</button>
      </div>
    </div>
  </div>
  <div class="flux-filter-chips" data-flux-table-chips hidden>
    <ul class="flux-filter-chips__list" aria-label="Active filters"></ul>
    <button class="flux-button flux-button--ghost flux-button--sm" type="button" data-flux-table-clear-filters>Clear all</button>
  </div>
  <p class="flux-visually-hidden" data-flux-table-status aria-live="polite"></p>
  <div class="flux-table-wrap flux-table-wrap--sticky">
    <table class="flux-table flux-table--sticky-header flux-table--sticky-first" aria-labelledby="dt-title">
      <thead>
        <tr>
          <th scope="col" class="flux-table__control"><input class="flux-checkbox" type="checkbox" aria-label="Select all departures" data-flux-table-select-all /></th>
          <th scope="col" class="flux-table__control"><span class="flux-visually-hidden">Details</span></th>
          <th scope="col" aria-sort="none"><button class="flux-table__sort" type="button">Reference<svg class="flux-table__sort-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path class="flux-table__sort-up" d="M5 6.5L8 3.5L11 6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path class="flux-table__sort-down" d="M5 9.5L8 12.5L11 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></th>
          <th scope="col" data-column="route">Route</th>
          <th scope="col" data-column="date" aria-sort="none"><button class="flux-table__sort" type="button">Departure<svg class="flux-table__sort-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path class="flux-table__sort-up" d="M5 6.5L8 3.5L11 6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path class="flux-table__sort-down" d="M5 9.5L8 12.5L11 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></th>
          <th scope="col" data-column="status">Status</th>
          <th scope="col" data-column="pax" data-numeric aria-sort="none"><button class="flux-table__sort" type="button">Pax<svg class="flux-table__sort-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path class="flux-table__sort-up" d="M5 6.5L8 3.5L11 6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path class="flux-table__sort-down" d="M5 9.5L8 12.5L11 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></th>
          <th scope="col" data-column="total" data-numeric aria-sort="none"><button class="flux-table__sort" type="button">Total<svg class="flux-table__sort-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path class="flux-table__sort-up" d="M5 6.5L8 3.5L11 6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path class="flux-table__sort-down" d="M5 9.5L8 12.5L11 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button></th>
          <th scope="col" class="flux-table__actions"><span class="flux-visually-hidden">Actions</span></th>
        </tr>
      </thead>
      <tbody>
        <tr data-guide="Anna Jónsdóttir">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104928" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-1" aria-label="Details for KB-104928"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104928</th>
          <td>Reykjavik &rarr; Akureyri</td>
          <td data-sort-value="2026-09-12">12 Sep 2026</td>
          <td><span class="flux-badge flux-badge--confirmed">Confirmed</span></td>
          <td data-numeric>4</td>
          <td data-numeric>486,000</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-1" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104928"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-1" popover role="menu" aria-label="Actions for KB-104928">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-1" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">Anna Jónsdóttir</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">Coach 18</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">0</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr data-guide="Unassigned">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104931" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-2" aria-label="Details for KB-104931"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104931</th>
          <td>Golden Circle</td>
          <td data-sort-value="2026-09-14">14 Sep 2026</td>
          <td><span class="flux-badge flux-badge--pending">On request</span></td>
          <td data-numeric>12</td>
          <td data-numeric>1,240,500</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-2" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104931"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-2" popover role="menu" aria-label="Actions for KB-104931">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-2" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">Unassigned</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">Coach 32</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">620,250</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr data-guide="Kári Pétursson">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104940" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-3" aria-label="Details for KB-104940"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104940</th>
          <td>South Coast</td>
          <td data-sort-value="2026-09-19">19 Sep 2026</td>
          <td><span class="flux-badge flux-badge--awaiting">Awaiting supplier</span></td>
          <td data-numeric>2</td>
          <td data-numeric>198,000</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-3" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104940"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-3" popover role="menu" aria-label="Actions for KB-104940">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-3" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">Kári Pétursson</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">Minibus 9</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">99,000</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr data-guide="Sara Björk">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104952" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-4" aria-label="Details for KB-104952"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104952</th>
          <td>Snæfellsnes</td>
          <td data-sort-value="2026-09-21">21 Sep 2026</td>
          <td><span class="flux-badge flux-badge--confirmed">Confirmed</span></td>
          <td data-numeric>16</td>
          <td data-numeric>1,612,000</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-4" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104952"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-4" popover role="menu" aria-label="Actions for KB-104952">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-4" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">Sara Björk</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">Coach 18</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">0</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr data-guide="—">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104957" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-5" aria-label="Details for KB-104957"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104957</th>
          <td>Westfjords</td>
          <td data-sort-value="2026-09-26">26 Sep 2026</td>
          <td><span class="flux-badge flux-badge--cancelled">Cancelled</span></td>
          <td data-numeric>0</td>
          <td data-numeric>0</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-5" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104957"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-5" popover role="menu" aria-label="Actions for KB-104957">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-5" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">—</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">—</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">0</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr data-guide="Unassigned">
          <td class="flux-table__control"><input class="flux-checkbox flux-table__select" type="checkbox" aria-label="Select KB-104963" /></td>
          <td class="flux-table__control">
            <button class="flux-table__icon-button flux-table__toggle" type="button" aria-expanded="false" aria-controls="dt-detail-6" aria-label="Details for KB-104963"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
          </td>
          <th scope="row">KB-104963</th>
          <td>Diamond Circle</td>
          <td data-sort-value="2026-09-28">28 Sep 2026</td>
          <td><span class="flux-badge flux-badge--pending">On request</span></td>
          <td data-numeric>8</td>
          <td data-numeric>904,000</td>
          <td class="flux-table__actions">
            <div class="flux-table__action-group">
              <button class="flux-button flux-button--ghost flux-button--sm" type="button">Manage</button>
              <button class="flux-table__icon-button" type="button" popovertarget="dt-menu-6" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for KB-104963"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
              <div class="flux-menu" id="dt-menu-6" popover role="menu" aria-label="Actions for KB-104963">
                <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
                <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
                <button class="flux-menu__item" type="button" role="menuitem">Cancel departure</button>
              </div>
            </div>
          </td>
        </tr>
        <tr class="flux-table__detail" id="dt-detail-6" hidden>
          <td colspan="9">
            <dl class="flux-dl">
              <div class="flux-dl__pair"><dt class="flux-dl__term">Guide</dt><dd class="flux-dl__detail">Unassigned</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Vehicle</dt><dd class="flux-dl__detail">Minibus 16</dd></div>
              <div class="flux-dl__pair"><dt class="flux-dl__term">Balance due</dt><dd class="flux-dl__detail flux-dl__detail--data">452,000</dd></div>
            </dl>
          </td>
        </tr>
      </tbody>
      <tbody data-flux-table-empty hidden>
        <tr><td class="flux-table__empty" colspan="9">No departures match the search and filters.</td></tr>
      </tbody>
    </table>
  </div>
</div>

Anatomy

PartDescription
WrapperBordered, rounded surface that also provides horizontal scroll. With --sticky it bounds the height so the header can stay in view.
ToolbarTitle and live count on the left; search, the most-used filters, a More filters panel and the column menu on the right. Replaced by the bulk-action bar while rows are selected.
Filter chipsOne removable chip per active filter, below the toolbar, then Clear all. Hidden while no filter is active.
HeaderMono, uppercase, extra-small, letter-spaced, in text-subtle. Sortable headers hold a button and an arrow.
Row headerThe cell that names the record, usually its reference: th scope="row", body type, bold.
Control columnsSelection checkbox and expand chevron, before the row header.
Rows48px, divided by a single hairline. 40px with --compact.
Detail rowHidden until expanded. Shares a tbody with its row so they sort and filter together.
Row actionsOne visible action and an overflow menu for the rest, in the last column.

API

NameKindDescription
flux-table-wrapclassBordered surface and scroll container. Always wrap the table.
flux-table-wrap--stickyclassBounds the wrapper height so a sticky header has something to stick within.
flux-tableclassThe table element itself.
flux-table--compactclass40px rows instead of 48px. Type size is unchanged.
flux-table--sticky-headerclassKeeps the header row in view while the wrapper scrolls. Pair with flux-table-wrap--sticky.
flux-table--sticky-firstclassPins every column up to and including the row header during horizontal scroll. Offsets are measured by flux-components.js.
scopeattributeRequired on every header cell: scope="col" in the head, scope="row" on the cell that names each record.
data-numericattributeRight-aligns and applies tabular figures. Put it on both th and td. A sortable numeric column sorts by number.
captionelementNames the table. Add flux-visually-hidden when a visible heading already does, or use aria-labelledby.
data-flux-tableattributeOn the element wrapping the toolbar and table. Initialises every behaviour below.
flux-table__sortclassButton inside a sortable th. The th carries aria-sort="none", which the script updates to ascending or descending.
flux-sortelementCancelable event dispatched on the data-flux-table element before a sort, with detail.column (the th’s data-column), detail.index and detail.direction. A framework that renders the rows cancels it and renders the order and aria-sort from detail; the table still announces the sort.
data-wrapattributeLets a prose cell wrap. Body cells are single-line by default so rows keep their height and the wrapper scrolls instead.
data-sort-valueattributeOn a cell whose text does not sort correctly, such as a formatted date. Use an ISO date or a plain number.
data-columnattributeNames a header so filters and the column menu can find its column.
flux-table__controlclassNarrow cell for the selection checkbox or the expand toggle.
flux-checkboxclassFlux-sized native checkbox. Add flux-table__select on row checkboxes.
data-flux-table-select-allattributeHeader checkbox. Selects visible rows and shows a mixed state when some are selected.
flux-table__toggleclassExpand button with aria-expanded and aria-controls pointing at the detail row.
flux-table__detailclassHidden detail row with one cell spanning every column. Put it in the same tbody as its row.
flux-table__actionsclassLast cell, holding flux-table__action-group: one ghost small button and the overflow trigger.
flux-table__icon-buttonclassIcon-only table control for the toggle and overflow trigger. Always give it an aria-label naming the row.
flux-menuclassPopover menu with role="menu", opened by a button with popovertarget, aria-haspopup="menu" and aria-expanded. A text trigger ends in flux-button__chevron.
flux-menu__itemclassA button with role="menuitem", or role="menuitemcheckbox" with aria-checked and a flux-menu__check icon.
flux-table-toolbarclassRow above the wrapper: __summary (title and count), __bulk (selection actions, hidden until rows are selected) and __controls.
data-flux-table-countattributeOn the count element. Its value is the noun, as in "4 of 6 departures".
data-flux-table-searchattributeSearch input. Matches against the text of each record row.
data-flux-table-filterattributeOn a Flux select whose value filters the column named by the attribute. An empty value means all. A name with no matching column filters on that data attribute of the record row instead, as data-flux-table-filter="guide" matches data-guide.
flux-filter-panelclassPopover with role="dialog" for filters used less often, opened by a secondary button with popovertarget, aria-haspopup="dialog" and aria-expanded. Holds a flux-filter-panel__title and Flux selects with data-flux-table-filter. Filters apply as they change; there is no Apply button.
data-flux-table-chipsattributeOn flux-filter-chips, placed below the toolbar and hidden. Holds an empty flux-filter-chips__list with aria-label="Active filters" and the Clear all button. The script writes one flux-chip per active filter.
flux-chipclassA removable filter chip: a button reading "Label: value" with a cross. Written by the script; use it by hand only for filters the table script does not own.
data-flux-table-clear-filtersattributeClear all button inside the chips row. Resets search and every filter.
data-flux-table-columnattributeOn a menuitemcheckbox. Shows or hides the column with the matching data-column.
data-flux-table-statusattributeVisually hidden live region. Announces sorting, filtering and selection changes.
data-flux-table-emptyattributeA hidden tbody shown when search and filters leave no rows.
flux-visually-hiddenclassRead by assistive technology, not drawn. For captions, header names and icon labels.

States

StateTreatment
Row hoverlayer-hover-01 fill across the row.
Selectedsurface-selected fill across the row, driven by its checkbox. The checkbox carries the state; the fill only echoes it.
SortedThe header text darkens and a single arrow in icon-brand shows the direction. Unsorted headers show both arrows.
ExpandedThe chevron turns down and the detail row appears on layer-02.
Bulk selectionThe toolbar summary is replaced by the selection count and bulk actions.
FilteredEach active search or filter shows as a chip on surface-selected below the toolbar; the border turns border-interactive on hover. Removing a chip resets that control.
No resultsThe empty tbody replaces the rows, in text-secondary.

Keyboard

KeysAction
TabMoves through interactive cells only: checkboxes, toggles, sort buttons, row actions. A static table is not a tab stop.
Enter / SpaceSorts by the focused header, toggles the focused checkbox or expands the focused row.
Arrow Up / Down, Home, EndMove between items in an open menu.
EscapeCloses an open menu or the More filters panel and returns focus to its trigger.
Enter / Space on a chipRemoves that filter and moves focus to the next chip, or to search when none remain.

Accessibility

  • Every header cell needs scope="col" or scope="row"; without it the table is a grid of unrelated cells to a screen reader.
  • Name the table with a caption or aria-labelledby pointing at the toolbar title.
  • Sort state is aria-sort on the th, not the arrow. Only sortable headers carry it.
  • Icon-only controls name their row: "Details for KB-104928", "More actions for KB-104928".
  • Selection is the checkbox state. The row fill is a second signal, never the only one.
  • The live region announces sort order, result counts and selection, so changes that happen away from focus are heard.
  • Each chip is named "Remove filter Status: Confirmed", so its purpose is clear without the cross. Removing one never drops focus to the page.
  • Status is announced from the badge text, not inferred from the tint.
  • The wrapper scrolls horizontally on narrow screens rather than shrinking text below the minimum readable size.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-table-cell-padding-y
  • --flux-table-cell-padding-x
  • --flux-table-row-height | -compact
  • --flux-table-divider
  • --flux-table-header-text
  • --flux-table-header-bg
  • --flux-table-row-hover
  • --flux-table-row-selected
  • --flux-table-detail-bg
  • --flux-table-control-width
  • --flux-table-icon | -hover | -hover-bg
  • --flux-table-sort-icon-active
  • --flux-table-sticky-max-height
  • --flux-menu-bg | -border | -shadow | -item-hover
  • --flux-chip-height | -bg | -text | -border | -border-hover | -icon
  • --flux-filter-panel-width | -padding | -gap

Do and don’t

Do

  • Right-align money, quantities and dates that are compared down a column.
  • Use the Badge component for status cells.
  • Make the reference a row header, so every cell in the row is announced with it.
  • Keep one visible action per row and put the rest in the overflow menu, so action columns line up.
  • Make only the columns people actually compare sortable.
  • Keep the one or two filters people use most in the toolbar and put the rest in More filters; the chips show what is active either way.

Don’t

  • Do not add vertical column borders or a border on every cell.
  • Do not zebra-stripe and divide at the same time; choose one.
  • Do not invent status colours that differ from the badge set.
  • Do not shrink type to fit more columns. Use compact rows, hide columns or let the wrapper scroll.
  • Do not offer the row header in the column menu; a row must always say what it is.