useTheme
useTheme returns a runtime snapshot of the active theme — the resolved CSS variable values, the detected variant, and helpers for reading individual tokens by path. Reach for it when a feature genuinely cannot use compile-time Vanilla Extract tokens: canvas drawing, third-party libraries that take colours as plain strings, or branching logic that depends on whether the user is in dark or light mode.
For ordinary styling, keep using vars.* from @/theme — it’s zero-runtime and tree-shakeable. useTheme exists for the cases where Vanilla Extract isn’t an option.
Live preview
Import
import { useTheme } from 'entangle-ui';Signature
function useTheme(): UseThemeReturn;
type ThemeVariant = 'dark' | 'light' | 'custom';
interface UseThemeReturn { variant: ThemeVariant; values: ThemeValues; getToken: (path: string) => string; getVar: (path: string) => string;}Usage
function AccentSwatch() { const { values } = useTheme(); return ( <div style={{ width: 24, height: 24, background: values.colors.accent.primary, }} /> );}Common patterns
Canvas drawing with theme-aware colours
Vanilla Extract can’t reach into a <canvas> — the values from useTheme can. Pull the resolved CSS-var values out of values and use them directly in your draw loop.
Canvas drawing
const ref = useRef<HTMLCanvasElement>(null);const { values } = useTheme();
useEffect(() => { const ctx = ref.current?.getContext('2d'); if (!ctx) return; ctx.fillStyle = values.colors.accent.primary; ctx.fillRect(0, 0, 100, 100);}, [values]);Conditional logic by variant
When you need to branch on the active theme — preloading dark- or light-tuned image assets, picking a Mapbox basemap style, choosing a Three.js material — read variant directly.
Conditional logic
const { variant } = useTheme();
if (variant === 'light') { loadLightAssets();} else { loadDarkAssets();}getToken — resolve a single token by path
getToken walks the theme contract, reads the underlying CSS custom property, and returns the resolved value as a plain string. Repeated calls for the same path are cached.
getToken
const { getToken } = useTheme();
const accent = getToken('colors.accent.primary'); // → '#007acc'const padding = getToken('spacing.md'); // → '8px'const menuBar = getToken('shell.menuBar.height'); // → '28px'getVar — var() references for inline styles
getVar returns the var(--etui-...) reference string. Unlike getToken, the result is SSR-safe (it doesn’t read the DOM) and the browser re-resolves the colour automatically if the surrounding theme class changes.
getVar
const { getVar } = useTheme();
<div style={{ color: getVar('colors.text.primary'), background: getVar('colors.surface.default'), }}/>;Caveats
- Root-only scope. Both
variantandvaluescome fromdocument.documentElement— the global root theme. A light-themed subtree wrapped inVanillaThemeProvider(e.g. one light dialog inside a dark app) is not reflected:useThemecalled inside that subtree still reports the root variant and root values. For elements inside such a subtree, style with Vanilla Extractvars.*so the browser resolves them per scope. - One-shot read on mount. v0.8 reads the CSS variables once via
useLayoutEffectand does not subscribe to subsequent class changes. Toggling the root theme at runtime is not reflected — remount the consuming subtree to pick up the new theme. AMutationObserver-based subscription may be added in a later release if consumer demand emerges. - SSR fallback is the dark theme. Without a DOM,
valuesmirrorsdarkThemeValues,getTokenreturns the dark-theme defaults, andgetVarproduces the correctvar(--etui-...)string (which is itself SSR-safe). - Variant detection is root-only.
'light'is reported only when thelightThemeClassproduced bycreateLightTheme()is applied todocument.documentElement— the same element the hook reads values from, so the two never disagree.'custom'is reserved for future explicit detection; themes generated viacreateCustomThemecurrently report as'dark'. - Prefer
vars.*for normal styling.useThemeexists for runtime reads that genuinely need plain strings (canvas, third-party libs, branching logic). Anywhere a Vanilla Extract token works, that’s the right tool — it has no runtime cost and no re-render.
API
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'dark' | 'light' | 'custom' | — | Detected theme variant. `'dark'` is the default; `'light'` is reported when the light-theme class is present in the document. |
values | ThemeValues | — | Resolved theme values — the same shape as `darkThemeValues`/`lightThemeValues`, with each leaf replaced by the current CSS variable value. |
getToken | (path: string) => string | — | Resolve a single token by path (e.g. `'colors.accent.primary'`). Returns an empty string for unknown paths. Cached per path for the life of the hook instance. |
getVar | (path: string) => string | — | Get the `var(--etui-...)` reference for a token. SSR-safe and ideal for inline styles that should follow theme changes automatically. |