Data Table
A Table people work with: sort it, select rows, act on a row, page through it, and see where it stands while it loads or when there's nothing to show. Specification: docs/components/data-table.md
Basics
A <div class="canon-data-table"> around a Table and a Pagination. Headers with aria-sort sort; the data-column="select" column selects rows (Shift+click selects a range); the data-column="actions" column holds each row's actions, here a Menu. With data-local all rows are in the page, and canon.js sorts them and shows them a page at a time.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | $984.24 | Delivered | |||
| Lauren Monahan | jermaine.corkery@gmail.com | $916.69 | In delivery | |||
| Antoinette Wilkinson | marcos.zulauf@gmail.com | $378.25 | Cancelled | |||
| Glen Tremblay | ora_gutmann@gmail.com | $357.93 | Ready | |||
| Marjorie Mertz | joshua_hodkiewicz@yahoo.com | $954.96 | In delivery | |||
| Janet Hyatt | graham.cassin@yahoo.com | $569.18 | Delivered | |||
| Dwayne Kub | alva.kemmer@gmail.com | $1,204.50 | Waiting | |||
| Rosa Larkin | verna_hand@hotmail.com | $88.40 | Delivered |
Bulk actions
A <div class="canon-data-table-bulk" role="region"> in the data table, named, holding the actions for the selected rows: Buttons by meaning (the main action, data-intent="danger" for a destructive one), a menu button for more. While rows are selected it floats at the bottom of the window, with Clear selection and the count (screen readers hear it change); the app's buttons read the selection from canon-select. Select every row on this page: it offers to select all 12 (here the rows are in the page, so canon.js checks them; for the app's rows, canon-select says allMatching). Without canon.js the bar stays under the table, for a form round trip.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | $984.24 | Delivered | |||
| Lauren Monahan | jermaine.corkery@gmail.com | $916.69 | In delivery | |||
| Antoinette Wilkinson | marcos.zulauf@gmail.com | $378.25 | Cancelled | |||
| Glen Tremblay | ora_gutmann@gmail.com | $357.93 | Ready | |||
| Marjorie Mertz | joshua_hodkiewicz@yahoo.com | $954.96 | In delivery | |||
| Janet Hyatt | graham.cassin@yahoo.com | $569.18 | Delivered | |||
| Dwayne Kub | alva.kemmer@gmail.com | $1,204.50 | Waiting | |||
| Rosa Larkin | verna_hand@hotmail.com | $88.40 | Delivered |
Search, filters, columns, export
A .canon-data-table-toolbar before the table holds a search field (type="search"), a data-panel="filters" button and a data-panel="columns" button; data-align="end" sends an item to the far end. Headers say what their column holds (data-type: text, number, date, choice), which makes them filterable. Filters show as chips under the toolbar: a chip opens its filter, its × removes it; a filtered column also shows a filter button in its header that opens its filter. Columns shows, hides, orders (drag the grip, or its arrow keys) and pins them. An Export item with data-export="csv" downloads the matching rows.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | $984.24 | Delivered | |||
| Lauren Monahan | jermaine.corkery@gmail.com | $916.69 | In delivery | |||
| Antoinette Wilkinson | marcos.zulauf@gmail.com | $378.25 | Cancelled | |||
| Glen Tremblay | ora_gutmann@gmail.com | $357.93 | Ready | |||
| Marjorie Mertz | joshua_hodkiewicz@yahoo.com | $954.96 | In delivery | |||
| Janet Hyatt | graham.cassin@yahoo.com | $569.18 | Delivered | |||
| Dwayne Kub | alva.kemmer@gmail.com | $1,204.50 | Waiting | |||
| Rosa Larkin | verna_hand@hotmail.com | $88.40 | Delivered |
Long tables: a header that stays, text that's cut
Give the frame a max-block-size and it scrolls down too, with the header staying at the top. data-wrap="truncate" on a header cuts its column's text with "…"; the full text shows on hover and is read in full by screen readers.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | $984.24 | Delivered | |||
| Lauren Monahan | jermaine.corkery@gmail.com | $916.69 | In delivery | |||
| Antoinette Wilkinson | marcos.zulauf@gmail.com | $378.25 | Cancelled | |||
| Glen Tremblay | ora_gutmann@gmail.com | $357.93 | Ready | |||
| Marjorie Mertz | joshua_hodkiewicz@yahoo.com | $954.96 | In delivery | |||
| Janet Hyatt | graham.cassin@yahoo.com | $569.18 | Delivered | |||
| Dwayne Kub | alva.kemmer@gmail.com | $1,204.50 | Waiting | |||
| Rosa Larkin | verna_hand@hotmail.com | $88.40 | Delivered |
Cut in the middle: file names, paths and ids
data-wrap="middle" cuts a column's text in its middle and keeps its end: its last third, at least 8 characters, and a file name's extension with the 5 characters before it. Use it where values share a start and differ at the end (file names, paths, hashes). The full text shows on hover and is read in full by screen readers.
| Name | Owner | Size | Modified |
|---|---|---|---|
| Q3-board-report-finance-review-v12-final.pdf | Amara Okafor | 2.4 MB | |
| Q3-board-report-finance-review-v11.pdf | Amara Okafor | 2.3 MB | |
| customer-export-emea-2026-09-28T14-02-11Z.csv | Jonas Weber | 18.7 MB | |
| customer-export-emea-2026-09-21T14-02-07Z.csv | Jonas Weber | 18.1 MB | |
| onboarding-checklist-warehouse-staff.docx | Li Na | 96 KB |
Row actions as buttons
A few actions can sit in the row as buttons: a Toolbar named for its row, so each row is one Tab stop and the arrow keys move between its buttons. The actions column needs no header text.
| Order | Date | Customer | Total | |
|---|---|---|---|---|
| #72 | Garcia-Wood | $66,546.57 | ||
| #73 | Lindgren & Co | $12,840.00 | ||
| #74 | Okafor Studio | $3,215.90 |
Editing cells
data-edit on a header makes its cells editable. Click a value (or focus it and press Enter, or start typing), change it, then Enter or click away to save; Escape puts it back. The editor follows the column's data-type (Status is a choice, Review a date), or a template.canon-data-table-editor in the header: Owner is a Combobox, Target a number field that can't be empty or below 0. Each change fires canon-edit; here the page cancels it to save on its own, then answers canon-saved, or canon-save-failed for a target of 0. KPI isn't editable, nor is the last row's Review (data-edit="none"). Specification: docs/components/data-table-editing.md
| KPI | Owner
|
Target | Actual | Status | Review |
|---|---|---|---|---|---|
| Time to hire | Ada Lovelace | 30 days | 34 days | At risk | |
| Offer acceptance | Grace Hopper | 85% | 88% | On track | |
| First-year retention | Katherine Johnson | 92% | 84% | Off track | |
| Training hours | Ada Lovelace | 24 h | 26 h | On track |
Edit a value to see its event.
Editing in a form
In a form with a Save Bar, each editable cell holds its value in a field (<input hidden name>): an edit changes the field, the Save Bar counts it and the cell is marked; Discard puts every value back, Save sends them together. A switch (or checkbox) in an editable column is changed where it is, and counts the same way.
Pinned columns
data-pin on a header keeps its column in view while the others scroll; here, User. The select column and the actions column are always pinned. A pinned column shows a line at its edge; the last one casts a shadow while rows scroll under it. Scroll the table sideways.
| User | Phone | Company | City | Country | Date | Amount | Status | |||
|---|---|---|---|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | +1 202 555 0143 | Kertzmann LLC | Austin | United States | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | +44 20 7946 0958 | Hills Group | London | United Kingdom | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | +49 30 901820 | Boyle Inc | Berlin | Germany | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | +33 1 70 18 99 00 | Ward & Sons | Paris | France | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | +81 3 6823 1234 | Kuhic Ltd | Tokyo | Japan | $984.24 | Delivered |
Sorting by the app
Without data-local, a sort button fires canon-sort (detail: { index, key, direction }) and updates aria-sort; the app loads the rows in that order. Cancel the event to keep the current order. Sort values come from a cell's data-value, else its <time datetime>, else its text.
| User | Date | Amount | Status | |
|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting |
Press a header to sort.
Lines, stripes, hover, frame
A Data Table takes Table's options on its frame: here data-lines="grid", data-striped and data-hover. A selected row is a step darker than a stripe; the row under the pointer is darker still.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled | |||
| Shari Feeney PhD | cecelia_erdman@yahoo.com | $436.68 | Waiting | |||
| Mrs. Jo Emard | jovany66@hotmail.com | $984.24 | Delivered | |||
| Lauren Monahan | jermaine.corkery@gmail.com | $916.69 | In delivery | |||
| Antoinette Wilkinson | marcos.zulauf@gmail.com | $378.25 | Cancelled | |||
| Glen Tremblay | ora_gutmann@gmail.com | $357.93 | Ready | |||
| Marjorie Mertz | joshua_hodkiewicz@yahoo.com | $954.96 | In delivery | |||
| Janet Hyatt | graham.cassin@yahoo.com | $569.18 | Delivered | |||
| Dwayne Kub | alva.kemmer@gmail.com | $1,204.50 | Waiting | |||
| Rosa Larkin | verna_hand@hotmail.com | $88.40 | Delivered |
Loading, empty, no results, error
While the app loads, aria-busy="true" on the data table: skeleton rows stand in for the body, as many as a page. When there's nothing to show, one row holds an Empty State (canon.js spans it across the columns): nothing yet, nothing matching the filters, or an error with a way to try again.
| User | Date | Amount | Status |
|---|
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
|
No users yet People you invite will show up here. | ||||||
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
|
No matching users Try other words, or clear the filters to see everyone. | ||||||
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
|
Couldn't load users Something went wrong on our side. Try again in a moment. | ||||||
Size
data-size on the data table or a container sizes the table, its controls and its pagination together.
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled |
| User | Date | Amount | Status | |||
|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | $235.71 | Cancelled |
Edge cases
Right to left, pinned columns stick to the right and the actions column to the left; their shadows fall the other way.
| User | Phone | Company | City | Country | Date | Amount | Status | |||
|---|---|---|---|---|---|---|---|---|---|---|
| Edwin Mueller | tyrese_nicolas@gmail.com | +1 202 555 0143 | Kertzmann LLC | Austin | United States | $495.90 | Waiting | |||
| Suzanne Jacobi | cindy_murphy41@yahoo.com | +44 20 7946 0958 | Hills Group | London | United Kingdom | $662.02 | Ready | |||
| Kellie Schoen | troy.lubowitz@gmail.com | +49 30 901820 | Boyle Inc | Berlin | Germany | $235.71 | Cancelled |