Theming

Seven themes — or write your own.

Themes are just CSS custom properties applied to the host element — switching one is instant and never re-renders. Preview every built-in theme below, compare two side-by-side, or open the studio to tweak any token and copy the result straight into your app.

Jump to section

Theme picker & live preview

Pick a theme — the preview below updates instantly. Want to test your own program? Expand the editor and paste it in.

Theme
Edit preview program

Compare two themes

Pick any two themes and see the same content rendered side-by-side. The panes scroll in sync so it's easy to spot differences in spacing, radii, and surface contrast.

light
dark

Customization studio

Tweak any token and watch the live preview update in real time. The studio remembers your edits in localStorage, so you can come back later and keep iterating. Copy the result as a setTheme() call, as raw CSS variables, or download the full JSON.

Copy output


                

Use it from your app

Every option below hits the same code path — pick the one that fits your stack. Built-in names additionally set data-rui-theme on the host so the bundled stylesheet can layer on theme-specific tweaks (fonts, gradients, animations).

1. Built-in name (attribute)

<aktion-app theme="neon"></aktion-app>

2. JSON token map (attribute)

<aktion-app
  theme='{"colorPrimary":"#16a34a","radiusMd":"14px"}'>
</aktion-app>

3. Programmatic setTheme()

const el = document.querySelector("aktion-app");

el.setTheme("dark");

el.setTheme({
  colorPrimary: "#16a34a",
  colorPrimaryHover: "#15803d",
  colorBg: "#f0fdf4",
  radiusMd: "14px",
});

4. Host-page CSS variable override

aktion-app {
  --rui-color-primary: #16a34a;
  --rui-radius-md: 14px;
}

Partial maps are merged on top of light

Only override what you need — everything else falls back to the default light theme tokens.

In-script theming with Theme({...})

An LLM can brand a single response without touching host code by assigning a Theme({...}) call to a reserved top-level identifier named theme. The runtime detects the binding, writes the tokens to the host as CSS custom properties, and reskins the rest of the rendered UI instantly.

theme = Theme({
  colors: {
    primary:      "#0969da",
    primaryHover: "#0860c7",
    accent:       "#1f883d"
  },
  font: {
    family:      "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
    weightBody:  "400"
  },
  radius: { button: "6px" }
})

_app_ = Stack([
  CardHeader("Welcome", subtitle: "Sign in to your workspace"),
  Buttons([Button("Sign in"), Button("Cancel", variant: "secondary")])
])
  • Reserved name. Only theme (alongside _app_) is allowed without being referenced elsewhere. The runtime picks both up by name.
  • Structured groups only. The top-level keys must be colors, radius, font, motion, elevation (plus the metadata keys name and direction). Flat-shape tokens (colorPrimary, radiusMd) and free-form CSS variables (--rui-color-x) raise a schema-validator warning and are dropped.
  • Naming convention. Inside each group, camel-case keys flatten predictably: colors.primarycolorPrimary, colors.primaryHovercolorPrimaryHover, radius.mdradiusMd, radius.buttonradiusButton, font.familyHeadingfontFamilyHeading.
  • Partial maps. Pass only the groups you want to override — everything else inherits from the base theme set on the host (theme="light", theme="dark", …).
  • Live updates. Streaming a different Theme({...}) block on the next render re-applies the override; the previous tokens are cleared first so themes never leak across responses.
  • Host-side setTheme() uses flat tokens. The structured-only rule applies to the in-script Theme({...}) call. el.setTheme({...}) from the host (see below) takes the flat token names listed in the reference table.

See it in action

Open the brand-themes live example to see the same dashboard reskinned as GitHub, Apple, Stripe, IONOS, Notion, and Vercel — using only this construct. Copy any brand into your own response or feed the token map through setTheme() instead.

Author a brand theme

Most brands need to override 4–6 tokens to feel native. Here's a minimal recipe; the studio above generates the same shape of object.

1

Pick a base

Start from one of the seven built-in themes: light, dark, neon, pastel, glass, brutalist, or skyline — whichever surface and vibe best fits your brand.

2

Override 4–6 tokens

Typically colorPrimary, colorPrimaryHover, radiusMd, and 1–2 chart colors.

3

Name it

Save it as a constant in your app so it's importable from anywhere — e.g. brandTheme.

4

Apply & ship

Pass it as the theme attribute, or call el.setTheme(brandTheme) after mount.

// brand-theme.js
export const brandTheme = {
  colorPrimary:      "#16a34a",
  colorPrimaryHover: "#15803d",
  colorBg:           "#f0fdf4",
  radiusMd:          "14px",
  chart1:            "#16a34a",
  chart2:            "#0ea5e9",
};

// app.js
import { brandTheme } from "./brand-theme.js";
const el = document.querySelector("aktion-app");
el.setTheme(brandTheme);

Use the Download JSON button in the studio above to export the tokens you just tweaked — the file drops straight into a JavaScript module like the one above.

Token reference

Every theme token, its corresponding CSS custom property, and what it affects. Use the search box to filter by name or description.

Flat ↔ structured cheatsheet for in-script Theme({...})

The flat tokens below are the canonical names for host-side el.setTheme({...}) calls, the theme attribute’s JSON form, and direct CSS custom-property overrides. Inside an in-script Theme({...}) binding the runtime requires the structured form: every colorX token lives under colors.x, every radiusX under radius.x, every fontX under font.x, and so on. Camel-case is preserved (e.g. colorPrimaryHovercolors.primaryHover; radiusButtonradius.button; fontFamilyHeadingfont.familyHeading).

Token CSS variable Current value Description