Hesoyam

Table

Semantic HTML table. Wrap it in TableRoot so wide grids scroll instead of breaking the page.

Basic

TableRoot is the overflow wrapper. Table is the table element. Header cells are TableHead, body cells are TableCell.

HostRegionRequests
iad.proxyIAD12.4k
fra.proxyFRA8.1k
sin.proxySIN3.6k

        
1 <TableRoot>
2 <Table>
3 <TableHeader>
4 <TableRow>
5 <TableHead>Host</TableHead>
6 <TableHead>Region</TableHead>
7 <TableHead>Requests</TableHead>
8 </TableRow>
9 </TableHeader>
10 <TableBody>
11 <TableRow>
12 <TableCell>iad.proxy</TableCell>
13 <TableCell>IAD</TableCell>
14 <TableCell>12.4k</TableCell>
15 </TableRow>
16 </TableBody>
17 </Table>
18 </TableRoot>

Striped

striped on TableBody tints even rows with --ds-gray-100.

HostRegionRequests
iad.proxyIAD12.4k
fra.proxyFRA8.1k
sin.proxySIN3.6k
syd.proxySYD

        
1 <TableBody striped>
2 <TableRow>
3 <TableCell>iad.proxy</TableCell>
4 <TableCell>IAD</TableCell>
5 <TableCell>12.4k</TableCell>
6 </TableRow>
7 </TableBody>

Bordered

bordered draws a hairline on every cell. Use it when columns must line up for comparison.

HostRegionRequests
iad.proxyIAD12.4k
fra.proxyFRA8.1k
sin.proxySIN3.6k

        
1 <TableBody bordered>
2 <TableRow>
3 <TableCell>iad.proxy</TableCell>
4 <TableCell>IAD</TableCell>
5 <TableCell>12.4k</TableCell>
6 </TableRow>
7 </TableBody>

Interactive

interactive adds a hover wash and a pointer. You still own click and the selected row.

HostRegionStatus
iad.proxyIADReady
fra.proxyFRAReady
sin.proxySINDegraded
syd.proxySYDIdle

        
1 <TableBody interactive>
2 <TableRow
3 :class="selectedHost === row.host && 'bg-[var(--ds-gray-100)]'"
4 @click="selectedHost = row.host"
5 >
6 <TableCell>{{ row.host }}</TableCell>
7 </TableRow>
8 </TableBody>

Full featured

TableColgroup sets column widths. TableFooter sits under the body for a subtotal.

ProductUsagePriceCharge
Residential 1 GB840 MB$4.20 / GB$35
Datacenter 5 GB5.0 GB$1.10 / GB$55
Mobile 500 MB120 MB$8.00 / GB$10
ISP 2 GB1.4 GB$3.40 / GB$48
Sticky session48 h$0.12 / h$6
Subtotal$153

        
1 <Table>
2 <TableColgroup>
3 <TableCol class="w-[44%]" />
4 <TableCol class="w-[22%]" />
5 <TableCol class="w-[22%]" />
6 <TableCol class="w-[12%]" />
7 </TableColgroup>
8 <TableHeader>…</TableHeader>
9 <TableBody interactive striped>…</TableBody>
10 <TableFooter>
11 <TableRow>
12 <TableCell :col-span="3">Subtotal</TableCell>
13 <TableCell>$15,326</TableCell>
14 </TableRow>
15 </TableFooter>
16 </Table>

Virtualized

virtualize on TableBody pins the header and caps the root at 420px. This port scrolls the body; it does not window individual rows.

ProductUsagePrice
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB
Residential 1 GB840 MB$4.20 / GB
Datacenter 5 GB5.0 GB$1.10 / GB
Mobile 500 MB120 MB$8.00 / GB

        
1 <TableBody striped virtualize>
2 <TableRow v-for="row in virtualRows" :key="row.key">
3 <TableCell>{{ row.product }}</TableCell>
4 <TableCell>{{ row.usage }}</TableCell>
5 <TableCell>{{ row.price }}</TableCell>
6 </TableRow>
7 </TableBody>

Best practices

  • Use a table when every row shares a shape and at least one column is comparable. A membership row with one action is an entity list, not a table. Key/value metadata is a description list.
  • Wrap the grid in TableRoot. The table element itself should stay Table.
  • Empty lists render an Empty State outside the table, not a blank TableBody.
  • Unknown cells are an em dash. Do not write N/A, null, or leave the cell empty.
  • Headers are Title Case nouns: Last Used, Requests (7d). Never a sentence.
  • Numeric columns get tabular-nums so digits line up.
  • interactive is a hover affordance. Wire selection and keyboard yourself.