Flat token maps, grouped output
Each preset on this page is stored as a flat
ThemeTokens map — colorPrimary,
radiusButton, buttonFontWeight — because that is the
shape el.setTheme(...) and the theme attribute take.
The Theme source panel above shows the same palette translated
into the grouped $theme({...}) shape a program
uses, and that translated block is what the live preview actually runs.
The two shapes are not quite interchangeable. Ten groups exist —
colors, radius, font,
spacing, shadows, gradients,
zIndex, motion, plus the two side-effect groups
fonts and icons — but 25 tokens have no group at all.
The button* padding and weight, the six chart* series
colours, borderWidth, letterSpacingHeading and
lineHeight* only reach the host through the flat path, so the
grouped block you copy from here omits them. Every preset on this page uses some
of them, which is exactly why the flat map is the source of truth.
One more gap worth knowing about: these presets retint the status
fills (colorSuccess and friends) but not their text-safe
partners (colorSuccessText, colorOnSuccess, …) or
colorBorderControl. Those inherit the light theme's
accessible values, so status labels stay on the built-in hue rather
than the brand's. Add the partners to finish a brand — see
status colours come in threes.
The shared program
Every brand renders the same Aktion source. Only the
$theme({...}) line at the top changes — the
runtime applies the new tokens to the host as CSS custom properties,
so the existing components instantly pick up the new palette,
typography, and shape.
head = PageHeader(
"Acme Platform",
{
subtitle: "All your operations, one workspace.",
breadcrumbs: ["Workspace", "Dashboard"],
actions: [
Button("Settings", { variant: "secondary", size: "sm", icon: "gear" }),
Button("New project", { variant: "primary", size: "sm", icon: "plus" })
],
badge: Badge("On track", { tone: "success" })
}
)
kpis = Stats([
StatCard("Active deals", { value: "1,284", trend: "up", delta: "+12%", icon: "briefcase" }),
StatCard("Monthly revenue", { value: "$48.2k", trend: "up", delta: "+8%", icon: "sack-dollar" }),
StatCard("Open tickets", { value: "42", trend: "down", delta: "-15%", icon: "ticket" }),
StatCard("Churn", { value: "1.4%", trend: "flat", delta: "0", icon: "user-minus" })
])
trendChart = Card([
SectionHeader("Revenue trend", {
subtitle: "Last 12 weeks",
actions: [Button("Export", { variant: "ghost", size: "sm", icon: "arrow-up-from-bracket" })]
}),
LineChart({
labels: ["W1","W2","W3","W4","W5","W6","W7","W8","W9","W10","W11","W12"],
series: [
Series("This quarter", { values: [22, 28, 26, 32, 34, 31, 38, 42, 44, 41, 46, 48] }),
Series("Last quarter", { values: [18, 21, 20, 24, 27, 25, 28, 31, 33, 30, 34, 36] })
]
})
])
teamCard = Card([
SectionHeader("Team", { subtitle: "6 members", eyebrow: "Engineering" }),
Stack([
PersonChip("Asha Patel", { role: "Engineering lead", size: "md", status: "online" }),
PersonChip("Linus Wong", { role: "Senior developer", size: "md", status: "online" }),
PersonChip("Mira Sanchez", { role: "Product designer", size: "md", status: "away" }),
PersonChip("Sam Reyes", { role: "QA engineer", size: "md", status: "offline" })
], { direction: "column", gap: "sm" })
])
activityCard = Card([
SectionHeader("Recent activity", { subtitle: "Last 24h" }),
Timeline([
TimelineItem("Deployed v2.4.1", { time: "12:04", icon: "rocket", tone: "primary" }),
TimelineItem("Closed 4 support tickets", { time: "11:18", icon: "circle-check", tone: "success" }),
TimelineItem("Updated billing API", { time: "10:02", icon: "credit-card", tone: "info" }),
TimelineItem("Investigating latency", { time: "08:47", icon: "triangle-exclamation", tone: "warning" })
])
])
planCard = Card([
SectionHeader("Workspace plan", { subtitle: "Renews in 12 days" }),
ProgressRing(72, { max: 100, label: "12 / 17", description: "Seats used", tone: "primary" }),
Buttons([
Button("Manage seats", { variant: "secondary", size: "sm" }),
Button("Upgrade", { variant: "primary", size: "sm" })
])
])
quickForm = Card([
SectionHeader("Quick capture", { subtitle: "Add a new task" }),
FormControl("Title", { control: Input("title", { placeholder: "Ship the analytics tab" }) }),
FormControl("Owner", { control: Select("owner", { items: [
SelectItem("asha", "Asha Patel"),
SelectItem("linus", "Linus Wong"),
SelectItem("mira", "Mira Sanchez")
], value: "asha" }) }),
Buttons([
Button("Add task", { variant: "primary" }),
Button("Cancel", { variant: "ghost" })
])
])
bottomGrid = Grid([teamCard, activityCard, planCard, quickForm], { columns: 2, gap: "lg" })
$app(Stack([head, kpis, trendChart, bottomGrid], { direction: "column", gap: "lg" }))
Use it from your app
Three ways to apply a brand, in three different languages. Pick by where the
decision lives: in the model's response, in your host JavaScript, or in your
page's stylesheet.
$theme({
colors: { primary: "#0969da", primaryText: "#ffffff" },
font: { family: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" },
radius: { button: "6px" }
})
$app(Stack([CardHeader("Welcome"), Buttons([Button("Sign in")])]))
Inside a program the keys are grouped. Button weight is not available here —
buttonFontWeight has no group, so it has to come from the host.
// The host paths take flat token names, and they reach every token —
// including buttonFontWeight, chart1..6 and borderWidth.
el.setTheme({
colorPrimary: "#0969da",
colorPrimaryText: "#ffffff",
radiusButton: "6px",
buttonFontWeight: "500",
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
});
// Equivalently, hand the element a JSON token map on the attribute:
el.setAttribute("theme", JSON.stringify({ colorPrimary: "#0969da" }));
setTheme() replaces the whole base layer and resets the
per-response overlay tracker; the next render reapplies any
$theme({...}) from the program on top.
aktion-app {
/* !important is required: see the note below. */
--rui-color-primary: #0969da !important;
--rui-radius-button: 6px !important;
--rui-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
}
The --rui-* prefix is the public contract, so a stylesheet can
drive it — with one catch. Applying a theme writes every token the theme
defines as an inline style on the host element, and an inline
declaration beats a normal stylesheet rule, so the !important is
what makes this win. Tokens the active theme leaves unset (the optional ones,
such as --rui-color-link or --rui-motion-fast) are not
written inline, so those need no !important. Prefer options 1 and 2
unless the values genuinely have to come from CSS.