API
This page lists the public helpers exported by @domphy/theme.
Common Runtime Helpers
themeColor(object, tone?, color?)
Resolve a color CSS variable reference (var(--…)) from the current theme and tone context.
backgroundColor: (listener) => themeColor(listener, "inherit", "primary")
color: (listener) => themeColor(listener, "shift-9", "primary")object accepts an ElementNode, a Listener, or null. Pass null to resolve against the light theme with no context.
Use this for text color, background color, outline color, and interaction states.
themeColorToken(object, tone?, color?)
Same signature as themeColor but returns the resolved token value (e.g. "#4a7ff4") instead of a var(--…) CSS reference. Use at design-time or when integrating with third-party APIs that require a concrete color string.
const hex = themeColorToken(null, "shift-9", "primary") // e.g. "#4a7ff4"object may be null — resolves against the light theme with no node context.
themeSize(object, size?)
Resolve a font size from the nearest dataSize context. object must be an ElementNode or a Listener — unlike themeColor, it does not accept null.
fontSize: (listener) => themeSize(listener, "inherit")
fontSize: (listener) => themeSize(listener, "increase-1")themeDensity(object)
Resolve the current density factor from the nearest dataDensity context. object accepts an ElementNode, a Listener, or null. Pass null to resolve against the default density with no context.
const d = themeDensity(listener)
paddingBlock: themeSpacing(d * 1)
paddingInline: themeSpacing(d * 3)themeDensity() returns a number, not a CSS value.
themeSpacing(n)
Return a CSS calc(n/4 em) string. The result is wrapped in calc() to preserve composability.
gap: themeSpacing(3)
minWidth: themeSpacing(32)
height: themeSpacing(6)themeFluidSpacing(min, max, viewportMin?, viewportMax?)
Return a CSS clamp() that scales between themeSpacing(min) and themeSpacing(max) across a viewport width range (default 320 px → 1280 px). Use for structural spacing — page padding, section gaps — that should grow with the viewport.
import { themeFluidSpacing } from "@domphy/theme"
padding: themeFluidSpacing(4, 16) // 1em at 320px → 4em at 1280px
gap: themeFluidSpacing(4, 8) // 1em at 320px → 2em at 1280pxDo not use fluid spacing for bounded-control padding (buttons, inputs) — use themeSpacing(themeDensity(l) * n) instead.
applySystemTheme(targetEl?, options?)
Detect the OS color-scheme preference, apply data-theme to targetEl (default: document.documentElement), and set up a listener for OS-level changes. Returns a cleanup function that removes the listener.
import { applySystemTheme } from "@domphy/theme"
// One-liner: reads localStorage first, falls back to OS preference
const cleanup = applySystemTheme()
// Custom target or storage key
const cleanup = applySystemTheme(document.getElementById("app")!, {
storageKey: "my-theme",
})Options:
| Option | Type | Default | Description |
|---|---|---|---|
persist | boolean | true | Save resolved theme in localStorage |
storageKey | string | "dp-theme" | localStorage key |
Setup Helpers
themeApply(el?)
Inject the CSS for all registered themes into the DOM.
themeApply()
themeApply(styleTag)themeCSS()
Return the CSS string for all registered themes. Mostly used for SSR.
const css = themeCSS()setTheme(name, input)
Register or override a theme.
setTheme("brand", {
colors: {
primary: ["#fff", "..."],
},
})getTheme(name)
Return the full theme object.
const brand = getTheme("brand")generateTheme(baseColors, options?)
Build a full PartialThemeInput from one base hex color per semantic role, using @domphy/palette's generateRamp for every family — see Theme Builder for a live demo and DESIGN.md for the math.
setTheme("brand", generateTheme({
primary: "#4a7ff4",
secondary: "#d8597d",
neutral: "#8d8d8d",
}))Each role's baseTones entry is picked automatically (nearest CIEDE2000 match to the input color). Roles you don't pass are simply absent from the result — setTheme() deep-merges the rest from whatever theme name already had (or light, if name is new).
Token Helpers
themeVars()
Return CSS variable references such as var(--primary-6) and var(--fontSize-2).
themeTokens(name)
Return the raw token object of a registered theme.
themeName(object)
Return the active theme name for the current node or listener.
Theme Shape
setTheme() accepts a partial ThemeInput.
type ThemeInput = {
direction: "lighten" | "darken"
colors: Record<string, string[]>
baseTones: Record<string, number>
fontSizes: string[]
densities: number[]
darkBias: number
custom: Record<string, string | number>
}For how tone and size resolution work, see Tone and Size.
Exported Types
| Type | Description |
|---|---|
ThemeInput | Full theme shape accepted by setTheme(). All fields are optional when passing a partial. |
PartialThemeInput | Deep-partial version of ThemeInput — what setTheme() actually accepts at runtime. |
ThemeVars | Object of var(--…) CSS variable references returned by themeVars(). |
ThemeColor | ColorRole | (string & {}) — the 10 built-in role names rank first in editor autocomplete/hover, but any string still type-checks (custom themes may register their own role names via setTheme/generateTheme). This is intentionally NOT a strict union — see ColorRole below for the exhaustive list. |
ColorRole | Strict union of the 10 built-in semantic role names: "neutral" | "primary" | "secondary" | "info" | "success" | "warning" | "attention" | "error" | "danger" | "highlight". Derived from COLOR_ROLES below. |
ElementTone | Valid tone descriptor strings: "inherit", "base", "shift-N", "increase-N", "decrease-N", or a semantic alias ("surface", "hover", "border", "border-strong", "muted", "text") — see Semantic Aliases. |
ElementTones | Runtime array of all valid tone strings (exported as a value for validation tooling). |
ElementSize | Valid size descriptor strings: "inherit", "increase-N", "decrease-N" (N 0–7). |
ElementDensity | Valid density descriptor strings: "inherit", "increase-N", "decrease-N" (N 0–4). |
COLOR_ROLES — runtime readonly array of the same 10 names (ColorRole's source of truth: type ColorRole = (typeof COLOR_ROLES)[number]). Use this instead of hand-listing the 10 roles when you need them as a real iterable (e.g. rendering one control per role).