Skip to main content
VelinStyle v1.1.0
⌂ Home
  1. Docs
  2. Components
  3. Data table

Data table

The <velin-data-table> Web Component enhances a plain <table> with sorting, filtering and pagination. The markup stays semantic, so the table is fully readable before the component upgrades and with JavaScript disabled.

Basic

Wrap an ordinary table. Add sortable to make every column sortable, or opt in per column with data-sort.

Team members
Name Commits Joined Role
Ada Lovelace4122023-01-15Engineering
Grace Hopper12802022-06-02Engineering
Alan Turing962024-03-01Research
Katherine Johnson7342021-11-20Research
Radia Perlman582025-07-09Networking
<velin-data-table filter-input="#dtFilter" page-size="3">
  <table class="velin-table">
    <caption>Team members</caption>
    <thead>
      <tr>
        <th data-sort="text">Name</th>
        <th data-sort="number">Commits</th>
        <th data-sort="date">Joined</th>
        <th data-sort="none">Role</th>
      </tr>
    </thead>
    <tbody>…</tbody>
  </table>
</velin-data-table>

The table needs an accessible name. Provide a <caption>, an aria-label, or a label attribute on the host — the component warns in the console if all three are missing.

Sorting

Each sortable header is replaced with a real <button>, so sorting works with the keyboard and is announced as a button by screen readers. The current state lives on the <th> as aria-sort, and only one column is sorted at a time. Activating the same column again reverses the direction.

data-sortComparison
textCase-insensitive string comparison (default with sortable)
numberParsed as a number, so 9 sorts before 30
dateParsed with Date.parse
noneColumn is not sortable and gets no button

To sort by something other than the visible text — a raw timestamp behind a formatted date, for example — put the comparable value in data-sort-value on the cell.

<td data-sort-value="1700000000">15 Nov 2023</td>

Filtering

Point filter-input at any existing input; typing filters rows on a debounced substring match. Non-matching rows get the hidden attribute, which removes them from the layout and the accessibility tree rather than just hiding them visually.

By default the whole row is searched. Mark one or more cells with data-filter to restrict matching to those columns.

<td data-filter>Ada Lovelace</td>
<td>412</td>  <!-- ignored while filtering -->

When nothing matches, a single row with empty-text is rendered across all columns.

Pagination

Set page-size to show a slice at a time. A labelled <nav> with Previous and Next buttons and a “Page X of Y” status is appended; the edge buttons are disabled rather than removed so focus is never lost. Pagination is omitted entirely while everything fits on one page, and filtering resets to page 1.

Attributes

AttributeDefaultNotes
sortableMakes every column sortable as text unless data-sort says otherwise
filter-inputCSS selector of the input driving the filter
page-sizeRows per page; omit for no pagination
empty-textNo matching rowsShown when the filter matches nothing
labelApplied as aria-label when the table has no caption
pagination-labelTable paginationAccessible name of the pagination nav
previous-text / next-textPrevious / NextPagination button labels

JavaScript API

const table = document.querySelector('velin-data-table');

table.sort(1, 'descending');   // column index, optional direction
table.filter('ada');           // same matching as the bound input
table.goToPage(2);             // clamped to the available pages

table.rows;          // every data row
table.matchingRows;  // rows passing the current filter
table.visibleRows;   // rows on the current page
table.page;          // current page number
table.pageCount;     // total pages for the current filter

Events

Accessibility

Contract status in core/a11y/component-contracts.json: pass. See also Tables for the static styling options.