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: eight 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 handle you get from onGridReady or useGridApi(). There's no separate theming object to construct.

Built-in themes

dark

Default. High-contrast dark surface tuned for long sessions.

light

Neutral light surface for bright environments.

light-hc

High-contrast light theme for accessibility.

dark-hc

High-contrast dark theme for accessibility.

cool-blue

Modern tech aesthetic with a blue accent.

warm-orange

Warm accent theme.

minimal-monochrome

Greyscale theme with no accent color.

spreadsheet

Excel/Google Sheets-style daylight theme.

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.switchTheme('dark');
api.switchTheme('cool-blue');

const theme = api.getTheme();

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

Composing a custom theme

ThemeTokens is 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',
};

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

<Grid initialState={{ themeOverrides: acmeTheme }} />
api.setTheme(acmeTheme);

Incremental tweaks

For small changes on top of whatever theme is currently active, use api.mergeTheme({ focusRing: '#10b981' }) instead of setTheme — it merges into the active theme rather than replacing it.

System color scheme detection

ThemeManager.detectSystemPreference() is a standalone static helper — use it to pick the right built-in theme name up front:

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

const prefersDark = ThemeManager.detectSystemPreference();

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

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.switchTheme(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