table-wc

Progressive enhancement wrapper for HTML tables. Adds sorting, filtering, pagination, row expansion, and selection.

Overview

The <table-wc> component wraps a standard HTML table to provide interactive features like column sorting, text filtering, pagination, expandable rows, row selection with bulk actions, responsive card layouts, and sticky headers/columns. All features are opt-in via data attributes and the table remains fully functional without JavaScript.

<table-wc> <table data-filterable data-paginate="5"> <thead> <tr> <th data-sort="string">Name</th> <th data-sort="number">Age</th> </tr> </thead> <tbody> <tr> <td>Alice Johnson</td> <td>32</td> </tr> <tr> <td>Bob Smith</td> <td>28</td> </tr> </tbody> </table> </table-wc>

Basic Usage

Wrap any HTML table with <table-wc> and add data attributes to enable features. The table structure remains semantic and accessible.

<table-wc> <table> <thead> <tr> <th data-sort="string">Name</th> <th data-sort="string">Email</th> <th data-sort="string">Department</th> </tr> </thead> <tbody> <tr> <td>Alice Johnson</td> <td>alice.johnson@example.com</td> <td>Engineering</td> </tr> <!-- More rows... --> </tbody> </table> </table-wc>

Sorting

Add data-sort to any <th> element to make that column sortable. Click the column header to cycle through ascending, descending, and unsorted states.

Sort Types

Value Description
string Alphabetical sorting (case-insensitive)
number Numeric sorting (handles decimals and negatives)
date Date sorting (parses common date formats)

Custom Sort Values

Use data-sort-value on cells when the display value differs from the sort value (e.g., formatted currency or dates).

<!-- Display shows formatted value, sorts by raw value --> <td data-sort-value="95000">$95,000</td> <td data-sort-value="2019-03-15">Mar 15, 2019</td>

Filtering

Add data-filterable to the table to enable text filtering. A search input is automatically generated above the table. Rows that don't match the filter text are hidden.

<table-wc> <table data-filterable> <thead> <tr> <th>Name</th> <th>Email</th> <th>Department</th> </tr> </thead> <tbody> <!-- Rows will be filtered as user types --> </tbody> </table> </table-wc>

Pagination

Add data-paginate="n" to the table to show only n rows per page. Pagination controls are automatically generated below the table.

<table-wc> <table data-paginate="5"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Department</th> <th>Hire Date</th> </tr> </thead> <tbody> <!-- 12 rows, showing 5 per page --> </tbody> </table> </table-wc>

Row Expansion

Create expandable rows by adding data-expandable to a row and following it with a data-expand-content row. A toggle button is automatically added to control the expansion.

<table-wc> <table> <thead> <tr> <th></th> <th>Name</th> <th>Department</th> <th>Status</th> </tr> </thead> <tbody> <tr data-expandable> <td><button data-action="toggle-expand">...</button></td> <td>Alice Johnson</td> <td>Engineering</td> <td>Active</td> </tr> <tr data-expand-content hidden> <td colspan="4"> <div>Additional details here...</div> </td> </tr> </tbody> </table> </table-wc>

Row Selection

Add checkboxes to rows for selection, with a "select all" checkbox in the header and a bulk actions bar that appears when rows are selected.

<table-wc> <div data-bulk-actions hidden> <span><strong data-selected-count>0</strong> selected</span> <button>Export</button> <button>Archive</button> <button class="danger">Delete</button> </div> <table> <thead> <tr> <th><input type="checkbox" data-action="select-all"/></th> <th>Name</th> <th>Email</th> <th>Department</th> </tr> </thead> <tbody> <tr data-selectable> <td><input type="checkbox" data-action="select-row"/></td> <td>Alice Johnson</td> <td>alice.johnson@example.com</td> <td>Engineering</td> </tr> <!-- More selectable rows... --> </tbody> </table> </table-wc>

Responsive Card Mode

Add data-responsive="card" to transform the table into a card layout on narrow screens. Use data-label on cells to show column headers in card mode.

<table-wc> <table data-responsive="card"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Department</th> <th>Status</th> </tr> </thead> <tbody> <tr> <td data-label="Name">Alice Johnson</td> <td data-label="Email">alice.johnson@example.com</td> <td data-label="Department">Engineering</td> <td data-label="Status">Active</td> </tr> </tbody> </table> </table-wc>

Sticky Headers and Columns

Use data-layout-sticky to keep headers or columns visible while scrolling.

Sticky Options

Value Description
header Keeps the table header row fixed at the top
column Keeps the first column fixed on horizontal scroll
both Both header and first column are sticky

Multiple Sticky Columns

Use data-sticky-column="n" to make the first n columns sticky.

<!-- Sticky header only --> <table data-layout-sticky="header">...</table> <!-- Sticky first column --> <table data-layout-sticky="column">...</table> <!-- Both sticky --> <table data-layout-sticky="both">...</table> <!-- First 2 columns sticky --> <table data-layout-sticky="column" data-sticky-column="2">...</table>

Events

The component dispatches custom events for each interactive feature.

Event Detail Description
table:sort { column: number, direction: "asc"|"desc"|null } Fired when a column is sorted.
table:filter { query: string, matchCount: number } Fired when the filter input changes.
table:page { page: number, totalPages: number } Fired when the page changes.
table:expand { row: HTMLTableRowElement, expanded: boolean } Fired when a row is expanded or collapsed.
table:selection { selectedRows: HTMLTableRowElement[], count: number } Fired when row selection changes.
const table = document.querySelector('table-wc'); // Sort events table.addEventListener('table:sort', (e) => { console.log(`Column ${e.detail.column} sorted ${e.detail.direction}`); }); // Filter events table.addEventListener('table:filter', (e) => { console.log(`Filter "${e.detail.query}" matches ${e.detail.matchCount} rows`); }); // Page change events table.addEventListener('table:page', (e) => { console.log(`Page ${e.detail.page} of ${e.detail.totalPages}`); }); // Row expansion events table.addEventListener('table:expand', (e) => { console.log(`Row ${e.detail.expanded ? 'expanded' : 'collapsed'}`); }); // Selection events table.addEventListener('table:selection', (e) => { console.log(`${e.detail.count} rows selected`); });

Public API

The component exposes methods for programmatic control.

Method Parameters Returns Description
goToPage(n) n: number void Navigate to a specific page (1-indexed).
setFilter(query) query: string void Programmatically set the filter text.
refresh() - void Re-apply sorting, filtering, and pagination.
getSelectedRows() - HTMLTableRowElement[] Get array of currently selected rows.
const table = document.querySelector('table-wc'); // Navigate to page 3 table.goToPage(3); // Filter by department table.setFilter('engineering'); // Refresh after external data change table.refresh(); // Get selected rows for bulk operation const selected = table.getSelectedRows(); selected.forEach(row => { console.log(row.cells[1].textContent); // Log names });

Attributes Reference

Complete list of data attributes supported by table-wc.

Table Attributes

Attribute Value Description
data-filterable boolean Enable text filtering with auto-generated search input.
data-paginate number Number of rows per page. Enables pagination controls.
data-responsive card Transform to card layout on narrow screens.
data-layout-sticky header | column | both Make header and/or first column sticky.
data-sticky-column number Number of columns to make sticky (default: 1).

Header Cell Attributes

Attribute Value Description
data-sort string | number | date Enable sorting for this column with specified type.

Data Cell Attributes

Attribute Value Description
data-sort-value string Custom value used for sorting instead of cell text.
data-label string Label shown in responsive card mode.

Row Attributes

Attribute Value Description
data-expandable boolean Mark row as expandable (expects following expand-content row).
data-expand-content boolean Mark row as expansion content (hidden by default).
data-selectable boolean Enable row selection with checkbox.

Action Attributes

Attribute Element Description
data-action="select-all" checkbox Select/deselect all rows.
data-action="select-row" checkbox Select/deselect individual row.
data-action="toggle-expand" button Toggle row expansion.
data-bulk-actions container Container shown when rows are selected.
data-selected-count element Displays count of selected rows.

Accessibility

Keyboard Navigation

Key Action
Tab Move between interactive elements (sort headers, checkboxes, buttons)
Enter / Space Activate sort, toggle checkbox, or expand/collapse row
Arrow Up/Down Navigate between rows when focused on a row
Home / End Jump to first/last page in pagination

ARIA Attributes

  • aria-sort on sortable column headers indicates current sort direction
  • aria-expanded on expand buttons indicates row expansion state
  • aria-selected on selectable rows indicates selection state
  • aria-live="polite" on filter results announces match count
  • aria-label on pagination controls describes navigation

Reduced Motion

When prefers-reduced-motion: reduce is set, all transitions and animations are disabled. Sort indicators, row expansion, and pagination all function without motion effects.

Screen Reader Support

  • Sort state changes are announced when columns are sorted
  • Filter result counts are announced as users type
  • Row expansion state is announced when toggled
  • Selection count updates are announced on change