refract v0.1 · beta
Getting started / Quickstart

Your first theme

Author a small RawTheme, compile it, and render a themed button — end to end in five steps. Every snippet below is real; the output is exactly what the library emits.

1 · Author a RawTheme

A plain object, one key per subsystem. Here: a brand colour (its tonal steps are synthesized for you) with a solid recipe, and one components recipe that references it.

theme.ts
const raw = {
  colors: {
    brand: { base: "#4c6ef5", text: "#ffffff" },
    recipes: {
      solid: { brand: { background: "brand", color: "brand.text",
        states: [{ state: "hover", background: "brand.dark" }] } } },
    },
  },
  components: {
    recipes: {
      buttons: {
        // compose: reference the colours recipe, add an own style delta
        // css is literal-first: bare strings are raw CSS; ref() marks a token
        primary: { colors: "solid.brand",
          css: { color: ref("colors.brand.text"), cursor: "pointer", border: "none", borderRadius: "8px",
                 padding: "10px 16px", fontWeight: "600" } },
      },
    },
  },
};

2 · Compile it with createTheme

Pass an adapter — the output format. The CSS adapter is the common choice; core ships no default, which is what keeps the Model format-neutral.

theme.ts
import { createTheme } from "@theme-registry/refract";
import { createCssAdapter } from "@theme-registry/refract-css";

const theme = createTheme(raw, { adapter: createCssAdapter() });

3 · Inject the stylesheet

theme.css is the full stylesheet — :root custom properties plus recipe classes. Add it to the document once.

theme.ts
document.head.append(
  Object.assign(document.createElement("style"), { textContent: theme.css }),
);

That theme.css contains — brand steps synthesized, the recipe lowered to a class:

theme.css (emitted)
:root {
  --dt-colors-brand: rgb(76, 110, 245);
  --dt-colors-brand-text: rgb(255, 255, 255);
  --dt-colors-brand-light: rgb(114, 148, 255);   /* synthesized (OKLCH) */
  --dt-colors-brand-dark: rgb(51, 77, 210);      /* synthesized (OKLCH) */
}
.dt-components-buttons-primary {
  color: var(--dt-colors-brand-text);   /* the "colors.brand.text" ref */
  cursor: pointer; border: none; border-radius: 8px;
  padding: 10px 16px; font-weight: 600;
}

4 · Use the class

theme.classes mirrors your recipes — subsystem → group → variant. Composition is a class list (the referenced recipe + the component's own delta), exposed as a ready-to-use className.

app.tsx
theme.classes.components.buttons.primary.className;
// → "dt-colors-solid-brand dt-components-buttons-primary"

<button className={theme.classes.components.buttons.primary.className}>Save</button>

5 · Re-theme live with override()

Derive a child theme by passing only what changes. Untouched branches keep their reference; the parent is left byte-identical — so override() is a real child theme, not a re-run. Swap dark.css in to re-theme.

theme.ts
const dark = theme.override({ colors: { brand: { base: "#93a4ff", text: "#0b1020" } } });
dark.css;   // full stylesheet for the child — brand steps re-derived
theme.css;  // unchanged
That's the whole loop. This site is the same idea at scale: four presets compiled in your browser, and “Halcyon Noir” is a theme.override() child swapped in live. Next: the full Theme API · RawTheme anatomy · the subsystems.

Decision cheatsheet

Reach for the right tool at a glance:

I want to…UseWhere
Add a dark (or high-contrast) modemodesDark mode
Make a whole new brand / child theme at runtimetheme.override(delta)Theme API
Change a value at a breakpointresponsive + targetResponsive
Swap a component to a different variant at a breakpointresponsive + variantVariants & targets
Reuse one rule-set inside another componentcomposition (colors: "solid.brand")Components
Guarantee refract loses no cascade fightsthe CSS adapter layer optionPrecedence
Check colour contrastrefract audit / audit(theme)Contrast audit
Technical documentation for @theme-registry/refract · all output is compiled client-side by the real library (CSS adapter). The chrome is theme-aware; the render panes carry each preset's own world. · MIT licensed.