Table
A styled data table with caption, header, footer, alignment and row states.
Invoices
Caption, header, row headers, aligned amounts and a footer total.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| INV005 | Paid | PayPal | $550.00 |
| INV006 | Pending | Bank Transfer | $200.00 |
| INV007 | Unpaid | Credit Card | $300.00 |
| Total | $2,250.00 | ||
Code
<Table label="Invoices">
<table>
<TableCaption>A list of your recent invoices.</TableCaption>
<thead>
<tr>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead align="end">Amount</TableHead>
</tr>
</thead>
<tbody>
<tr e:for={inv of invoices} e:key={(i) => i.id}>
<TableHead scope="row">{inv.id}</TableHead>
<TableCell><Badge variant="success" dot>{inv.status}</Badge></TableCell>
<TableCell>{inv.method}</TableCell>
<TableCell align="end">{money(inv.amount)}</TableCell>
</tr>
</tbody>
<tfoot>
<tr>
<TableCell colspan={3}>Total</TableCell>
<TableCell align="end">{money(total)}</TableCell>
</tr>
</tfoot>
</table>
</Table>Usage
import { Table, TableCaption, TableHead, TableCell, rowState } from "@elements/ui/table";
<Table label="Invoices">
<table>
<TableCaption>A list of your recent invoices.</TableCaption>
<thead>
<tr>
<TableHead>Invoice</TableHead>
<TableHead align="end">Amount</TableHead>
</tr>
</thead>
<tbody>
<tr e:for={inv of invoices} e:key={(i) => i.id} data-state={rowState(inv.selected)}>
<TableCell>{inv.id}</TableCell>
<TableCell align="end">{inv.amount}</TableCell>
</tr>
</tbody>
</table>
</Table>Examples
Actions
A DropdownMenu on each row: edit, duplicate, delete.
| Product | Price | Actions |
|---|---|---|
| Wireless Mouse | $29.99 | |
| Mechanical Keyboard | $129.99 | |
| USB-C Hub | $49.99 |
No action yet
Code
<tr e:for={p of products} e:key={(p) => p.name}>
<TableCell>{p.name}</TableCell>
<TableCell align="end">{money(p.price)}</TableCell>
<TableCell align="end">
<DropdownMenu align="end">
<Button slot="trigger" variant="ghost" icon size="sm" label={`Open menu for ${p.name}`}><Icon name="more-horizontal" /></Button>
<DropdownMenuItem onSelect={() => edit(p)}>Edit</DropdownMenuItem>
<DropdownMenuItem onSelect={() => duplicate(p)}>Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive" onSelect={() => remove(p)}>Delete</DropdownMenuItem>
</DropdownMenu>
</TableCell>
</tr>Selection
A checkbox column; selected rows take the selected fill and nothing moves.
| Task | Owner | |
|---|---|---|
| Write the release notes | Ada | |
| Review the theme contract | Grace | |
| Ship the display group | Katherine | |
| Plan the next wave | Hedy |
1 of 4 selected
Code
<tr e:for={t of state.tasks} e:key={(t) => t.id} data-state={rowState(t.selected)}>
<TableCell><input type="checkbox" checked={t.selected} aria-label={`Select ${t.title}`}></TableCell>
<TableCell>{t.title}</TableCell>
<TableCell>{t.owner}</TableCell>
</tr>Caption on top, wide content
side=top; a wide table scrolls inside its labelled region at phone width.
| Location | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
| Downtown | 9 to 5 | 9 to 5 | 9 to 5 | 9 to 7 | 9 to 7 | 10 to 2 |
| Harbor | 8 to 6, every weekday | Closed | ||||
Code
<Table label="Opening hours">
<table class="wide">
<TableCaption side="top">Opening hours</TableCaption>
...
</table>
</Table>RTL
Columns run right to left; end-aligned amounts align left, and the checkbox column hugs its control on the inline end.
| الفاتورة | المبلغ | |
|---|---|---|
| INV001 | $250.00 | |
| INV002 | $150.00 |
Code
<div dir="rtl">
<Table label="الفواتير">
<table>
<thead><tr><TableHead><input type="checkbox" aria-label="تحديد الكل"></TableHead><TableHead>الفاتورة</TableHead><TableHead align="end">المبلغ</TableHead></tr></thead>
<tbody><tr><TableCell><input type="checkbox" aria-label="تحديد"></TableCell><TableCell>INV001</TableCell><TableCell align="end">$250.00</TableCell></tr></tbody>
</table>
</Table>
</div>ARIA roles on divs
APG's table example: role=table, rowgroup, row, columnheader and cell take the same look when a native table cannot be used.
Students currently enrolled in WAI-ARIA 101
First Name Last Name Company
Fred Jackson Canary, Inc.
Sara James Acme, Inc.
Ralph Bard Corp, Inc.
Code
<!-- APG's table from ARIA roles, for a layout a <table> cannot take -->
<Table>
<div role="table" aria-label="Students" aria-describedby="students-caption">
<div class="table-caption" id="students-caption">Students currently enrolled in WAI-ARIA 101</div>
<div role="rowgroup">
<div role="row">
<span role="columnheader">First Name</span>
<span role="columnheader">Last Name</span>
<span role="columnheader" data-align="end">Company</span>
</div>
</div>
<div role="rowgroup">
<div role="row">
<span role="cell">Fred</span> <span role="cell">Jackson</span> <span role="cell" data-align="end">Canary, Inc.</span>
</div>
</div>
</div>
</Table>