Grid
Interactive data grid with keyboard navigation.
Usage
<w-grid label="Data table">
<w-slot row>
<div>
<w-slot cell><div>A1</div></w-slot>
<w-slot cell><div>B1</div></w-slot>
<w-slot cell><div>C1</div></w-slot>
</div>
</w-slot>
<w-slot row>
<div>
<w-slot cell><div>A2</div></w-slot>
<w-slot cell><div>B2</div></w-slot>
<w-slot cell><div>C2</div></w-slot>
</div>
</w-slot>
</w-grid>
Sized columns
Use size="N" on a cell slot to give it a wider flex grow value. Sizes 1–12 are supported.
<w-grid label="Inventory">
<w-slot row>
<div>
<w-slot cell size="2"><div>Product</div></w-slot>
<w-slot cell><div>Category</div></w-slot>
<w-slot cell><div>Price</div></w-slot>
</div>
</w-slot>
</w-grid>
Fixed height + sticky header
Set height on the grid to make it scroll, and add sticky to a row slot to pin it to the top. Give the header row a solid background so scrolling content doesn't bleed through.
<w-grid height="240px">
<w-slot row sticky>
<div style="background: #f5f5f5;">
<w-slot cell><div>Product</div></w-slot>
<w-slot cell><div>Price</div></w-slot>
</div>
</w-slot>
<!-- many rows… -->
</w-grid>
Layout
Rows default to display: flex and cells default to flex: 1, so a bare grid lays out as a horizontal row of equal-width cells without any inline styles. <w-slot cell size="N"> sets flex: N on the cell (1–12). Defaults are applied with zero specificity (:where()), so any inline style="flex: ..." or class-based rule overrides them — useful for non-numeric values like auto or fixed widths.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "" | Accessible label |
selection-mode | "none" | "cell" | "row" | "cell" | What gets aria-selected on click/Enter — the cell or the whole row |
multi-select | boolean | false | Allow multiple cells/rows to be selected at once |
height | string | — | CSS height (e.g. "240px", "50vh"). Applies overflow: auto for you. |
Slots
| Slot | Element | Description |
|---|---|---|
row | Container | Grid row |
cell | Any | Grid cell |
rowheader | Any | Row header cell |
Row Attributes
| Attribute | Values | Description |
|---|---|---|
sticky | boolean | Pins the row to the top of a scrolling grid (position: sticky) |
Cell Attributes
| Attribute | Values | Description |
|---|---|---|
size | 1–12 | Sets flex: N on the cell. Default 1. |
Keyboard
| Key | Action |
|---|---|
ArrowRight | Next cell |
ArrowLeft | Previous cell |
ArrowDown | Cell below |
ArrowUp | Cell above |
Home | First cell in row |
End | Last cell in row |
Ctrl+Home | First cell in grid |
Ctrl+End | Last cell in grid |
Styling
Rows are flex; cells are flex: 1 (or flex: N if size="N"). State is exposed via aria-selected on cells/rows. The keyboard-focused cell has tabindex="0".
/* Rows and cells */
w-grid w-slot[row] > * {
border-bottom: 1px solid #eee;
}
w-grid w-slot[cell] > * {
padding: 0.75rem 1rem;
}
/* Sticky header row */
w-grid w-slot[row][sticky] > * {
background: #f5f5f5;
font-weight: 600;
}
/* Selected cell or row */
w-grid w-slot[cell] > *[aria-selected="true"] {
background: #dbeafe;
}
w-grid w-slot[row] > *[aria-selected="true"] {
background: #eef2ff;
}
/* Currently focused cell (keyboard nav) */
w-grid w-slot[cell] > *[tabindex="0"] {
outline: 2px solid #6366f1;
outline-offset: -2px;
}
/* Focused cell — sticky row only */
w-grid w-slot[row][sticky] w-slot[cell] > *[tabindex="0"] {
outline: 2px solid #6366f1;
outline-offset: -2px;
}
/* Focused cell — non-sticky rows only */
w-grid w-slot[row]:not([sticky]) w-slot[cell] > *[tabindex="0"] {
outline: 2px solid #6366f1;
outline-offset: -2px;
}
| Selector | Targets |
|---|---|
w-grid w-slot[cell] > *[aria-selected="true"] | Selected cell |
w-grid w-slot[row] > *[aria-selected="true"] | Selected row |
w-grid w-slot[row][sticky] > * | Sticky header row |
w-grid w-slot[cell] > *[tabindex="0"] | Focused cell (any row) |
w-grid w-slot[row][sticky] w-slot[cell] > *[tabindex="0"] | Focused cell in sticky row |
w-grid w-slot[row]:not([sticky]) w-slot[cell] > *[tabindex="0"] | Focused cell in non-sticky row |
w-grid[height] | Scrolling grid (overflow auto) |
w-grid[selectionmode="row"] | Row-selection mode |
Accessibility
- Container has
role="grid" - Rows have
role="row" - Cells have
role="gridcell"orrole="rowheader" - Focusable cells with roving tabindex