Knit UI
GitHub

Theme builder

createTheme builds a complete config from brand inputs. Every option is optional and falls back to the kit's defaults, so you override as little as you need.

import { createTheme, Provider } from "@knitui/core";

export const config = createTheme({
  brand: "#7C3AED",
});

export function App({ children }) {
  return (
    <Provider config={config} defaultColorScheme="system">
      {children}
    </Provider>
  );
}

One hex is enough: the light and dark 12-step ramps are derived for you.

Brand and accents

createTheme({
  brand: "violet",                              // a @tamagui/colors ramp name…
  // brand: "#7C3AED",                          // …or a hex
  // brand: { light: "violet", dark: "purple" },// …or per scheme
  brandThemeName: "primary",                    // theme="primary" (default "brand")
  accents: { teal: "#14b8a6", brandish: "indigo" },
  includeDefaultAccents: true,                  // keep blue/red/green/…
  includeTamaguiColors: ["violet", "sand"],     // register extra Radix ramps
  neutral: "#71717a",                           // the UI chrome ramp
});

Each accent becomes a usable theme value: <Button theme="teal">.

Tokens

createTheme({
  radius: "rounded",        // "default" | "rounded" | "sharp" | { md: 12, … }
  space: "comfortable",     // "default" | "compact" | "comfortable" | { … }
  size: { md: 18 },
  fontSizes: { xl: 24 },
  zIndex: { 6: 600 },
  colors: { brandRaw: "#7C3AED" },
});

Presets exist because the three scales have to stay in proportion; picking space: "comfortable" also moves size unless you override it.

Fonts

createTheme({
  fonts: {
    body: "Inter",
    heading: { family: "Sora", weight: { 7: "800" } },
    mono: "JetBrains Mono",
  },
  defaultFont: "body",
});

Named fonts beyond body/heading are registered too, so fontFamily="$mono" works.

Responsive and motion

createTheme({
  breakpoints: { md: 900 },              // rebuilds the media queries
  media: { tall: { minHeight: 800 } },   // custom queries
  animations: myDriver,                  // replace the animation driver
  shorthands: { bgc: "backgroundColor" },
});

Extending instead of replacing

extendTheme takes an existing config and layers on top, which is what you want when you only need one more accent:

import { extendTheme } from "@knitui/core";

const config = extendTheme({ accents: { brandPink: "#ec4899" } });

Presets

themePresets ships ready-made configurations to start from:

import { themePresets, Provider } from "@knitui/core";

<Provider config={themePresets.violet} />

Validation

Bad input fails loudly rather than rendering something odd — validateThemeOptions runs inside createTheme and reports which key was wrong and what it accepts. That matters because a mistyped ramp silently falls back to grey otherwise.

Where to put it

Build the config once at module scope and pass it to Provider. Do not build it inside a component — a new config object per render invalidates every style.