API reference

theme

Every export of @kitr/theme, @kitr/theme/provider, @kitr/theme/server, read from the published type definitions. The guide shows them in use.

@kitr/theme

Re-exports class-variance-authority, clsx unchanged, plus the following.

dataAttrs

function dataAttrs(values: Record<string, string | number | boolean | null | undefined>): Record<`data-${string}`, string>;

Data-attribute variants for vanilla CSS: <div {...dataAttrs({ state, size })}> → data-state="open".

defineTokens

function defineTokens<const T extends TokenGroup>(tokens: T, options?: {
  prefix?: string;
}): DefineTokensResult<T>;

Turn a nested token object into CSS custom properties with typed names. The prefix defaults to kitr, so color.primary becomes --kitr-color-primary.

pick

function pick<K extends string>(styles: CssModule<K>, ...parts: Array<K | Partial<Record<K, boolean | undefined>> | false | null | undefined>): string;

Pick classes from a module with optional conditions: pick(styles, 'card', { active }).

themeFromCookie

function themeFromCookie(cookieHeader: string | null | undefined, storageKey?: string): ResolvedTheme | undefined;

Read the theme cookie on the server to pass as initialResolved.

ThemeProvider

function ThemeProvider({ children, defaultTheme, storageKey, attribute, initialResolved, colors }: ThemeProviderProps): import("react").JSX.Element;

Light/dark/system with no flash: pair with <ThemeScript /> in the document head. Sets data-theme="light|dark" on , which the Tailwind preset and styles.css read.

ThemeScript

function ThemeScript(props: {
  storageKey?: string;
  attribute?: string;
  tokens?: ThemeConfig['tokens'];
  nonce?: string;
}): import("react").JSX.Element;

Inline script for the document head; applies the stored theme before first paint. Its storageKey, attribute and tokens default to config/theme.ts (through the theme provider), so the head and the provider always agree. tokens are emitted as a <style> of --kitr-* overrides: light on :root, dark under the dark attribute.

tokenCss

function tokenCss(tokens: {
  light?: TokenGroup;
  dark?: TokenGroup;
}, attribute?: string): string;

The CSS for config/theme.ts tokens. :root:root outranks @kitr/theme/styles.css (:root, [data-theme="dark"]) wherever the stylesheet loads. < is escaped so a value cannot close the tag.

useTheme

function useTheme(): ThemeContextValue;

The current theme and the functions that change it: { theme, resolved, setTheme, toggle }. Throws when no ThemeProvider (or the theme provider in kitrrc.ts) is above the component.

vars

function vars(values: Record<`--${string}`, string | number>): Record<string, string>;

Build a style object from CSS variables: vars({ '--gap': '8px' }).

darkTokens

const darkTokens: DefineTokensResult<{
  readonly color: {
    readonly background: "oklch(16% 0.01 260)";
    readonly foreground: "oklch(95% 0.005 260)";
    readonly muted: "oklch(24% 0.01 260)";
    readonly mutedForeground: "oklch(70% 0.02 260)";
    readonly border: "oklch(30% 0.01 260)";
    readonly primary: "oklch(70% 0.17 262)";
    readonly primaryForeground: "oklch(16% 0.01 260)";
    readonly destructive: "oklch(65% 0.2 27)";
    readonly destructiveForeground: "oklch(16% 0 0)";
    readonly ring: "oklch(70% 0.17 262)";
  };
}>;

kitr's dark colour tokens, the values @kitr/theme/styles.css sets under [data-theme="dark"]. Radius, font and shadow are shared with the light set.

defaultTokens

const defaultTokens: DefineTokensResult<{
  readonly color: {
    readonly background: "oklch(99% 0 0)";
    readonly foreground: "oklch(20% 0.01 260)";
    readonly muted: "oklch(96% 0.005 260)";
    readonly mutedForeground: "oklch(50% 0.02 260)";
    readonly border: "oklch(90% 0.01 260)";
    readonly primary: "oklch(55% 0.2 262)";
    readonly primaryForeground: "oklch(99% 0 0)";
    readonly destructive: "oklch(55% 0.22 27)";
    readonly destructiveForeground: "oklch(99% 0 0)";
    readonly ring: "oklch(55% 0.2 262)";
  };
  readonly radius: {
    readonly sm: "0.25rem";
    readonly md: "0.5rem";
    readonly lg: "0.75rem";
  };
  readonly font: {
    readonly sans: "ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif";
    readonly mono: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
  };
  readonly shadow: {
    readonly sm: "0 1px 2px oklch(0% 0 0 / 0.06)";
    readonly md: "0 4px 12px oklch(0% 0 0 / 0.08)";
  };
}>;

kitr's light (default) tokens, the values @kitr/theme/styles.css sets on :root. Override them in app/styles/tokens.css.

defineConfig

const defineConfig: <const C extends ThemeConfig>(config: C) => C;

Type-check config/theme.ts: returns the object unchanged, with autocomplete for every ThemeConfig option.

ThemeConfigContext

const ThemeConfigContext: import("react").Context<ThemeConfig | null>;

config/theme.ts, provided by the theme provider so <ThemeScript /> uses the same settings.

DefineTokensResult

interface DefineTokensResult<T extends TokenGroup> {
  /** The token tree as passed in. */
  tokens: T;
  /** A `var()` reference for a path: `var('color.primary')` → `var(--kitr-color-primary)`. */
  var: (path: TokenPath<T>) => `var(--${string})`;
  /** The CSS variable name for a path: `name('color.primary')` → `--kitr-color-primary`. */
  name: (path: TokenPath<T>) => `--${string}`;
  /** `:root { --kitr-...: ...; }` ready to drop into a stylesheet. */
  css: (selector?: string) => string;
  /** Flat `{ '--kitr-color-primary': '#...' }` for inline styles or runtime theming. */
  flat: () => Record<string, string>;
}

What defineTokens returns: the tree itself plus helpers that name, reference and emit its CSS variables.

ThemeConfig

interface ThemeConfig {
  /** 'light' | 'dark' | 'system'. Default 'system'. */
  default?: 'light' | 'dark' | 'system';
  /** Storage key (localStorage and the cookie the server reads). Default 'kitr-theme'. */
  storageKey?: string;
  /** Attribute set on <html>. Default 'data-theme'. */
  attribute?: string;
  /**
   * Token overrides, emitted by `<ThemeScript />` as `--kitr-*` variables: `light` on `:root`,
   * `dark` under the dark attribute. `{ light: { color: { primary: 'oklch(60% 0.2 150)' } } }`.
   */
  tokens?: {
    light?: TokenGroup;
    dark?: TokenGroup;
  };
  /** `<meta name="theme-color">` and native window chrome per resolved theme. */
  colors?: {
    light?: string;
    dark?: string;
  };
  /** Where `kitr ui add` pulls components from. Any shadcn-compatible registry. */
  registry?: string;
  /** Where components are written. Default `app/components/ui`. */
  componentsDir?: string;
}

The options of config/theme.ts: the default theme, where it is stored, window colours and the component registry.

ThemeProviderProps

interface ThemeProviderProps {
  /** The tree that can call `useTheme()`. */
  children: ReactNode;
  /** The theme before the user picks one. Default `'system'`. */
  defaultTheme?: Theme;
  /** The `localStorage` key and cookie name holding the choice. Default `'kitr-theme'`. */
  storageKey?: string;
  /** The attribute set on `<html>` to `light` or `dark`. Default `'data-theme'`. */
  attribute?: string;
  /** Theme known on the server (e.g. from a cookie) to avoid a flash. */
  initialResolved?: ResolvedTheme;
  /** `<meta name="theme-color">` per resolved theme, which native shells and PWAs show as window chrome. */
  colors?: {
    light?: string;
    dark?: string;
  };
}

Props of ThemeProvider. The theme provider in kitrrc.ts fills them from config/theme.ts.

TokenGroup

interface TokenGroup {
  [key: string]: TokenLeaf | TokenGroup;
}

A nested object of tokens, such as { color: { primary: '#0ea5e9' } }. Each leaf becomes one CSS variable.

CssModule

/** Type a CSS Modules import so `styles.foo` is checked. Generate the `.d.ts` with `kitr css:types`. */
type CssModule<K extends string> = {
  readonly [P in K]: string;
};

Type a CSS Modules import so styles.foo is checked. Generate the .d.ts with kitr css:types.

ResolvedTheme

type ResolvedTheme = 'light' | 'dark';

The theme on screen once 'system' is resolved: always 'light' or 'dark'.

Theme

type Theme = 'light' | 'dark' | 'system';

The user's choice: a fixed theme, or 'system' to follow the operating system.

TokenLeaf

/** A single token value: a CSS string such as `'0.5rem'`, or a number written without a unit. */
type TokenLeaf = string | number;

A single token value: a CSS string such as '0.5rem', or a number written without a unit.

TokenPath

type TokenPath<T, Prefix extends string = ''> = {
  [K in keyof T & string]: T[K] extends TokenLeaf ? `${Prefix}${K}` : TokenPath<T[K], `${Prefix}${K}.`>;
}[keyof T & string];

Every dotted path to a leaf of a token tree ('color.primary'), so token names are checked at compile time.

@kitr/theme/provider

themeProvider

const themeProvider: import("@kitr/core").KitrProvider;

Wraps the app in ThemeProvider with config/theme.ts. Listed in kitrrc.ts as () => import('@kitr/theme/provider'). Pair with <ThemeScript /> in the document head.

@kitr/theme/server

themeFromCookie

function themeFromCookie(cookieHeader: string | null | undefined, storageKey?: string): ResolvedTheme | undefined;

Read the theme cookie on the server to pass as initialResolved.

ResolvedTheme

type ResolvedTheme = 'light' | 'dark';

The theme on screen once 'system' is resolved: always 'light' or 'dark'.

Theme

type Theme = 'light' | 'dark' | 'system';

The user's choice: a fixed theme, or 'system' to follow the operating system.