Components
Table
Styled semantic HTML tables with striped, grid, and compact variants.
Apply .ui-table to a standard <table> element (<caption>, <thead>, <tbody>, <tfoot>, <tr>, <th>, <td>). Rows include hover tints by default. Set data-state="selected" on a <tr> for selected styling.
Wrap wide tables in <div class="overflow-x-auto"> for horizontal scrolling.
Loading components…
<table class="ui-table"> <caption> A list of your recent invoices. </caption> <thead> <tr> <th>Invoice</th> <th>Status</th> <th>Method</th> <th class="text-right">Amount</th> </tr> </thead> <tbody> <tr> <td>INV-001</td> <td>Paid</td> <td>Credit card</td> <td class="text-right">$250.00</td> </tr> <tr> <td>INV-002</td> <td>Pending</td> <td>PayPal</td> <td class="text-right">$150.00</td> </tr> <tr> <td>INV-003</td> <td>Unpaid</td> <td>Bank transfer</td> <td class="text-right">$350.00</td> </tr> <tr> <td>INV-004</td> <td>Paid</td> <td>Credit card</td> <td class="text-right">$450.00</td> </tr> </tbody> <tfoot> <tr> <td colspan="3">Total</td> <td class="text-right">$1,200.00</td> </tr> </tfoot></table>/** * table.css — Table (.ui-table) * * @layer variables, components * @requires layers.css, _variables.css, _reset.css * @uses oklch(from …) — translucent hover/footer tints * @tokens --ui-table-* (@layer variables) */@layer variables { :root { --ui-table-font-family: var(--font-family-body); --ui-table-font-size: var(--font-size-sm); --ui-table-foreground: var(--foreground); --ui-table-background: transparent; /* structural borders — the outer/footer rule, the header separator, the tbody row dividers, and the column dividers (grid variant) */ --ui-table-border: 1px solid var(--border); --ui-table-head-border: var(--ui-table-border); --ui-table-row-border: var(--ui-table-border); --ui-table-column-border: none; --ui-table-cell-padding-block: var(--step-2); --ui-table-cell-padding-inline: var(--step-2); --ui-table-head-height: var(--step-10); --ui-table-head-foreground: var(--foreground); --ui-table-head-font-weight: var(--font-weight-strong); --ui-table-foot-background: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-foot-font-weight: var(--font-weight-strong); --ui-table-row-background--hover: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-row-background--selected: var(--muted); --ui-table-alternating-background: var(--muted); --ui-table-caption-foreground: var(--muted-foreground); --ui-table-caption-padding-block: var(--gap-xs); --ui-table-caption-padding-inline: var(--gap-xs); }}@layer zazz.components { /* =========================================================================== TABLE =========================================================================== */ .ui-table { font-family: var(--ui-table-font-family); font-size: var(--ui-table-font-size); font-variant-numeric: tabular-nums; inline-size: 100%; color: var(--ui-table-foreground); background-color: var(--ui-table-background); caption-side: bottom; text-align: start; } .ui-table :is(th, td) { padding-block: var(--ui-table-cell-padding-block); padding-inline: var(--ui-table-cell-padding-inline); vertical-align: middle; text-align: start; border-inline: var(--ui-table-column-border); } .ui-table th { color: var(--ui-table-head-foreground); font-weight: var(--ui-table-head-font-weight); } .ui-table thead th { block-size: var(--ui-table-head-height); padding-block: 0; } .ui-table thead tr { border-block-end: var(--ui-table-head-border); } .ui-table tbody tr:not(:last-child) { border-block-end: var(--ui-table-row-border); } .ui-table tfoot { background-color: var(--ui-table-foot-background); font-weight: var(--ui-table-foot-font-weight); border-block-start: var(--ui-table-border); } .ui-table caption { color: var(--ui-table-caption-foreground); padding-block: var(--ui-table-caption-padding-block); padding-inline: var(--ui-table-caption-padding-inline); } /* =========================================================================== TABLE LAYOUT & CAPTION SIDE =========================================================================== */ /* omit = table-layout: auto */ .ui-table[data-layout="fixed"] { table-layout: fixed; } /* omit = caption-side: bottom (set on .ui-table) */ .ui-table caption[data-side="top"] { caption-side: top; } /* =========================================================================== TABLE STATES =========================================================================== */ /* hover/selected paint on the cells so they layer above any row stripe; disable hover with --ui-table-row-background--hover: transparent */ .ui-table tbody tr:hover > :is(th, td) { background-color: var(--ui-table-row-background--hover); } .ui-table tbody tr[data-state="selected"] > :is(th, td) { background-color: var(--ui-table-row-background--selected); } /* =========================================================================== TABLE VARIANTS =========================================================================== */ .ui-table[data-variant="alternating"] { --ui-table-row-border: none; } .ui-table[data-variant="alternating"] tbody tr:nth-child(even) { background-color: var(--ui-table-alternating-background); } .ui-table[data-variant="grid"] { --ui-table-column-border: var(--ui-table-border); border: var(--ui-table-border); } /* =========================================================================== TABLE SIZES =========================================================================== */ .ui-table[data-size="sm"] { --ui-table-cell-padding-block: var(--step-1); --ui-table-cell-padding-inline: var(--step-1_5); --ui-table-head-height: var(--step-8); }}Alternating rows
Add data-variant="alternating" for zebra striping:
Loading components…
<table class="ui-table" data-variant="alternating"> <thead> <tr> <th>Name</th> <th>Role</th> <th>Location</th> </tr> </thead> <tbody> <tr> <td>Ada Lovelace</td> <td>Engineering</td> <td>London</td> </tr> <tr> <td>Grace Hopper</td> <td>Engineering</td> <td>New York</td> </tr> <tr> <td>Katherine Johnson</td> <td>Research</td> <td>Hampton</td> </tr> <tr> <td>Margaret Hamilton</td> <td>Engineering</td> <td>Boston</td> </tr> <tr> <td>Annie Easley</td> <td>Research</td> <td>Cleveland</td> </tr> </tbody></table>/** * table.css — Table (.ui-table) * * @layer variables, components * @requires layers.css, _variables.css, _reset.css * @uses oklch(from …) — translucent hover/footer tints * @tokens --ui-table-* (@layer variables) */@layer variables { :root { --ui-table-font-family: var(--font-family-body); --ui-table-font-size: var(--font-size-sm); --ui-table-foreground: var(--foreground); --ui-table-background: transparent; /* structural borders — the outer/footer rule, the header separator, the tbody row dividers, and the column dividers (grid variant) */ --ui-table-border: 1px solid var(--border); --ui-table-head-border: var(--ui-table-border); --ui-table-row-border: var(--ui-table-border); --ui-table-column-border: none; --ui-table-cell-padding-block: var(--step-2); --ui-table-cell-padding-inline: var(--step-2); --ui-table-head-height: var(--step-10); --ui-table-head-foreground: var(--foreground); --ui-table-head-font-weight: var(--font-weight-strong); --ui-table-foot-background: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-foot-font-weight: var(--font-weight-strong); --ui-table-row-background--hover: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-row-background--selected: var(--muted); --ui-table-alternating-background: var(--muted); --ui-table-caption-foreground: var(--muted-foreground); --ui-table-caption-padding-block: var(--gap-xs); --ui-table-caption-padding-inline: var(--gap-xs); }}@layer zazz.components { /* =========================================================================== TABLE =========================================================================== */ .ui-table { font-family: var(--ui-table-font-family); font-size: var(--ui-table-font-size); font-variant-numeric: tabular-nums; inline-size: 100%; color: var(--ui-table-foreground); background-color: var(--ui-table-background); caption-side: bottom; text-align: start; } .ui-table :is(th, td) { padding-block: var(--ui-table-cell-padding-block); padding-inline: var(--ui-table-cell-padding-inline); vertical-align: middle; text-align: start; border-inline: var(--ui-table-column-border); } .ui-table th { color: var(--ui-table-head-foreground); font-weight: var(--ui-table-head-font-weight); } .ui-table thead th { block-size: var(--ui-table-head-height); padding-block: 0; } .ui-table thead tr { border-block-end: var(--ui-table-head-border); } .ui-table tbody tr:not(:last-child) { border-block-end: var(--ui-table-row-border); } .ui-table tfoot { background-color: var(--ui-table-foot-background); font-weight: var(--ui-table-foot-font-weight); border-block-start: var(--ui-table-border); } .ui-table caption { color: var(--ui-table-caption-foreground); padding-block: var(--ui-table-caption-padding-block); padding-inline: var(--ui-table-caption-padding-inline); } /* =========================================================================== TABLE LAYOUT & CAPTION SIDE =========================================================================== */ /* omit = table-layout: auto */ .ui-table[data-layout="fixed"] { table-layout: fixed; } /* omit = caption-side: bottom (set on .ui-table) */ .ui-table caption[data-side="top"] { caption-side: top; } /* =========================================================================== TABLE STATES =========================================================================== */ /* hover/selected paint on the cells so they layer above any row stripe; disable hover with --ui-table-row-background--hover: transparent */ .ui-table tbody tr:hover > :is(th, td) { background-color: var(--ui-table-row-background--hover); } .ui-table tbody tr[data-state="selected"] > :is(th, td) { background-color: var(--ui-table-row-background--selected); } /* =========================================================================== TABLE VARIANTS =========================================================================== */ .ui-table[data-variant="alternating"] { --ui-table-row-border: none; } .ui-table[data-variant="alternating"] tbody tr:nth-child(even) { background-color: var(--ui-table-alternating-background); } .ui-table[data-variant="grid"] { --ui-table-column-border: var(--ui-table-border); border: var(--ui-table-border); } /* =========================================================================== TABLE SIZES =========================================================================== */ .ui-table[data-size="sm"] { --ui-table-cell-padding-block: var(--step-1); --ui-table-cell-padding-inline: var(--step-1_5); --ui-table-head-height: var(--step-8); }}Grid format
Add data-variant="grid" for borders around all cells:
Loading components…
<table class="ui-table" data-variant="grid"> <thead> <tr> <th>Plan</th> <th>Seats</th> <th>Storage</th> <th class="text-right">Price</th> </tr> </thead> <tbody> <tr> <td>Starter</td> <td>3</td> <td>10 GB</td> <td class="text-right">$9/mo</td> </tr> <tr> <td>Team</td> <td>10</td> <td>100 GB</td> <td class="text-right">$29/mo</td> </tr> <tr> <td>Business</td> <td>Unlimited</td> <td>1 TB</td> <td class="text-right">$79/mo</td> </tr> </tbody></table>/** * table.css — Table (.ui-table) * * @layer variables, components * @requires layers.css, _variables.css, _reset.css * @uses oklch(from …) — translucent hover/footer tints * @tokens --ui-table-* (@layer variables) */@layer variables { :root { --ui-table-font-family: var(--font-family-body); --ui-table-font-size: var(--font-size-sm); --ui-table-foreground: var(--foreground); --ui-table-background: transparent; /* structural borders — the outer/footer rule, the header separator, the tbody row dividers, and the column dividers (grid variant) */ --ui-table-border: 1px solid var(--border); --ui-table-head-border: var(--ui-table-border); --ui-table-row-border: var(--ui-table-border); --ui-table-column-border: none; --ui-table-cell-padding-block: var(--step-2); --ui-table-cell-padding-inline: var(--step-2); --ui-table-head-height: var(--step-10); --ui-table-head-foreground: var(--foreground); --ui-table-head-font-weight: var(--font-weight-strong); --ui-table-foot-background: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-foot-font-weight: var(--font-weight-strong); --ui-table-row-background--hover: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-row-background--selected: var(--muted); --ui-table-alternating-background: var(--muted); --ui-table-caption-foreground: var(--muted-foreground); --ui-table-caption-padding-block: var(--gap-xs); --ui-table-caption-padding-inline: var(--gap-xs); }}@layer zazz.components { /* =========================================================================== TABLE =========================================================================== */ .ui-table { font-family: var(--ui-table-font-family); font-size: var(--ui-table-font-size); font-variant-numeric: tabular-nums; inline-size: 100%; color: var(--ui-table-foreground); background-color: var(--ui-table-background); caption-side: bottom; text-align: start; } .ui-table :is(th, td) { padding-block: var(--ui-table-cell-padding-block); padding-inline: var(--ui-table-cell-padding-inline); vertical-align: middle; text-align: start; border-inline: var(--ui-table-column-border); } .ui-table th { color: var(--ui-table-head-foreground); font-weight: var(--ui-table-head-font-weight); } .ui-table thead th { block-size: var(--ui-table-head-height); padding-block: 0; } .ui-table thead tr { border-block-end: var(--ui-table-head-border); } .ui-table tbody tr:not(:last-child) { border-block-end: var(--ui-table-row-border); } .ui-table tfoot { background-color: var(--ui-table-foot-background); font-weight: var(--ui-table-foot-font-weight); border-block-start: var(--ui-table-border); } .ui-table caption { color: var(--ui-table-caption-foreground); padding-block: var(--ui-table-caption-padding-block); padding-inline: var(--ui-table-caption-padding-inline); } /* =========================================================================== TABLE LAYOUT & CAPTION SIDE =========================================================================== */ /* omit = table-layout: auto */ .ui-table[data-layout="fixed"] { table-layout: fixed; } /* omit = caption-side: bottom (set on .ui-table) */ .ui-table caption[data-side="top"] { caption-side: top; } /* =========================================================================== TABLE STATES =========================================================================== */ /* hover/selected paint on the cells so they layer above any row stripe; disable hover with --ui-table-row-background--hover: transparent */ .ui-table tbody tr:hover > :is(th, td) { background-color: var(--ui-table-row-background--hover); } .ui-table tbody tr[data-state="selected"] > :is(th, td) { background-color: var(--ui-table-row-background--selected); } /* =========================================================================== TABLE VARIANTS =========================================================================== */ .ui-table[data-variant="alternating"] { --ui-table-row-border: none; } .ui-table[data-variant="alternating"] tbody tr:nth-child(even) { background-color: var(--ui-table-alternating-background); } .ui-table[data-variant="grid"] { --ui-table-column-border: var(--ui-table-border); border: var(--ui-table-border); } /* =========================================================================== TABLE SIZES =========================================================================== */ .ui-table[data-size="sm"] { --ui-table-cell-padding-block: var(--step-1); --ui-table-cell-padding-inline: var(--step-1_5); --ui-table-head-height: var(--step-8); }}Compact size
Add data-size="sm" to reduce cell padding:
Loading components…
<table class="ui-table" data-size="sm"> <thead> <tr> <th>Property</th> <th>Type</th> <th>Default</th> </tr> </thead> <tbody> <tr> <td>data-variant</td> <td>string</td> <td>—</td> </tr> <tr> <td>data-size</td> <td>string</td> <td>—</td> </tr> <tr> <td>data-state</td> <td>string</td> <td>—</td> </tr> </tbody></table>/** * table.css — Table (.ui-table) * * @layer variables, components * @requires layers.css, _variables.css, _reset.css * @uses oklch(from …) — translucent hover/footer tints * @tokens --ui-table-* (@layer variables) */@layer variables { :root { --ui-table-font-family: var(--font-family-body); --ui-table-font-size: var(--font-size-sm); --ui-table-foreground: var(--foreground); --ui-table-background: transparent; /* structural borders — the outer/footer rule, the header separator, the tbody row dividers, and the column dividers (grid variant) */ --ui-table-border: 1px solid var(--border); --ui-table-head-border: var(--ui-table-border); --ui-table-row-border: var(--ui-table-border); --ui-table-column-border: none; --ui-table-cell-padding-block: var(--step-2); --ui-table-cell-padding-inline: var(--step-2); --ui-table-head-height: var(--step-10); --ui-table-head-foreground: var(--foreground); --ui-table-head-font-weight: var(--font-weight-strong); --ui-table-foot-background: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-foot-font-weight: var(--font-weight-strong); --ui-table-row-background--hover: oklch(from var(--muted) l c h / calc(alpha / 2)); --ui-table-row-background--selected: var(--muted); --ui-table-alternating-background: var(--muted); --ui-table-caption-foreground: var(--muted-foreground); --ui-table-caption-padding-block: var(--gap-xs); --ui-table-caption-padding-inline: var(--gap-xs); }}@layer zazz.components { /* =========================================================================== TABLE =========================================================================== */ .ui-table { font-family: var(--ui-table-font-family); font-size: var(--ui-table-font-size); font-variant-numeric: tabular-nums; inline-size: 100%; color: var(--ui-table-foreground); background-color: var(--ui-table-background); caption-side: bottom; text-align: start; } .ui-table :is(th, td) { padding-block: var(--ui-table-cell-padding-block); padding-inline: var(--ui-table-cell-padding-inline); vertical-align: middle; text-align: start; border-inline: var(--ui-table-column-border); } .ui-table th { color: var(--ui-table-head-foreground); font-weight: var(--ui-table-head-font-weight); } .ui-table thead th { block-size: var(--ui-table-head-height); padding-block: 0; } .ui-table thead tr { border-block-end: var(--ui-table-head-border); } .ui-table tbody tr:not(:last-child) { border-block-end: var(--ui-table-row-border); } .ui-table tfoot { background-color: var(--ui-table-foot-background); font-weight: var(--ui-table-foot-font-weight); border-block-start: var(--ui-table-border); } .ui-table caption { color: var(--ui-table-caption-foreground); padding-block: var(--ui-table-caption-padding-block); padding-inline: var(--ui-table-caption-padding-inline); } /* =========================================================================== TABLE LAYOUT & CAPTION SIDE =========================================================================== */ /* omit = table-layout: auto */ .ui-table[data-layout="fixed"] { table-layout: fixed; } /* omit = caption-side: bottom (set on .ui-table) */ .ui-table caption[data-side="top"] { caption-side: top; } /* =========================================================================== TABLE STATES =========================================================================== */ /* hover/selected paint on the cells so they layer above any row stripe; disable hover with --ui-table-row-background--hover: transparent */ .ui-table tbody tr:hover > :is(th, td) { background-color: var(--ui-table-row-background--hover); } .ui-table tbody tr[data-state="selected"] > :is(th, td) { background-color: var(--ui-table-row-background--selected); } /* =========================================================================== TABLE VARIANTS =========================================================================== */ .ui-table[data-variant="alternating"] { --ui-table-row-border: none; } .ui-table[data-variant="alternating"] tbody tr:nth-child(even) { background-color: var(--ui-table-alternating-background); } .ui-table[data-variant="grid"] { --ui-table-column-border: var(--ui-table-border); border: var(--ui-table-border); } /* =========================================================================== TABLE SIZES =========================================================================== */ .ui-table[data-size="sm"] { --ui-table-cell-padding-block: var(--step-1); --ui-table-cell-padding-inline: var(--step-1_5); --ui-table-head-height: var(--step-8); }}API
| Attribute | Values |
|---|---|
data-variant | alternating, grid (omit = default) |
data-size | sm |
data-layout | fixed (omit = table-layout: auto) |
data-state | selected (on a <tr>) |
data-side | top (on the <caption>; omit = bottom) |
Override styling tokens globally on :root or on an instance (--ui-table-row-background--hover, --ui-table-alternating-background).