Tables and data grids

Source: src/styles/parts/tables.scs — and controls::table / controls::table-data for a table inside a shadow root.

Two kinds, and they are not the same object. A <table> is a table inside a page or a document — read, occasionally sorted. A table.data is the data grid of an application — scanned, filtered, selected, paginated. Neither uses zebra striping: hairlines separate the rows and the hover ground tracks the eye, because zebra fights the status colours a data row carries.

The document table

Invoices, Q3
ReferenceClientStatusAmount
INV-0146Hydra AgencyPaid1 250,00
INV-0147OvieproLate860,00
INV-0148Lorem SASDraft3 400,00
Total5 510,00
Framework
mmo.corethe php framework
mmo.clientthe client library
Tooling
mmo.docthe documentation generator

Variants

Rail width232px
Control height34px
Content measure1180px
TokenLightDark
$color.bg#F1F3F7#0D121B
$color.surface#FFFFFF#161C27
$color.text#161C27#F1F3F7
TemplateShape
adminrail, breadcrumb, page tabs, content, side panel, status bar
landingsticky translucent header, hero, sections, large footer
blogsticky header, content column, sticky rail, footer

The data grid

The header stays put while the body scrolls — the container is what scrolls, not the table, which is what .table-wrap is for. Rows hover, and a selected row takes the brand's soft ground, from either .selected or aria-selected="true".

Client Contact Status Amount
HAHydra AgencyParis marc@hydra-agency.com Active 1 250,00
OVOvieproLyon contact@oviepro.fr Pending 860,00
LSLorem SASLille hello@lorem.example Blocked 3 400,00
IPIpsum SARLMarseille contact@ipsum.example Trial 120,00

That header sticks on its own: table.data thead th is already position: sticky. Any other table — or a column rather than a row — asks for it with the .sticky classes, which are the same fragment (lib::sticky) an element inlines inside its own shadow root:

Reference Client Contact City Amount
INV-0146Hydra Agencymarc@hydra-agency.comParis1 250,00
INV-0147Ovieprocontact@oviepro.frLyon860,00
INV-0148Lorem SAShello@lorem.exampleLille3 400,00
INV-0149Ipsum SARLcontact@ipsum.exampleMarseille120,00
INV-0150Dolor SAhello@dolor.exampleNantes640,00
A sticky row needs a ground on its cells Under border-collapse: collapse a <thead>'s own background does not paint, so the rows would scroll straight through it. table.data paints its header cells already; a plain table's do not, which is why the specimen above puts .sticky on the cells as well as on the row.

The pieces of that grid, one by one:

IdEventWhenDuration
a8f1compile2026-09-18 14:31128 ms
a8f2render2026-09-18 14:3112 ms
a8f3flush2026-09-18 14:313 ms

What frames a grid

.table-tools above it, .table-foot under it, both hairlined against the grid; .table-wrap is the scroller the sticky header needs. In a .panel the three add up to the standard screen of an application.

ReferenceClientAmount
INV-0146Hydra Agency1 250,00
INV-0147Oviepro860,00
2 of 148
No invoice yet

Issue one from a quote, or import a batch.

Below 720px a data grid cannot stack: .table-wrap scrolls sideways in its own container instead. For a grid driven by data rather than written by hand, see <mmo-table>.