Theming

Theme generator.

Design your brand visually — tune colors, corner radius, typography, spacing, shadows and gradients and watch a real Aktion UI update live. When it looks right, copy the ready‑to‑paste $theme({…}) statement and drop it into any program.

Live preview
$theme(…) output
$theme({})

How to use it

The generator starts from one of the six built‑in themes (Start from). Every control you touch becomes an override layered on top of that base — so the output stays minimal and only contains what you actually changed. Pick a colour preset for an instant palette, fine‑tune individual tokens, then copy the generated $theme({…}).

  1. Choose a base. It seeds the surfaces, type, and defaults you build on top of.
  2. Tune tokens. Colours, roundness, density, typography, elevation and a brand gradient — the preview re‑skins instantly.
  3. Copy the statement. Hover the code block and hit Copy, then paste the $theme({…}) at the top of any Aktion program.

Every value in the panel is read back from the runtime rather than hardcoded here, so the base defaults you see are the ones the library actually ships.

Using the generated theme

A bare $theme({…}) statement lives at the top level of a program, alongside your $app(…) root. The runtime writes the tokens to the host element as CSS variables and re‑skins every component — no stylesheet, no rebuild:

$theme({
  name: "light",
  colors: { primary: "#7c3aed", primaryHover: "#6d28d9", accent: "#06b6d4" },
  radius: { md: "14px", button: "999px" }
})

$app(Column([
  Card([
    CardHeader("Branded card"),
    Row([
      Button("Primary", { variant: "primary" }),
      Button("Ghost", { variant: "ghost" })
    ], { gap: "sm" })
  ])
], { gap: "md" }))

Grouped tokens, not flat keys

Inside $theme({…}) tokens live in groups (colors.primary, radius.md, font.familyHeading) — that is exactly what this generator emits. The optional name key selects a built‑in theme as the base palette before your overrides layer on top. Flat keys like colorPrimary are not accepted here — those belong on the host theme attribute / setTheme().

Setting it on the host instead

The same palette can be applied imperatively on the <aktion-app> element — handy for a user‑facing theme switcher. The host path takes a built‑in name or a flat token map:

const app = document.querySelector("aktion-app");
app.setTheme("dark");                          // a built-in name
app.setTheme({ colorPrimary: "#7c3aed",        // flat token overrides
              radiusMd: "14px" });

Use $theme({…}) (grouped) inside a program for a per‑response brand; use setTheme() (flat) on the host for an app‑wide base. The flat shape is also the only one that reaches all 113 tokens. See Themes & customization for the full token reference.

What the generator controls

Six of the ten token groups have controls here. They are the ones that change how a UI feels; the rest are better set by hand once, and are listed below.

GroupTokens
colorsBrand (primary, accent, focusRing), surfaces (bg, surface, border, borderControl), text, status fills (success / warning / danger / info) and their text‑safe partners (successText, onSuccess, …).
radiusOne roundness slider drives xslg plus button/input; a pill toggle rounds buttons fully.
spacingA density control scales the xsxl spacing ramp behind every gap/padding.
fontBody & heading families (with on‑demand web‑font loading), base size, and heading weight.
shadowsAn elevation preset sets the sm/md/lg shadow ramp.
gradientsA two‑stop brand gradient powering GradientText and sx.bg: "gradient.brand".

The generator does not emit zIndex or motion, but both are real $theme groups — add them to the copied statement by hand:

$theme({
  zIndex: { modal: 2000, toast: 2100 },
  motion: { fast: "100ms", base: "160ms", ease: "cubic-bezier(.2,.8,.2,1)" }
})

What genuinely cannot be expressed in the grouped form is a different list: the five button* tokens, the six chart* series colours, the eight hl* syntax colours, borderWidth, transitionDuration and the four type metrics (lineHeightBody, lineHeightHeading, letterSpacingHeading, headingTextTransform). Those 25 tokens have no group to live in, so they only reach the host through the flat shape — see tokens with no $theme route.

A name: base does not carry the theme's CSS personality

When you start from shadcn, mui, heroui, signal or soft, the generated statement begins $theme({ name: "…" }). That copies the theme's token values, but not its extra stylesheet block — MUI's uppercase tabs and borderless Paper, HeroUI's dimming hover and offset focus ring, shadcn's 3px focus halo. Those are gated on the data-rui-theme marker, which only the host theme attribute and setTheme() write. For the full look, set the theme on the host and paste the generated overrides on top. Details: Per-response vs host theme.

Two more things the panel leaves alone. colors.link / colors.linkHover are optional and derived from the accent, so they have no base value to show; add them by hand if your accent is too light to read as link text. And RTL is not a token at all — use the dir attribute on <aktion-app>.

Next