Name
Email
Role
Ada Lovelace
ada@example.com
Admin
Grace Hopper
grace@example.com
Editor
Alan Turing
alan@example.com
Viewer
Ada Lovelace ada@example.com Admin Grace Hopper grace@example.com Editor Alan Turing alan@example.com Viewer
< td > grace@example.com </ td >
< td > alan@example.com </ td >
< Table.HeaderCell > Name </ Table.HeaderCell >
< Table.HeaderCell > Email </ Table.HeaderCell >
< Table.HeaderCell > Role </ Table.HeaderCell >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell > ada@example.com </ Table.Cell >
< Table.Cell > Admin </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell > grace@example.com </ Table.Cell >
< Table.Cell > Editor </ Table.Cell >
< Table.Cell > Alan Turing </ Table.Cell >
< Table.Cell > alan@example.com </ Table.Cell >
< Table.Cell > Viewer </ Table.Cell >
Markup only; wire the filtering logic yourself. The layout is a flex toolbar above the table.
#1001 Ada Lovelace Shipped #1002 Grace Hopper Processing #1003 Alan Turing Shipped
< div class = " flex w-full flex-col gap-3 " >
< div class = " flex flex-wrap items-center gap-2 " >
< input class = " input flex-1 " type = " search " placeholder = " Search orders " />
commandfor = " orders-filters "
< i class = " ti ti-filter " aria-hidden = " true " ></ i > Filters
< button type = " button " class = " btn btn-primary btn-sm " >
< i class = " ti ti-plus " aria-hidden = " true " ></ i > New order
< table class = " table table-striped " >
< dialog id = " orders-filters " class = " dialog drawer " closedby = " any " >
< div class = " dialog-header " >
< h2 class = " dialog-title " > Filters </ h2 >
< div class = " dialog-body flex flex-col gap-4 " >
< label class = " field-label " for = " filter-customer " > Customer </ label >
< label class = " field-label " >
< input type = " checkbox " class = " checkbox " /> Unfulfilled only
< div class = " dialog-footer " >
< button type = " submit " class = " btn btn-primary " value = " apply " >
< div className = " flex w-full flex-col gap-3 " >
< div className = " flex flex-wrap items-center gap-2 " >
< Input type = " search " placeholder = " Search orders " className = " flex-1 " />
commandfor = " orders-filters-r "
< Button variant = " primary " size = " sm " icon = { IconPlus } >
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Table.HeaderCell > Status </ Table.HeaderCell >
< Table.Cell > #1001 </ Table.Cell >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell > Shipped </ Table.Cell >
< Table.Cell > #1002 </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell > Processing </ Table.Cell >
< Table.Cell > #1003 </ Table.Cell >
< Table.Cell > Alan Turing </ Table.Cell >
< Table.Cell > Shipped </ Table.Cell >
< Drawer.Container id = " orders-filters-r " >
< Drawer.Title > Filters </ Drawer.Title >
< Drawer.Body className = " flex flex-col gap-4 " >
< Field.Label > Customer </ Field.Label >
< Input type = " search " placeholder = " Any " />
< Checkbox /> Unfulfilled only
< Button variant = " ghost " value = " reset " type = " submit " formNoValidate >
< Button variant = " primary " value = " apply " type = " submit " >
Name
Created
Ada Lovelace
2024-01-12
Alan Turing
2024-03-04
Grace Hopper
2024-05-21
Ada Lovelace 2024-01-12 Alan Turing 2024-03-04 Grace Hopper 2024-05-21
< th aria-sort = " ascending " >
< button type = " button " class = " btn btn-ghost btn-sm " >
Name < i class = " ti ti-chevron-up " aria-hidden = " true " ></ i >
< button type = " button " class = " btn btn-ghost btn-sm " >
Created < i class = " ti ti-selector " aria-hidden = " true " ></ i >
< Table.HeaderCell aria-sort = " ascending " >
< Button variant = " ghost " size = " sm " iconTrailing = { IconChevronUp } >
< Button variant = " ghost " size = " sm " iconTrailing = { IconSelector } >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell > 2024-01-12 </ Table.Cell >
< Table.Cell > Alan Turing </ Table.Cell >
< Table.Cell > 2024-03-04 </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell > 2024-05-21 </ Table.Cell >
There is no sort component — the click handler and ordering are yours. Set aria-sort to ascending or descending on the sorted column only ; leaving it on every column tells a screen reader they are all sorted.
Order
Status
#1001
Shipped
#1002
Processing
#1003
Shipped
#1004
Processing
#1001 Shipped #1002 Processing #1003 Shipped #1004 Processing
< table class = " table table-striped " >
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Status </ Table.HeaderCell >
< Table.Cell > #1001 </ Table.Cell >
< Table.Cell > Shipped </ Table.Cell >
< Table.Cell > #1002 </ Table.Cell >
< Table.Cell > Processing </ Table.Cell >
< Table.Cell > #1003 </ Table.Cell >
< Table.Cell > Shipped </ Table.Cell >
< Table.Cell > #1004 </ Table.Cell >
< Table.Cell > Processing </ Table.Cell >
SKU
Name
A-001
Widget
A-002
Gadget
< table class = " table table-bordered table-relaxed " >
< Table.HeaderCell > SKU </ Table.HeaderCell >
< Table.HeaderCell > Name </ Table.HeaderCell >
< Table.Cell > A-001 </ Table.Cell >
< Table.Cell > Widget </ Table.Cell >
< Table.Cell > A-002 </ Table.Cell >
< Table.Cell > Gadget </ Table.Cell >
SKU
Name
Qty
A-001
Widget
128
A-002
Gadget
64
A-003
Sprocket
32
A-001 Widget 128 A-002 Gadget 64 A-003 Sprocket 32
< table class = " table table-compact table-striped " >
< th data-align = " right " > Qty </ th >
< td data-align = " right " > 128 </ td >
< td data-align = " right " > 64 </ td >
< td data-align = " right " > 32 </ td >
< Table density = " compact " striped >
< Table.HeaderCell > SKU </ Table.HeaderCell >
< Table.HeaderCell > Name </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Qty </ Table.HeaderCell >
< Table.Cell > A-001 </ Table.Cell >
< Table.Cell > Widget </ Table.Cell >
< Table.Cell numeric > 128 </ Table.Cell >
< Table.Cell > A-002 </ Table.Cell >
< Table.Cell > Gadget </ Table.Cell >
< Table.Cell numeric > 64 </ Table.Cell >
< Table.Cell > A-003 </ Table.Cell >
< Table.Cell > Sprocket </ Table.Cell >
< Table.Cell numeric > 32 </ Table.Cell >
ID
Customer
#1001
Ada
#1002
Grace
#1003
Alan
#1004
Donald
#1005
Edsger
#1006
Linus
#1007
Tony
#1008
Margaret
#1009
Barbara
#1010
Frances
#1001 Ada #1002 Grace #1003 Alan #1004 Donald #1005 Edsger #1006 Linus #1007 Tony #1008 Margaret #1009 Barbara #1010 Frances
< div style = " overflow: auto; max-height: 240px " >
< table class = " table table-sticky " >
< div style = { { overflow: " auto " , maxHeight: 240 } } >
< Table.HeaderCell > ID </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Table.Cell > { id } </ Table.Cell >
< Table.Cell > { name } </ Table.Cell >
Name
Email
Team
Role
Joined
Ada Lovelace
ada@example.com
Platform
Admin
2021-04-02
Grace Hopper
grace@example.com
Compilers
Editor
2020-11-18
Ada Lovelace ada@example.com Platform Admin 2021-04-02 Grace Hopper grace@example.com Compilers Editor 2020-11-18
< div style = " overflow-x: auto; max-width: 360px " >
< table class = " table table-pin-col " >
< td > grace@example.com </ td >
< div style = { { overflowX: " auto " , maxWidth: 360 } } >
< Table.HeaderCell > Name </ Table.HeaderCell >
< Table.HeaderCell > Email </ Table.HeaderCell >
< Table.HeaderCell > Team </ Table.HeaderCell >
< Table.HeaderCell > Role </ Table.HeaderCell >
< Table.HeaderCell > Joined </ Table.HeaderCell >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell > ada@example.com </ Table.Cell >
< Table.Cell > Platform </ Table.Cell >
< Table.Cell > Admin </ Table.Cell >
< Table.Cell > 2021-04-02 </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell > grace@example.com </ Table.Cell >
< Table.Cell > Compilers </ Table.Cell >
< Table.Cell > Editor </ Table.Cell >
< Table.Cell > 2020-11-18 </ Table.Cell >
Item
Qty
Total
Widget
3
$129.00
Gadget
12
$1,344.50
Widget 3 $129.00 Gadget 12 $1,344.50
< th data-align = " right " > Qty </ th >
< th data-align = " right " > Total </ th >
< td data-align = " right " > 3 </ td >
< td data-align = " right " > $129.00 </ td >
< td data-align = " right " > 12 </ td >
< td data-align = " right " > $1,344.50 </ td >
< Table.HeaderCell > Item </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Qty </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Total </ Table.HeaderCell >
< Table.Cell > Widget </ Table.Cell >
< Table.Cell numeric > 3 </ Table.Cell >
< Table.Cell numeric > $129.00 </ Table.Cell >
< Table.Cell > Gadget </ Table.Cell >
< Table.Cell numeric > 12 </ Table.Cell >
< Table.Cell numeric > $1,344.50 </ Table.Cell >
Order
Customer
Total
#1001
Ada Lovelace
$129.00
#1002
Grace Hopper
$72.50
#1003
Alan Turing
$310.00
#1001 Ada Lovelace $129.00 #1002 Grace Hopper $72.50 #1003 Alan Turing $310.00
< th class = " table-cell-gutter " ></ th >
< th data-align = " right " > Total </ th >
< td class = " table-cell-gutter " >
class = " ti ti-circle-check "
style = " color: var(--color-success) "
< td data-align = " right " > $129.00 </ td >
< td class = " table-cell-gutter " >
< i class = " ti ti-clock " aria-hidden = " true " ></ i >
< td data-align = " right " > $72.50 </ td >
< td class = " table-cell-gutter " >
style = " color: var(--color-danger) "
< td data-align = " right " > $310.00 </ td >
< Table.HeaderCell gutter />
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Total </ Table.HeaderCell >
style = { { color: " var(--color-success) " } }
< Table.Cell > #1001 </ Table.Cell >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell numeric > $129.00 </ Table.Cell >
< IconClock size = { 16 } aria-hidden />
< Table.Cell > #1002 </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell numeric > $72.50 </ Table.Cell >
style = { { color: " var(--color-danger) " } }
< Table.Cell > #1003 </ Table.Cell >
< Table.Cell > Alan Turing </ Table.Cell >
< Table.Cell numeric > $310.00 </ Table.Cell >
Put a Checkbox in the first cell. Wire the select-all header checkbox yourself.
< th class = " table-cell-gutter " >
< input type = " checkbox " class = " checkbox " aria-label = " Select all " />
< td class = " table-cell-gutter " >
< input type = " checkbox " class = " checkbox " aria-label = " Select #1001 " />
< td class = " table-cell-gutter " >
aria-label = " Select #1002 "
< td class = " table-cell-gutter " >
< input type = " checkbox " class = " checkbox " aria-label = " Select #1003 " />
< Table.HeaderCell gutter >
< Checkbox aria-label = " Select all " />
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Checkbox aria-label = " Select #1001 " />
< Table.Cell > #1001 </ Table.Cell >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Checkbox aria-label = " Select #1002 " defaultChecked />
< Table.Cell > #1002 </ Table.Cell >
< Table.Cell > Grace Hopper </ Table.Cell >
< Checkbox aria-label = " Select #1003 " />
< Table.Cell > #1003 </ Table.Cell >
< Table.Cell > Alan Turing </ Table.Cell >
#1001
Ada
#1002
Grace
#1003
Alan
#1001 Ada #1002 Grace #1003 Alan
< Table.Cell > #1001 </ Table.Cell >
< Table.Cell > Ada </ Table.Cell >
< Table.Cell > #1002 </ Table.Cell >
< Table.Cell > Grace </ Table.Cell >
< Table.Cell > #1003 </ Table.Cell >
< Table.Cell > Alan </ Table.Cell >
Order
Customer
Total
#1001
Ada Lovelace
$129.00
#1002
Grace Hopper
$72.50
#1003
Alan Turing
$310.00
< th data-align = " right " > Total </ th >
< tr class = " table-row-link " >
< td >< a href = " #1001 " > #1001 </ a ></ td >
< td data-align = " right " > $129.00 </ td >
< tr class = " table-row-link " >
< td >< a href = " #1002 " > #1002 </ a ></ td >
< td data-align = " right " > $72.50 </ td >
< tr class = " table-row-link " >
< td >< a href = " #1003 " > #1003 </ a ></ td >
< td data-align = " right " > $310.00 </ td >
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Total </ Table.HeaderCell >
< a href = " #1001 " > #1001 </ a >
< Table.Cell > Ada Lovelace </ Table.Cell >
< Table.Cell numeric > $129.00 </ Table.Cell >
< a href = " #1002 " > #1002 </ a >
< Table.Cell > Grace Hopper </ Table.Cell >
< Table.Cell numeric > $72.50 </ Table.Cell >
< a href = " #1003 " > #1003 </ a >
< Table.Cell > Alan Turing </ Table.Cell >
< Table.Cell numeric > $310.00 </ Table.Cell >
The row-filling hit area is an ::after on the row’s first <a>. Any other link or button in the row must establish its own stacking context — position: relative plus a non-auto z-index — or the pseudo-element swallows its clicks. .btn already qualifies.
Item
Total
Widget
$129.00
Gadget
$72.50
Total
$201.50
Widget $129.00 Gadget $72.50 Total $201.50
< th data-align = " right " > Total </ th >
< td data-align = " right " > $129.00 </ td >
< td data-align = " right " > $72.50 </ td >
< td data-align = " right " > $201.50 </ td >
< Table.HeaderCell > Item </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Total </ Table.HeaderCell >
< Table.Cell > Widget </ Table.Cell >
< Table.Cell numeric > $129.00 </ Table.Cell >
< Table.Cell > Gadget </ Table.Cell >
< Table.Cell numeric > $72.50 </ Table.Cell >
< Table.Cell > Total </ Table.Cell >
< Table.Cell numeric > $201.50 </ Table.Cell >
Item
Amount
Widget
$129.00
Gadget
$72.50
Subtotal
$201.50
Tax
$20.15
Total
+12%
$221.65
Widget $129.00 Gadget $72.50 Subtotal $201.50 Tax $20.15 Total +12% $221.65
< th data-align = " right " > Amount </ th >
< td data-align = " right " > $129.00 </ td >
< td data-align = " right " > $72.50 </ td >
< td data-align = " right " > $201.50 </ td >
< td data-align = " right " > $20.15 </ td >
< span class = " badge badge-success badge-soft " >
< i class = " ti ti-trending-up " aria-hidden = " true " ></ i >
< td data-align = " right " > $221.65 </ td >
< Table.HeaderCell > Item </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Amount </ Table.HeaderCell >
< Table.Cell > Widget </ Table.Cell >
< Table.Cell numeric > $129.00 </ Table.Cell >
< Table.Cell > Gadget </ Table.Cell >
< Table.Cell numeric > $72.50 </ Table.Cell >
< Table.Cell > Subtotal </ Table.Cell >
< Table.Cell numeric > $201.50 </ Table.Cell >
< Table.Cell > Tax </ Table.Cell >
< Table.Cell numeric > $20.15 </ Table.Cell >
< Badge variant = " success " soft icon = { IconTrendingUp } >
< Table.Cell numeric > $221.65 </ Table.Cell >
Order
Customer
Total
No orders match your filters.
No orders match your filters.
< th data-align = " right " > Total </ th >
< td class = " table-empty " colspan = " 3 " > No orders match your filters. </ td >
< Table.HeaderCell > Order </ Table.HeaderCell >
< Table.HeaderCell > Customer </ Table.HeaderCell >
< Table.HeaderCell align = " right " > Total </ Table.HeaderCell >
< Table.Empty colSpan = { 3 } > No orders match your filters. </ Table.Empty >
A table never owns a scroll region. sticky, pinCol, and horizontal overflow on narrow viewports all need you to supply the scrolling ancestor:
< div style = " overflow: auto; max-height: 240px " >
< table class = " table table-sticky " >
Part
Renders
Class
Table
<table>
table
Table.Head
<thead>
—
Table.Body
<tbody>
—
Table.Foot
<tfoot>
—
Table.Row
<tr>
table-row-link when asLink
Table.HeaderCell
<th scope="col">
table-header-cell
Table.Cell
<td>
table-cell
Table.Empty
its own <tr> + <td>
table-empty
Part
Prop
Type
Default
Table
striped
boolean
false
Table
bordered
boolean
false
Table
density
"compact" | "default" | "relaxed"
"default"
Table
relaxed
boolean
false
Table
sticky
boolean
false
Table
pinCol
boolean
false
Table.Row
selected
boolean
false
Table.Row
asLink
boolean
false
Table.Cell, Table.HeaderCell
align
"left" | "right" | "center"
"left"
Table.Cell, Table.HeaderCell
gutter
boolean
false
Table.Cell
numeric
boolean
false
relaxed is deprecated — use density="relaxed". sticky needs a scrolling ancestor and pinCol an overflow-x one. selected sets [data-selected] for checkbox-less selection; asLink styles the row but you still supply the <a>. align="left" and density="default" emit nothing. Set gutter on the header cell too so the column lines up, and Table.Empty takes a colSpan — set it to the column count.
Plus each element’s native attributes. Table takes no classNames — every part accepts className. Table.Empty renders its own <tr>, so drop it straight into Table.Body.
Class / var
Effect
table
Full width, text-sm, collapsed borders; cells get 0.75rem/0.375rem padding and a bottom divider (~32px rows)
table-striped
Tints even <tbody> rows
table-bordered
Border on the table and every cell
table-compact
0.5rem/0.25rem padding, text-xs
table-relaxed
1rem/0.75rem padding
table-sticky
Pins <thead> cells to the top of the scroll region
table-pin-col
Pins the first cell of every row against horizontal scroll
table-header-cell
Header styling for a cell outside <thead>
table-cell
Cell styling for markup that isn’t a real <td>
table-cell-numeric
Right-aligns and uses tabular figures
table-cell-gutter
1.5rem centered status column with muted text — colour the icon yourself when status carries meaning
table-empty
Centered muted message cell; set colspan to the column count
table-row-link
Row-filling hit area taken from the row’s first <a>
Modifiers compose — striped with sticky with relaxed is fine.
Plain <th> and <td> need no class: descendant selectors style them, and <tfoot> rows are semibold with a strong top divider on the first automatically. [data-align="right"] or [data-align="center"] on a cell aligns it; left is the default. The row tint responds to input[type="checkbox"]:checked, .checkbox[data-checked], or [data-selected] on the <tr>.
Striping, hover and selection are scoped to <tbody>, and table-pin-col pins :first-child — so the pinned column must literally be first, and a loose <tr> outside <tbody> won’t stripe.