Table

Data table with variants and overflow helper

Intent

Table is a semantic wrapper: .table on the <table> element, all styling on semantic descendants (thead, tbody, th, td). Variants layer on top for visual distinction -- striping, borders, density, hover -- and they compose. Helpers round the component out: .table-wrap for responsive horizontal overflow, and .as-num for right-aligned tabular numbers.

Basic usage

.table on <table>, inside a <div class="table-wrap">. The wrap is strongly recommended even for tables that fit today -- content grows, column counts shift, and the wrap gracefully hands off to horizontal scroll when a table outgrows its container.

Name Email Role
Ada Lovelace ada@example.com Admin
Grace Hopper grace@example.com Editor
Katherine Johnson katherine@example.com Viewer
<docs-resize>
    <div class="table-wrap">
        <table class="table">
            <thead>
            <tr>
                <th>Name</th>
                <th>Email</th>
                <th>Role</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>Ada Lovelace</td>
                <td>ada@example.com</td>
                <td>Admin</td>
            </tr>
            <tr>
                <td>Grace Hopper</td>
                <td>grace@example.com</td>
                <td>Editor</td>
            </tr>
            <tr>
                <td>Katherine Johnson</td>
                <td>katherine@example.com</td>
                <td>Viewer</td>
            </tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Variants

Striped

.is-striped paints alternating tbody rows with a sunken surface tone. Useful when rows carry enough content that the eye needs help tracking across wide rows.

Project Owner Status
Atlas migrationAda LovelaceIn progress
Billing refreshGrace HopperBlocked
Onboarding rewriteKatherine JohnsonIn review
SSO integrationHedy LamarrDone
<docs-resize>
    <div class="table-wrap">
        <table class="table is-striped">
            <thead>
            <tr>
                <th>Project</th>
                <th>Owner</th>
                <th>Status</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>Atlas migration</td><td>Ada Lovelace</td><td>In progress</td></tr>
            <tr><td>Billing refresh</td><td>Grace Hopper</td><td>Blocked</td></tr>
            <tr><td>Onboarding rewrite</td><td>Katherine Johnson</td><td>In review</td></tr>
            <tr><td>SSO integration</td><td>Hedy Lamarr</td><td>Done</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Bordered

.is-bordered draws borders around every cell and the outer table. Inside a .table-wrap.is-bordered the table's outer border is suppressed so it doesn't double up with the wrap's frame; in a default (unframed) wrap the table's outer border shows.

Environment Region Version
Productionus-east-13.14.2
Stagingus-east-13.15.0-rc.2
Developmentlocalmain@HEAD
<docs-resize>
    <div class="table-wrap">
        <table class="table is-bordered">
            <thead>
            <tr>
                <th>Environment</th>
                <th>Region</th>
                <th>Version</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>Production</td><td>us-east-1</td><td>3.14.2</td></tr>
            <tr><td>Staging</td><td>us-east-1</td><td>3.15.0-rc.2</td></tr>
            <tr><td>Development</td><td>local</td><td>main@HEAD</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Compact

.is-compact tightens cell padding one density tier and drops the font size one step. Pick it for dense data tables, long listings, admin views where scan speed beats breathing room.

SKU Product Warehouse In stock
W-1042Widget, smallColumbus238
W-1043Widget, mediumColumbus117
W-1044Widget, largeColumbus42
G-2201Gadget, basicReno1,204
G-2202Gadget, proReno89
<docs-resize>
    <div class="table-wrap">
        <table class="table is-compact is-striped">
            <thead>
            <tr>
                <th>SKU</th>
                <th>Product</th>
                <th>Warehouse</th>
                <th>In stock</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>W-1042</td><td>Widget, small</td><td>Columbus</td><td class="as-num">238</td></tr>
            <tr><td>W-1043</td><td>Widget, medium</td><td>Columbus</td><td class="as-num">117</td></tr>
            <tr><td>W-1044</td><td>Widget, large</td><td>Columbus</td><td class="as-num">42</td></tr>
            <tr><td>G-2201</td><td>Gadget, basic</td><td>Reno</td><td class="as-num">1,204</td></tr>
            <tr><td>G-2202</td><td>Gadget, pro</td><td>Reno</td><td class="as-num">89</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Hoverable

.is-hoverable paints tbody rows with the stripe tone on hover. Adds an affordance for rows that respond to click or carry row-level actions.

Document Last edited Shared with
Q3 roadmap14 min agoTeam
Hiring planYesterdayLeadership
Architecture RFC3 days agoEngineering
<docs-resize>
    <div class="table-wrap">
        <table class="table is-hoverable">
            <thead>
            <tr>
                <th>Document</th>
                <th>Last edited</th>
                <th>Shared with</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>Q3 roadmap</td><td>14 min ago</td><td>Team</td></tr>
            <tr><td>Hiring plan</td><td>Yesterday</td><td>Leadership</td></tr>
            <tr><td>Architecture RFC</td><td>3 days ago</td><td>Engineering</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Variants compose freely. .is-striped.is-hoverable is a common pair for long listings; .is-compact.is-bordered works for dense reference matrices.

Numeric columns

Add .as-num to a <th> or <td> to right-align the cell and enable font-variant-numeric: tabular-nums. Right-aligned numbers let the eye compare magnitudes vertically; tabular nums keep digit widths consistent so "1,000" and "9,999" line up column-for-column.

Invoice Issued Subtotal Tax Total
INV-00412 2026-03-01 $1,240.00 $99.20 $1,339.20
INV-00413 2026-03-08 $420.50 $33.64 $454.14
INV-00414 2026-03-15 $9,876.00 $790.08 $10,666.08
<docs-resize>
    <div class="table-wrap">
        <table class="table is-striped">
            <thead>
            <tr>
                <th>Invoice</th>
                <th>Issued</th>
                <th class="as-num">Subtotal</th>
                <th class="as-num">Tax</th>
                <th class="as-num">Total</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>INV-00412</td>
                <td>2026-03-01</td>
                <td class="as-num">$1,240.00</td>
                <td class="as-num">$99.20</td>
                <td class="as-num">$1,339.20</td>
            </tr>
            <tr>
                <td>INV-00413</td>
                <td>2026-03-08</td>
                <td class="as-num">$420.50</td>
                <td class="as-num">$33.64</td>
                <td class="as-num">$454.14</td>
            </tr>
            <tr>
                <td>INV-00414</td>
                <td>2026-03-15</td>
                <td class="as-num">$9,876.00</td>
                <td class="as-num">$790.08</td>
                <td class="as-num">$10,666.08</td>
            </tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Responsive overflow

When a table has more columns than fit in its container, .table-wrap clips the overflow and exposes horizontal scroll.

Order Customer Shipped Carrier Tracking Items Weight Subtotal Shipping Total
#18421Ada Lovelace 2026-03-12UPS1Z999AA10123456784 34.2 lb $120.00$12.00$132.00
#18422Grace Hopper 2026-03-12FedEx785436728944 10.8 lb $38.00$7.50$45.50
#18423Katherine Johnson 2026-03-13USPS9400111899223344556677 56.9 lb $224.00$14.00$238.00
<docs-resize>
    <div class="table-wrap">
        <table class="table is-striped is-compact">
            <thead>
            <tr>
                <th>Order</th>
                <th>Customer</th>
                <th>Shipped</th>
                <th>Carrier</th>
                <th>Tracking</th>
                <th class="as-num">Items</th>
                <th class="as-num">Weight</th>
                <th class="as-num">Subtotal</th>
                <th class="as-num">Shipping</th>
                <th class="as-num">Total</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>#18421</td><td>Ada Lovelace</td>
                <td>2026-03-12</td><td>UPS</td><td>1Z999AA10123456784</td>
                <td class="as-num">3</td><td class="as-num">4.2 lb</td>
                <td class="as-num">$120.00</td><td class="as-num">$12.00</td><td class="as-num">$132.00</td>
            </tr>
            <tr>
                <td>#18422</td><td>Grace Hopper</td>
                <td>2026-03-12</td><td>FedEx</td><td>785436728944</td>
                <td class="as-num">1</td><td class="as-num">0.8 lb</td>
                <td class="as-num">$38.00</td><td class="as-num">$7.50</td><td class="as-num">$45.50</td>
            </tr>
            <tr>
                <td>#18423</td><td>Katherine Johnson</td>
                <td>2026-03-13</td><td>USPS</td><td>9400111899223344556677</td>
                <td class="as-num">5</td><td class="as-num">6.9 lb</td>
                <td class="as-num">$224.00</td><td class="as-num">$14.00</td><td class="as-num">$238.00</td>
            </tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Bordered wrap

The wrap is scroll-only by default -- no outer frame, no rounded corners. Add .is-bordered to the wrap for a framed look. When both the wrap and the inner table carry .is-bordered, the wrap owns the outer frame and the table's outer border is suppressed so they don't double up; per-cell borders remain.

Feature Status Ship week
Command paletteIn progress14
Dark-mode auditReview15
Webhook retriesPlanned17
<docs-resize>
    <div class="table-wrap is-bordered">
        <table class="table is-striped">
            <thead>
            <tr>
                <th>Feature</th>
                <th>Status</th>
                <th class="as-num">Ship week</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>Command palette</td><td>In progress</td><td class="as-num">14</td></tr>
            <tr><td>Dark-mode audit</td><td>Review</td><td class="as-num">15</td></tr>
            <tr><td>Webhook retries</td><td>Planned</td><td class="as-num">17</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Bordered table wrap with bordered table

Feature Status Ship week
Command paletteIn progress14
Dark-mode auditReview15
Webhook retriesPlanned17
<docs-resize>
    <div class="table-wrap is-bordered">
        <table class="table is-striped is-bordered">
            <thead>
            <tr>
                <th>Feature</th>
                <th>Status</th>
                <th class="as-num">Ship week</th>
            </tr>
            </thead>
            <tbody>
            <tr><td>Command palette</td><td>In progress</td><td class="as-num">14</td></tr>
            <tr><td>Dark-mode audit</td><td>Review</td><td class="as-num">15</td></tr>
            <tr><td>Webhook retries</td><td>Planned</td><td class="as-num">17</td></tr>
            </tbody>
        </table>
    </div>
</docs-resize>

Class reference

Class Role
.table On the <table>. Sets full-width, border-collapse, base padding and typography, bordered thead rule, row separators that drop on the last row.
.is-striped Alternating tbody row background.
.is-bordered Borders on every cell and the outer table. Inside .table-wrap the outer border is suppressed so wrap and table don't double up.
.is-compact Tighter cell padding and smaller font for dense tables.
.is-hoverable Hover background on tbody rows.
.table-wrap On a <div> wrapping the <table>. Horizontal scroll container; no outer frame by default.
.table-wrap.is-bordered Adds an outer border + rounded corners to the wrap. Suppresses .table.is-bordered's outer border when both are applied so the frame doesn't double up.
.as-num On a <th> or <td>. Right-aligns content and enables tabular numbers for column-aligned digits.

Customization

Table exposes five scoped tokens on .table: --table-padding (cell padding), --table-border-color (row separators), --table-header-bg and --table-header-color (thead tone), and --table-stripe-bg (striped rows, also reused by .is-hoverable). The wrap exposes --table-wrap-border for the outer frame (consumed when .is-bordered is applied).

Full token list and defaults: src/css/table.css.

Accessibility

  • Use proper table semantics. A real <table> with <thead>, <tbody>, and <th> elements announces correctly to screen readers. Faking a table with <div> rows strips the row/column navigation commands that assistive tech relies on for tabular data.
  • Add a <caption> when the table's purpose isn't obvious from surrounding context. A caption is announced as the table's name and appears visually as a title above it. For tables referenced from prose ("The table below shows..."), the caption is often redundant; for standalone tables it's essential.
  • Use scope="col" and scope="row" on headers. Column headers default to column scope in a simple <thead><tr><th> shape, so explicit scope is optional there. For complex tables with row headers (first column holds labels), mark those <th scope="row"> so screen readers associate each row's cells with the right label.
  • Announce sort state with aria-sort. For tables with sortable columns, add aria-sort="ascending", "descending", or "none" on the header cell. Sort triggers go on a button inside the <th>, not on the <th> itself, so the click target is keyboard-accessible.
  • Overflow requires keyboard scroll. A .table-wrap that scrolls horizontally must be reachable with the keyboard. Add tabindex="0" and role="region" with an aria-label on the wrap so keyboard users can focus it and arrow-key through the scroll region.

CSS Reference: Table

Styled data table

Source: src/css/table.css

Tokens

TokenDefaultDescription
--table-paddingvar(--ui-pad-y-tight) var(--ui-pad-x-tight)Cell padding for th and td
--table-border-colorvar(--color-border)Border color for cell dividers
--table-header-bgtransparentBackground color for the header row
--table-header-colorvar(--color-text)Text color for header cells
--table-stripe-bgvar(--color-fill-subtle)Background color for striped and hovered rows

Classes

ClassDescription
.tablecomponent root
.is-stripedAlternating row background on even rows
.is-borderedBorders on all cells and the table edge
.is-compactDenser padding and smaller font size
.is-hoverableHighlight rows on hover

CSS Reference: Table Wrap

Responsive scroll wrapper

Source: src/css/table.css

Tokens

TokenDefaultDescription
--table-wrap-bordervar(--border-width-default) solid var(--color-border)Border for the outer table-wrap when .is-bordered is used

Classes

ClassDescription
.table-wrapcomponent root
.is-borderedShow the border for the table wrap