Theming

Use built-in themes, switch at runtime, or compose your own theme from CSS tokens.

Wit Grid ships a shadcn-style, CSS-variable theming system: sixteen built-in themes, runtime switching with no flash of the wrong theme, and full custom theme composition. All theme interaction goes through GridApi — the same api you get from useGrid() or onGridReady. There's no separate theming object to construct.

Built-in themes

Each theme has its own palette and typeface.

ThemeLookFont
dark (default)Neutral graphite with an indigo accentInter
lightCrisp white with a blue accentInter
glass-darkFrosted navy surfaces and a cool-blue accentInter
glass-lightBright frosted surfaces and a cobalt-blue accentInter
oceanDeep sea navy with a cyan glowPlus Jakarta Sans
fjordArctic slate with frost-blue accents (Nord)IBM Plex Sans
velvetMidnight plum with a neon-pink accentDM Sans
emberWarm charcoal lit with amberSpace Grotesk
forestDeep pine with emerald highlightsSource Sans 3
monoA terminal: greys and a green cursorJetBrains Mono
paperCream paper, ink and terracottaSource Serif 4
latteSoft lavender-grey with a violet accentLexend
blossomRosewater and raspberryNunito
spreadsheetExcel / Google Sheets: hairlines, a green cursorRoboto
dark-hcBlack, white and a bright blue focusSystem
light-hcWhite, black and a deep blue focusSystem

The fonts come from Google Fonts; add BUILT_IN_THEME_FONTS_URL to your page to load them all, or load only the ones you use. Without them each theme falls back to a system font stack.

import { BUILT_IN_THEME_FONTS_URL } from '@witbybit/wit-grid-react';

<link rel='stylesheet' href={BUILT_IN_THEME_FONTS_URL} />;

Setting a theme

Set a built-in theme name declaratively via initialState. It's resolved once, atomically, before the grid's first paint — no flash of the default theme.

<Grid initialState={{ themeName: 'light' }} />

Switching themes at runtime

Get api from onGridReady (React) or the return value of createClientGrid / createInfiniteGrid / createServerPageGrid (core), then call the theme methods directly on it:

api.setState({ themeName: 'dark' });
api.setState({ themeName: 'paper' });

const theme = api.getStatus().theme;

const unsubscribe = api.watch(
	(grid) => grid.status.theme,
	(theme) => {
		console.log('Theme changed:', theme);
	}
);

Composing a custom theme

The quickest way is a palette: createTheme turns a dozen colours and a font into every token (selection, popovers, group rows, skeletons, pinned-column shadows), and picks dark or light text for the accent so labels on it stay readable.

import { createTheme } from '@witbybit/wit-grid-core';

export const acmeTheme = createTheme({
	appearance: 'dark',
	font: "'Inter', system-ui, sans-serif",
	background: '#101418',
	surface: '#0b0e11',
	raised: '#171c22',
	text: '#e8edf2',
	muted: '#93a1b0',
	border: '#1f2730',
	borderStrong: '#2d3844',
	accent: '#00d4ff',
	hover: '#161c22',
	error: '#ff6b6b',
	radius: '10px',
});

ThemeTokens is also a plain flat object — a themed variant of a built-in theme is just a spread, no helper function required:

import { getBuiltInTheme, type ThemeTokens } from '@witbybit/wit-grid-core';

export const acmeTheme: ThemeTokens = {
	...getBuiltInTheme('light'),
	bgColor: '#1a1a2e',
	textColor: '#eaeaea',
	focusRing: '#00d4ff',
	headerBg: '#0f0f1e',
	headerText: '#b0b0b0',
};

Frosted glass surfaces

Glass is an explicit theme opt-in. It gives floating menus, cell-editor popovers, tooltips, chart windows, sidebars, headers, and pinned rows a translucent tint and backdrop blur. It does not emulate refraction or distortion effects.

export const glassTheme = createTheme({
	appearance: 'dark',
	font: "'Inter', system-ui, sans-serif",
	background: '#0b1020',
	surface: '#111a30',
	raised: '#17213a',
	text: '#edf3ff',
	muted: '#a9b8d3',
	border: '#2a3855',
	borderStrong: '#465b82',
	accent: '#7c9cff',
	hover: '#18233d',
	error: '#fb7185',
	glass: {
		blur: '24px',
		saturation: '160%',
		// Optional per-surface tint overrides:
		tint: 'rgba(23, 33, 58, 0.46)',
		headerTint: 'rgba(17, 26, 48, 0.42)',
		pinnedTint: 'rgba(17, 26, 48, 0.52)',
	},
});

Use glass: true for the same treatment with palette-derived defaults, or select the ready-made glass-dark and glass-light themes. Users who request reduced transparency automatically receive solid surfaces instead.

Performance

backdrop-filter is most expensive on persistent surfaces that repaint while content moves behind them. Glass is therefore off by default. Menus and editors are low-cost; headers and pinned rows can add work during scroll. Measure scrolling on your largest supported grid and reduce blur or disable glass if it affects your frame budget.

Apply it declaratively (atomic, no flicker) or imperatively:

<Grid theme={acmeTheme} />
api.setOptions({ theme: acmeTheme });

Incremental tweaks

The theme option holds tokens over the theme the person picked (the themeName state): pass only what you change, api.setOptions({ theme: { focusRing: '#10b981' } }), and it stays over every theme they switch to.

System color scheme detection

systemColorScheme() reads the scheme the system prefers (light on a server): use it to pick the built-in theme up front:

import { systemColorScheme } from '@witbybit/wit-grid-core';

<Grid initialState={{ themeName: systemColorScheme() }} />

To keep the grid in sync as the OS preference changes live:

useEffect(() => {
	const mql = window.matchMedia('(prefers-color-scheme: dark)');
	const onChange = (e: MediaQueryListEvent) => api.setState({ themeName: e.matches ? 'dark' : 'light' });
	mql.addEventListener('change', onChange);
	return () => mql.removeEventListener('change', onChange);
}, [api]);

Bridging into host CSS

Use themeToCSSVariables when you want to expose grid theme tokens as CSS custom properties on your own stylesheet — useful for styling a toolbar or footer to match the active grid theme:

import { BUILT_IN_THEMES, getBuiltInTheme, themeToCSSVariables } from '@witbybit/wit-grid-react';

const styleVars = themeToCSSVariables(getBuiltInTheme('dark'));

See the full theming guide in the repository for the complete ThemeTokens token reference, merging semantics, and CSS variable customization.

Edit on GitHub

Last updated on

On this page