A table view is a table with what it takes to work in one: a title and the main action, a filter and a sort, selection, an action on each row, and pages. Each of those is a component; the block is how they sit together on one sheet.
Table view
<section class="sheet container-inline flex flex-col overflow-hidden" aria-labelledby="tv-title"> <header class="flex flex-wrap items-center justify-between gap-sm p-md"> <div class="flex flex-col gap-3xs"> <h2 class="heading heading-sm" id="tv-title">Projects</h2> <p class="m(0) fg-text-muted">12 projects in Atlas Labs</p> </div> <button type="button" class="btn btn-primary btn-sm"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M12 5v14M5 12h14" /> </svg>New project</button> </header> <div class="flex flex-wrap items-center gap-xs px-md pb-md"> <label class="input-group input-group-sm grow(1) basis(200) max-w(320)"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 11a7 7 0 1 0 14 0a7 7 0 0 0-14 0M20 20l-4-4" /> </svg> <input class="input-group-field" type="search" placeholder="Filter projects" aria-label="Filter projects"> </label> <div class="flex flex-wrap gap-xs" role="group" aria-label="Status"> <label class="chip"> <input type="checkbox" class="chip-input" name="status" value="ready">Ready</label> <label class="chip"> <input type="checkbox" class="chip-input" name="status" value="building">Building</label> <label class="chip"> <input type="checkbox" class="chip-input" name="status" value="failed">Failed</label> </div> <select class="select select-raised select-sm ml-auto" aria-label="Sort by"> <option class="select-option">Last deployed</option> <option class="select-option">Name</option> <option class="select-option">Region</option> </select> </div> <div class="scroll-area scroll-area-inline bt(1) bt-solid b-line-hair"> <table class="table table-interactive"> <thead> <tr class="table-row"> <th class="table-heading" scope="col"> <input type="checkbox" class="checkbox" aria-label="Select all projects"> </th> <th class="table-heading" scope="col">Project</th> <th class="table-heading" scope="col">Status</th> <th class="table-heading" scope="col">Region</th> <th class="table-heading" scope="col">Last deployed</th> <th class="table-heading" scope="col"> <span class="sr-only">Actions</span> </th> </tr> </thead> <tbody> <tr class="table-row"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Borealis"> </td> <th class="table-cell table-primary text-left" scope="row">Borealis <span class="block fg-text-muted font-regular">borealis.app</span> </th> <td class="table-cell"> <span class="badge badge-info badge-dot">Building</span> </td> <td class="table-cell">Frankfurt</td> <td class="table-cell">4 minutes ago</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-1" aria-label="Actions for Borealis"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-1" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-1" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-1" popovertargetaction="hide">Delete</button> </div> </td> </tr> <tr class="table-row" aria-selected="true"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Atlas" checked> </td> <th class="table-cell table-primary text-left" scope="row">Atlas <span class="block fg-text-muted font-regular">atlas.dev</span> </th> <td class="table-cell"> <span class="badge badge-success badge-dot">Ready</span> </td> <td class="table-cell">Tokyo</td> <td class="table-cell">2 hours ago</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-0" aria-label="Actions for Atlas"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-0" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-0" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-0" popovertargetaction="hide">Delete</button> </div> </td> </tr> <tr class="table-row" aria-selected="true"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Cinder" checked> </td> <th class="table-cell table-primary text-left" scope="row">Cinder <span class="block fg-text-muted font-regular">cinder.io</span> </th> <td class="table-cell"> <span class="badge badge-success badge-dot">Ready</span> </td> <td class="table-cell">Virginia</td> <td class="table-cell">Yesterday</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-2" aria-label="Actions for Cinder"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-2" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-2" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-2" popovertargetaction="hide">Delete</button> </div> </td> </tr> <tr class="table-row"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Drift"> </td> <th class="table-cell table-primary text-left" scope="row">Drift <span class="block fg-text-muted font-regular">drift.so</span> </th> <td class="table-cell"> <span class="badge badge-danger badge-dot">Failed</span> </td> <td class="table-cell">Sydney</td> <td class="table-cell">Yesterday</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-3" aria-label="Actions for Drift"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-3" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-3" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-3" popovertargetaction="hide">Delete</button> </div> </td> </tr> <tr class="table-row"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Ember"> </td> <th class="table-cell table-primary text-left" scope="row">Ember <span class="block fg-text-muted font-regular">ember.dev</span> </th> <td class="table-cell"> <span class="badge badge-success badge-dot">Ready</span> </td> <td class="table-cell">São Paulo</td> <td class="table-cell">3 days ago</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-4" aria-label="Actions for Ember"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-4" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-4" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-4" popovertargetaction="hide">Delete</button> </div> </td> </tr> <tr class="table-row"> <td class="table-cell"> <input type="checkbox" class="checkbox" aria-label="Select Fathom"> </td> <th class="table-cell table-primary text-left" scope="row">Fathom <span class="block fg-text-muted font-regular">fathom.app</span> </th> <td class="table-cell"> <span class="badge badge-success badge-dot">Ready</span> </td> <td class="table-cell">London</td> <td class="table-cell">Last week</td> <td class="table-cell text-right"> <button type="button" class="btn btn-quiet btn-square btn-sm" popovertarget="tv-row-5" aria-label="Actions for Fathom"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </button> <div class="menu menu-end" id="tv-row-5" popover> <a class="menu-item" href="#">Open</a> <button type="button" class="menu-item" popovertarget="tv-row-5" popovertargetaction="hide">Redeploy</button> <hr class="menu-separator"> <button type="button" class="menu-item menu-item-danger" popovertarget="tv-row-5" popovertargetaction="hide">Delete</button> </div> </td> </tr> </tbody> </table> </div> <footer class="flex flex-wrap items-center justify-between gap-sm p-md bt(1) bt-solid b-line-hair"> <p class="m(0) fg-text-muted">2 of 12 selected</p> <nav aria-label="Pagination"> <ol class="pagination"> <li> <a class="btn btn-quiet btn-square btn-sm" aria-disabled="true" aria-label="Previous page"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="m15 6-6 6 6 6" /> </svg> </a> </li> <li> <a class="btn btn-quiet btn-square btn-sm" href="#" aria-label="Page 1" aria-current="page">1</a> </li> <li> <a class="btn btn-quiet btn-square btn-sm" href="#" aria-label="Page 2">2</a> </li> <li> <a class="btn btn-quiet btn-square btn-sm" href="#" aria-label="Next page"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="m9 6 6 6-6 6" /> </svg> </a> </li> </ol> </nav> </footer></section>Blocks are markup to copy. They take the page's own text color and size; the previews set fg-text-body text-sm on the body. Each block is a size container, so it reflows by its own width: in a wide page, a sidebar's worth of room or a dialog.
#What is in it
- One sheet. The bands are parted by hairlines,
bt(1) bt-solid b-line-hair, not by cards inside the card. - The header is a split. A title, a count and the primary action:
flex flex-wrap items-center justify-between, so the action drops under the title when the sheet is narrow. - The filters are a wrapping row. A search input group, filter chips and a raised select;
ml-autosends the sort to the end. No chip is on, so every status shows. - The filter grows, to a point.
grow(1) basis(200) max-w(320): it takes the room in the row, never less than 200px before wrapping, never more than 320px. - The table scrolls in its own band. It stands in a scroll area with
scroll-area-inline, so a narrow sheet scrolls the table and not the page, and the edge fades where columns continue. - Rows are in the order the sort names. "Last deployed" is chosen, and the newest deployment is first.
- Selected rows say so.
aria-selected="true"on atable-rowgives it the selected tone; the checkbox in it is checked. Your script keeps the two in step, and sets the header checkbox'sindeterminateproperty when some rows are selected. - Row actions hide in a menu behind a quiet button at the end, named for its row: "Actions for Atlas".
- The footer is a split: the selection count and a pagination. On the first page "Previous" has
aria-disabled="true"and nohref, since a link cannot be disabled.
#Acting on a selection
When rows are selected, show what can be done to all of them in a floating toolbar: the count, the actions, and a way to clear. Place it with sticky bottom(16) at the foot of the sheet, or fixed at the foot of the viewport; show it while the selection is not empty.
Acting on a selection
<div class="toolbar toolbar-floating flex-wrap justify-center" role="toolbar" aria-label="2 projects selected"> <span class="px-xs fg-text-strong">2 selected</span> <span class="toolbar-separator" role="separator" aria-orientation="vertical"></span> <button type="button" class="btn btn-quiet btn-sm"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 14c0-5 4-10 11-10 0 7-5 11-10 11zM5 14l-2 1 1 3M10 19l-1 2-3-1M14.5 9.5h.01" /> </svg>Redeploy</button> <button type="button" class="btn btn-quiet btn-sm"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M7 11l5 5 5-5M12 4v12" /> </svg>Export</button> <button type="button" class="btn btn-quiet btn-sm btn-danger"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 7h16M10 11v6M14 11v6M6 7l1 12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-12M9 7V4h6v3" /> </svg>Delete</button> <span class="toolbar-separator" role="separator" aria-orientation="vertical"></span> <button type="button" class="btn btn-quiet btn-square btn-sm" aria-label="Clear the selection"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M6 6l12 12M18 6 6 18" /> </svg> </button></div>#What needs a script
Filtering, sorting, selecting all and paging change data, so they belong to your application or your server. The block gives each of them the right element to listen to: a search field, checkboxes, a <select>, links. Without a script, wrap the bands in a <form method="get"> and the filters become a query string the server can answer.
#Accessibility
- The sheet is a
<section>named by its title. - Every checkbox has a name that includes its row: "Select Atlas". The header's is "Select all projects".
- The actions column has a heading for assistive technology, visually hidden.
- The pagination is a
<nav>with a label, and the current page carriesaria-current="page". - The first cell of each row is a
<th scope="row">, so a cell is announced with its project. - The table scrolls sideways in a scroll area. It holds checkboxes and buttons, so the keyboard reaches it through them; a table with nothing focusable in it needs
tabindex="0", a role and a name on the scroll area.
#Related
- SurfaceTrays, sheets and wells: the material ladder every component is built on.
- HeadingA title in the ink of the page, with the line over it and the paragraph under it.
- ButtonA raised control that lifts on hover and sinks when pressed.
- InputText fields cut into the surface, with labels, hints, errors and groups.
- ChipA small pill that is a filter or a removable value.
- SelectA native select whose option list is drawn in the same material.
- TableRows of data on hairlines, with sortable headings and aligned figures.
- Scroll areaA scrolling region whose edges fade where content continues.
- CheckboxA small native well that fills with ink when checked.
- BadgeA small chip for a status, a category or a count.
- MenuA list of actions in a popover, placed against its button.
- PaginationThe pages of a long list, as a row of links.
- ToolbarA small raised sheet that holds a row of quiet controls.