Skip to main content

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

PropTypeDefaultDescription
labelstring""Accessible label
selection-mode"none" | "cell" | "row""cell"What gets aria-selected on click/Enter — the cell or the whole row
multi-selectbooleanfalseAllow multiple cells/rows to be selected at once
heightstringCSS height (e.g. "240px", "50vh"). Applies overflow: auto for you.

Slots

SlotElementDescription
rowContainerGrid row
cellAnyGrid cell
rowheaderAnyRow header cell

Row Attributes

AttributeValuesDescription
stickybooleanPins the row to the top of a scrolling grid (position: sticky)

Cell Attributes

AttributeValuesDescription
size112Sets flex: N on the cell. Default 1.

Keyboard

KeyAction
ArrowRightNext cell
ArrowLeftPrevious cell
ArrowDownCell below
ArrowUpCell above
HomeFirst cell in row
EndLast cell in row
Ctrl+HomeFirst cell in grid
Ctrl+EndLast 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;
}
SelectorTargets
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" or role="rowheader"
  • Focusable cells with roving tabindex