<divclass="data-table"data-data-table><divclass="data-table__toolbar"><labelclass="sr-only"for="data-table-filter">Filter people</label><inputclass="form-control form-control--sm data-table__filter"id="data-table-filter"type="search"placeholder="Filter by name, team or city…"data-table-filter><pclass="data-table__summary"aria-live="polite"data-table-summary>6 people</p></div><divclass="table-responsive"><tableclass="table table--hover table--middle"><captionclass="sr-only">Team members, sortable by column</caption><thead><tr><thscope="col"aria-sort="none"><buttonclass="data-table__sort"type="button"data-sort="0"data-type="text">Name<spanclass="data-table__sort-icon"aria-hidden="true"></span></button></th><thscope="col"aria-sort="none"><buttonclass="data-table__sort"type="button"data-sort="1"data-type="text">Team<spanclass="data-table__sort-icon"aria-hidden="true"></span></button></th><thscope="col"aria-sort="none"><buttonclass="data-table__sort"type="button"data-sort="2"data-type="text">City<spanclass="data-table__sort-icon"aria-hidden="true"></span></button></th><thscope="col"aria-sort="none"><buttonclass="data-table__sort"type="button"data-sort="3"data-type="date">Joined<spanclass="data-table__sort-icon"aria-hidden="true"></span></button></th><thscope="col"aria-sort="none"><buttonclass="data-table__sort"type="button"data-sort="4"data-type="text">Status<spanclass="data-table__sort-icon"aria-hidden="true"></span></button></th></tr></thead><tbody><tr><td>Maya Chen</td><td>Design</td><td>Lisbon</td><td><timedatetime="2021-03-14">2021-03-14</time></td><td><spanclass="data-table__status data-table__status--active">Active</span></td></tr><tr><td>Daniel Okafor</td><td>Engineering</td><td>Lagos</td><td><timedatetime="2019-11-02">2019-11-02</time></td><td><spanclass="data-table__status data-table__status--active">Active</span></td></tr><tr><td>Ava Martinez</td><td>Product</td><td>Madrid</td><td><timedatetime="2023-06-21">2023-06-21</time></td><td><spanclass="data-table__status data-table__status--away">Away</span></td></tr><tr><td>Leo Novak</td><td>Engineering</td><td>Prague</td><td><timedatetime="2020-01-09">2020-01-09</time></td><td><spanclass="data-table__status data-table__status--offline">Offline</span></td></tr><tr><td>Sara Kim</td><td>Marketing</td><td>Seoul</td><td><timedatetime="2022-08-30">2022-08-30</time></td><td><spanclass="data-table__status data-table__status--active">Active</span></td></tr><tr><td>Omar Haddad</td><td>Design</td><td>Amman</td><td><timedatetime="2024-02-12">2024-02-12</time></td><td><spanclass="data-table__status data-table__status--away">Away</span></td></tr></tbody></table></div><pclass="data-table__empty"hiddendata-table-empty>No people match your filter.</p></div>
A sortable, filterable data table built on Spruce's `.table` styles and a few lines of vanilla JavaScript.
Technical Details
The sort controls are real buttons inside the column headers; the header's aria-sort attribute reflects the state (ascending, descending, none), which screen readers announce.
Text columns sort with Intl.Collator (locale and number aware); data-type="date" columns sort chronologically.
The filter input updates a polite live region with the number of matching rows and shows an empty-state message when nothing matches.
The table is wrapped in .table-responsive, so it scrolls horizontally on small screens. Without JavaScript, it is a regular readable table.
Documentation
Learn about Spruce CSS through our extensive documentation.