Lattice Grid Buy a licence

developer guide

Data Grid Theming with CSS Custom Properties

The grid exposes its colours, spacing and type as custom properties, so a theme is a block of values rather than a pile of overrides. Four themes ship, the dark one can follow the operating system, and your own is the same tokens with different values.

Developer guideTheming and density › Data Grid Theming with CSS Custom Properties

Styling

Cells, columns and rows can all carry classes and inline styles, static or computed.

By scope

// Cells and columns: declared on the column
{ field: 'margin', cell: {
  class: 'tabular',
  classWhen: { 'is-loss': (p) => p.value < 0 },
  style: (p) => ({ fontWeight: p.value > 1e6 ? 650 : 400 }),
}}

// Rows: on the grid
rowClass: (p) => p.data.slaBreached ? 'row-breach' : null,
rowStyle: (p) => p.data.region === 'AMER' ? { borderLeft: '3px solid #7c3aed' } : null,

All of these are re-evaluated on every repaint and remove what they added last time first. That is not caution. Rows and cells come from pools, so an element that carried a class for one row will later carry a different row, a class written once and left alone smears down the grid as the user scrolls.

Theming

The stylesheet is custom properties throughout. Override the tokens, not the rules.

A house palette

.lattice {
  --lattice-accent: #7c3aed;
  --lattice-font-size: 13px;
  --lattice-space: 8px;
  --lattice-border-color: #e6e8eb;
}

Four themes ship. With no theme set, the grid follows the viewer's prefers-color-scheme between light and dark; naming one pins it. Density is separate: compact, standard, comfortable or spacious, and combines with any of them.

Pinning a theme, at build time or at runtime

createGrid(el, { theme: 'high-contrast' });
grid.set('theme', 'terminal');
grid.set('theme', null);   // back to following the viewer
ThemeWhat it is for
lightThe default. Follows prefers-color-scheme when theme is unset.
darkThe same palette inverted, with the accent and status hues re-picked for a dark ground rather than reused.
high-contrastNot "dark with more contrast". Text is 21:1 and borders 6.1:1 against the background, where the other themes sit near 1.3:1 on borders, WCAG 1.4.11 asks for 3:1 on the boundaries a user has to find. Cell borders are drawn rather than implied, selected rows carry an outline as well as a fill, and every status pill has a solid border so it does not depend on hue alone.
terminalA phosphor console: one hue on near-black, monospaced throughout. Status is carried by brightness rather than colour, so the palette stays a palette.

Forced colours

Windows High Contrast Mode replaces the palette outright: that is the point of it, and no stylesheet should fight it. What the grid does instead is translate every piece of meaning it normally carries in a background tint into something the mode preserves.

A selected row takes the system's own selection colours. A pinned region loses its shadow, which forced colours do not render, and gains a rule in its place. Status pills, fill decorations, progress tracks and histogram bars each gain a border, because a fill with no edge is invisible once its colour is discarded. Diff states stop depending on hue altogether: added, removed and changed are told apart by border style: solid, dashed and doubled : since the mode offers no way to keep four distinct colours.

Two things deliberately keep their colour, declared with forced-color-adjust: a colour swatch, where the colour is the value being shown, and a collaborator's presence colour, which is how one person is told from another. Replacing those would destroy the meaning rather than translate it. Both gain a border so they stay visible against either ground.

Every theme is the same token set with different values, so an override you write against .lattice applies to all of them, and one written against .lattice[data-theme="terminal"] applies to that one. The attribute is on the grid's own root element, not on <html>.

The grid and your page's CSS

Every selector in the stylesheet is namespaced under .lattice, so the grid cannot restyle your page. From 1.4.0 the reverse is also true: the grid gives the elements it builds a floor for the properties a page is most likely to set on a bare tag: margin, padding, border, radius, background, shadow, text transform and letter spacing, plus type and colour on form controls, which inherit neither.

Why this is needed at all. A grid is mounted inside somebody else's stylesheet. A rule as ordinary as section { padding: 5.5rem 0 }, a marketing page, a CMS theme, a Tailwind preflight: matches by tag name, and the grid builds parts of its own interface from those tags: the tool panel's filter rows are <section> elements. Without the reset, 5.5rem of somebody else's padding lands on every one of them.

The reset uses no !important. It is specificity (0,1,1) and every rule that dresses a grid element is (0,2,0) or higher, so the grid's own styling always wins and the reset only fills a gap. Yours wins too, on the same terms: a rule aimed at a Lattice class, .lattice .lat-cell { … }: outranks it, so overriding the grid deliberately works exactly as before. Only bare-tag rules are shut out.

It touches box model and decoration only. Nothing in it sets display, position or any dimension: those belong to the renderer, and a reset that reached them would break virtualisation rather than protect it.