Theming and icons
Themes are immutable per-grid values. Semantic parameters fan out to related
CSS variables, while a typed vars escape hatch handles precise customization.
- React
- Angular
- Core TypeScript
const theme = themeDark.withParams({
accentColor: "#2fd2e2",
backgroundColor: "#0a172b",
headerBackgroundColor: "#0f2140",
rowHeight: 40,
spacing: 10,
});
<Grid
rowData={rows}
columnDefs={columns}
theme={theme}
zebraRows
columnHover
highlightActiveCell
/>
theme = themeDark.withParams({
accentColor: "#2fd2e2",
backgroundColor: "#0a172b",
headerBackgroundColor: "#0f2140",
rowHeight: 40,
spacing: 10,
});
<awb-grid
[rowData]="rows"
[columnDefs]="columns"
[theme]="theme"
[zebraRows]="true"
[columnHover]="true"
[highlightActiveCell]="true"
/>
const theme = themeDark.withParams({
accentColor: "#2fd2e2",
backgroundColor: "#0a172b",
headerBackgroundColor: "#0f2140",
rowHeight: 40,
spacing: 10,
});
const core = new GridCore(measurer, {
columnDefs,
theme,
zebraRows: true,
columnHover: true,
highlightActiveCell: true,
});
Semantic parameters
Start with themeLight or themeDark, then call withParams(). Common
parameters cover accent, surfaces, text, borders, hover/selection colors,
typography, row height, padding, scrollbars, aggregates, and sparklines.
import { themeLight } from "@agility-workbench/grid";
const theme = themeLight.withParams({
accentColor: "#7c3aed",
backgroundColor: "#ffffff",
headerBackgroundColor: "#f5f3ff",
rowHeight: 42,
spacing: 10,
fontFamily: "Inter, sans-serif",
});
const options = { theme } satisfies GridOptions;
rowHeight and headerHeight are the two layout params: rows and header
levels are sized in JavaScript, so the grid reads the theme's values when it is
created and uses them as the row height and the header-level height. They take
pixels (42 or "42px"); an explicit rowHeight / headerHeight grid option
wins over them (and leafHeaderHeight / parentHeaderHeight fall back to the
resolved header height). Swapping the theme on a mounted grid resizes neither
the rows nor the header.
One parameter derives rather than fans out: findMatchColor turns a single
color into the quick filter's three find-highlight variables — the tint on every
matching cell (35% alpha), the stronger tint on the
active match (65%), and that match's
outline (the color as given).
const theme = themeLight.withParams({ findMatchColor: "#38bdf8" });
Because it reads the color's channels, it accepts hex or rgb() / rgba()
only. A named color or another color space is ignored with a console warning
rather than guessed at — set the three variables through vars for those, or to
choose the alphas yourself.
Themes apply inline to one grid and its portaled popups, so two grids on one
page can look different. vars overrides semantic fan-out for any individual
--pte-* variable:
const theme = themeDark.withParams({
accentColor: "#22d3ee",
vars: {
"--pte-scrollbar-thumb-color": "#475569",
"--pte-selected-bg-color": "#164e63",
},
});
Icons
Icons accept a URL, data URI, CSS image value, or inline SVG. Theme icons form
the base; the grid-level icons map wins for matching names.
const options = {
icons: {
filter: "<svg viewBox='0 0 24 24' aria-hidden='true'>…</svg>",
export: "/icons/download.svg",
},
} satisfies GridOptions;
Stylesheet delivery and CSP
The base stylesheet is injected automatically — the first attached grid injects it once per document or shadow root. Under a strict Content Security Policy, supply the page's nonce:
const options = { styleNonce: cspNonce } satisfies GridOptions;
// Or inject explicitly, e.g. into a shadow root:
import { areGridStylesInjected, injectGridStyles } from "@agility-workbench/grid";
injectGridStyles(document, { nonce: cspNonce });
Popups outside the grid root
Menus, the column panel and default (in-root) tooltips and ActionFrame
popovers live inside the grid root, so the theme reaches them by ordinary
inheritance. A few layers must leave the root: the column drag ghost (it
follows the pointer across the page), and tooltips and ActionFrame popovers
with escapeRootClip: true. Those are styled exactly as if they were inside
the root:
- Theme. The grid's inline
--pte-*variables — fromthemeDark,createTheme({...})params,withParams, rawvarsand icon overrides alike — are mirrored onto the portaled element while it is mounted, and kept in sync when the theme changes. - Shadow DOM. A grid mounted inside a shadow root has its stylesheet
delivered into that shadow root, not the page. Its portaled layers therefore
mount in the same shadow root rather than
document.body, so they are styled and nothing grid-related leaks into the page.
One trade-off under a shadow root: the layer is position: fixed inside the
shadow tree, so it escapes every overflow clip (the grid root's and the
host's included) but not a transform, filter or contain on the shadow
host or one of its ancestors — those make the ancestor the containing block —
and its z-index stacks within the host's stacking context. Avoid transforms
on the host if you rely on escapeRootClip.
Import @agility-workbench/grid/styles.css and set suppressStyleInjection
only when your build pipeline needs to own CSS delivery — opting out avoids two
copies of the stylesheet competing in the cascade:
import "@agility-workbench/grid/styles.css";
const options = { suppressStyleInjection: true } satisfies GridOptions;