Skip to content
Documentation

Content

Table

Tabular data with sortable headers, row selection, numeric alignment and a sticky header.

CSS only components/table.css

Example

Put the table in a dsgn-table-wrap. The wrapper scrolls sideways when the table is wider than the screen, so it is a focusable region with a name: keyboard users can scroll it too.

Client Amount (CZK)
2026-114 Studio Kolo s.r.o. 12 Nov 2026 24 200
2026-115 Pekárna u Mlýna 19 Nov 2026 8 650
2026-116 Novák Architects 2 Dec 2026 132 000
An invoice table with selection and sorting
<div class="dsgn-table-wrap" tabindex="0" role="region" aria-label="Invoices">
  <table class="dsgn-table">
    <thead>
      <tr>
        <th scope="col" class="dsgn-table-select"><input type="checkbox" class="dsgn-checkbox" aria-label="Select all"></th>
        <th scope="col" aria-sort="ascending"><button class="dsgn-table-sort" type="button">Number<span class="dsgn-icon" aria-hidden="true">arrow_upward</span></button></th>
        <th scope="col">Client</th>
        <th scope="col"><button class="dsgn-table-sort" type="button">Due<span class="dsgn-icon" aria-hidden="true">unfold_more</span></button></th>
        <th scope="col" data-numeric>Amount (CZK)</th>
      </tr>
    </thead>
    <tbody>
      <tr aria-selected="true">
        <td class="dsgn-table-select"><input type="checkbox" class="dsgn-checkbox" aria-label="Select 2026-114" checked></td>
        <td>2026-114</td>
        <td>Studio Kolo s.r.o.</td>
        <td>12 Nov 2026</td>
        <td data-numeric>24 200</td>
      </tr>
      <tr>
        <td class="dsgn-table-select"><input type="checkbox" class="dsgn-checkbox" aria-label="Select 2026-115"></td>
        <td>2026-115</td>
        <td>Pekárna u Mlýna</td>
        <td>19 Nov 2026</td>
        <td data-numeric>8 650</td>
      </tr>
      <tr>
        <td class="dsgn-table-select"><input type="checkbox" class="dsgn-checkbox" aria-label="Select 2026-116"></td>
        <td>2026-116</td>
        <td>Novák Architects</td>
        <td>2 Dec 2026</td>
        <td data-numeric>132 000</td>
      </tr>
    </tbody>
  </table>
</div>

Numbers

data-numeric on a header and its cells aligns them to the end. The whole table uses tabular figures, so digits line up from row to row.

Sorting

A sortable header holds a dsgn-table-sort button with an icon: unfold_more while unsorted, arrow_upward or arrow_downward once sorted. Sorting is visual only: your code sorts the data and sets aria-sort on the <th>, which tells screen readers the order.

Selection

A first column with dsgn-table-select holds a checkbox per row and one in the header to select all. Mark a selected row with aria-selected="true", so it is highlighted and announced.

Row height

A row is one control height plus a small gap above and below: 36 px in density S, 44 px in M and 52 px in L. A row with a checkbox, badge, link or ghost button keeps exactly that height, so a table never jumps when a row gets an action.

Accessibility

  • Give every header cell a scope: col for column headers, row for a header at the start of a row.
  • Name the table: an aria-label on the wrapper, or a <caption> (which can be visually hidden).
  • Every checkbox needs an aria-label that names its row.

Usage

Row height = control line-height + 2 × row-pad-y (S 36 · M 44 · L 52): one control height plus space-4 above and below, so buttons and inputs in a row never touch its edges. A row with a checkbox, badge, link or ghost button keeps exactly that height. Header sticks to the top of the wrapper when it scrolls. Sorting is visual only: the app sorts the data and sets aria-sort on the <th>. Icons: unfold_more (sortable), arrow_upward (ascending), arrow_downward (descending).

Details

Classes
.dsgn-table-wrap .dsgn-table .dsgn-table-sort .dsgn-table-select .dsgn-checkbox .dsgn-badge .dsgn-icon-button .dsgn-button .dsgn-icon .dsgn-table-muted
Tokens used
--dsgn-border --dsgn-control-font-size --dsgn-control-line-height --dsgn-control-pad-x --dsgn-control-pad-y --dsgn-control-subtle --dsgn-control-subtle-hover --dsgn-control-subtle-pressed --dsgn-control-text --dsgn-duration-fast --dsgn-ease --dsgn-focus --dsgn-focus-offset --dsgn-focus-width --dsgn-font-sans --dsgn-font-weight-medium --dsgn-inset --dsgn-line-container --dsgn-line-height-caption --dsgn-line-separator --dsgn-neutral-subtle --dsgn-neutral-subtle-hover --dsgn-neutral-subtle-pressed --dsgn-radius-l --dsgn-radius-s --dsgn-row-pad-y --dsgn-shadow-raised --dsgn-space-2 --dsgn-space-4 --dsgn-surface-base --dsgn-surface-raised --dsgn-text --dsgn-text-muted
In Figma
Table header cell · Table cell · Table example
Import alone
dsgn.systems/components/table.css