developer guide
Grid Lines, Corners and Cell Alignment
Grid lines can run both ways, one way, or not at all, rows can be striped, and the frame can be rounded to match the panel it sits in. Where a row is taller than its text, a cell can align to the top, the middle or the bottom of it.
Developer guide › Theming and density › Grid Lines, Corners and Cell Alignment
Grid lines and corners
Two settings that change how the grid is drawn rather than what it draws.
Rules between cells, and rounded corners
createGrid(element, {
columns, rows,
gridLines: 'both', // 'horizontal' (default), 'vertical', 'both', 'none'
cornerRadius: true, // or a number of pixels, or a CSS length
});
gridLines chooses which rules are drawn between cells. 'horizontal'
is the default and is what the grid has always drawn; vertical rules between body cells are
additive, so the default is unchanged and nothing moves on upgrade. 'rows' and
'columns' are accepted as aliases. Only the rules between data are affected: the
header underline and the seams beside pinned columns are structure rather than decoration, and
removing them would make the pinned regions look detached.
cornerRadius rounds the grid's outer corners: true adopts the theme's
own radius, a number is a count of pixels, and a string is used as written, so
'0 0 8px 8px' rounds only the bottom. The body is clipped to match, so a row
scrolling past a rounded corner is cut by it rather than squaring it off.
Zebra striping (opt-in)
createGrid(element, {
columns, rows,
stripedRows: true, // shade alternate data rows; off by default
});
stripedRows shades every other data row. It is strictly opt-in and off by default,
so a grid that never mentions it looks exactly as it did on upgrade. Parity is decided by each
row's logical index rather than its position in the DOM: rows are virtualised and
recycled, so a :nth-child rule would repaint the stripe onto whichever row landed
in an odd slot after a scroll, and a logical-index stripe keeps a row shaded consistently across
a scroll and across the left-pinned, centre and right-pinned segments of the same row. Group
headings, group footers and the grand total are structure rather than data, so they are never
striped. The stripe uses the theme's --lattice-surface-alt token, which every
palette defines, so dark, high-contrast and terminal are correct without any extra rule, and
both selection and hover still win over it.
Vertical alignment
verticalAlign is the vertical counterpart to the per-column align:
where align places cell content across the column (start,
center, end), verticalAlign places it down the row - 'top', 'middle' or 'bottom'. It is a grid-level default
with a per-column override: set it on the grid to align every column, and a column's own
verticalAlign (or cell.verticalAlign) wins for that column alone.
center and centre are accepted as synonyms for middle,
the same leniency align gives the horizontal names.
Omitted, the grid keeps the placement it has always had - content centred in a
fixed-height row and top-aligned in an autoHeight row - so a grid that never
mentions it is unchanged on upgrade. Where it earns its keep is a tall or autoHeight
grid: a wrapped-text column can sit at the top while its single-line neighbours are
middle, rather than every value floating in the middle of a tall row. The value
beats the auto-height rule, so a column asked to sit middle does so even when the
row is stretched to fit a wrapped sibling.
A grid default, with one column overriding it
const { createHeadlessGrid } = await import('../packages/core/src/index.js');
const grid = createHeadlessGrid({
verticalAlign: 'middle', // the default for every column
columns: [
{ field: 'name' }, // follows the default: middle
{ field: 'note', verticalAlign: 'bottom' }, // this column overrides it
],
rows: [{ name: 'a', note: 'b' }],
rowKey: 'name',
});
// The resolved value the renderer reads for each column.
const a = grid.columns.get('name').verticalAlign;
const b = grid.columns.get('note').verticalAlign;
grid.destroy();
return `${a}, ${b}`;