Setup
Install
npm install @domphy/core @domphy/theme @domphy/ui@domphy/core and @domphy/theme are peer dependencies of @domphy/ui — all three must be installed. If you only need theme tokens without the UI patches:
npm install @domphy/core @domphy/themeApply Theme CSS
Call themeApply() once on the client. It injects a <style id="domphy-themes"> tag into <head>.
import { themeApply } from "@domphy/theme"
themeApply()If you need to control the target style element, pass one explicitly:
const styleTag = document.createElement("style")
themeApply(styleTag)That is mainly useful for Shadow DOM or isolated preview roots.
Choose The Active Theme
Set dataTheme on any root element.
{ div: [App], dataTheme: "light" }
{ div: [App], dataTheme: "dark" }light and dark are built in. dark is generated automatically from light.
dataTheme can appear at any nesting level. Descendants inside that subtree resolve colors from the nearest theme root.
Register A Custom Theme
Use setTheme() to register or override a theme. All fields are optional — unspecified ones inherit from light.
Replace a built-in color family
Override an existing family (e.g. swap primary to a violet ramp):
import { setTheme, themeApply } from "@domphy/theme"
setTheme("brand", {
colors: {
primary: ["#ffffff", "#f7f5ff", "#efe8ff", "#e5d9ff", "#d6c2ff", "#c4a6ff", "#af87ff", "#9a6dff", "#8658ff", "#7345f7", "#6033df", "#512bc0", "#43249e", "#351c7d", "#28155d", "#1c0e3f", "#0e0720", "#000000"],
},
baseTones: {
primary: 9,
},
})
themeApply()Add a brand-new color family
Register a completely new color name alongside the built-in ones. The colors key can hold any name — Domphy generates CSS variables and exposes the family to themeColor() automatically:
import { setTheme, themeApply, themeColor } from "@domphy/theme"
setTheme("brand", {
colors: {
// New "brand-gold" family — 18-step ramp from light (#fffef0) to dark (#1a1500)
"brand-gold": [
"#fffef0", // 0 — near-white
"#fffbd0", // 1
"#fff6a0", // 2
"#ffee65", // 3
"#ffe030", // 4
"#f5cc00", // 5
"#d9b200", // 6
"#bf9a00", // 7
"#a58200", // 8
"#8a6b00", // 9 ← good accent zone
"#705600", // 10
"#574200", // 11
"#3f2f00", // 12
"#2d1f00", // 13
"#1f1500", // 14
"#130d00", // 15
"#070400", // 16
"#000000", // 17 — black
],
},
baseTones: {
"brand-gold": 9, // base accent at step 9
},
})
themeApply()
// Use the new family exactly like built-in ones:
const GoldBadge = {
span: "New",
style: {
background: (l) => themeColor(l, "shift-2", "brand-gold"),
color: (l) => themeColor(l, "shift-11", "brand-gold"),
},
}Custom color ramps must follow the 18-step model (index 0 = lightest, 17 = darkest). The dark theme is auto-generated by reversing the ramp — you do not need a separate dark variant.
Then activate the theme:
{ div: [App], dataTheme: "brand" }Custom design tokens
Arbitrary key-value tokens can be stored under custom. They appear as --custom-{key} CSS variables:
setTheme("brand", {
custom: {
"border-radius-pill": "9999px",
"sidebar-width": "240px",
"topbar-height": "56px",
},
})
// Access via CSS or themeVars():
import { themeVars } from "@domphy/theme"
const vars = themeVars()
// vars.custom["border-radius-pill"] === "var(--custom-border-radius-pill)"SSR
For SSR, inline themeCSS() on the server.
import { themeCSS } from "@domphy/theme"
const html = `<!DOCTYPE html>
<html>
<head>
<style id="domphy-themes">${themeCSS()}</style>
</head>
<body>
<div data-theme="light">...</div>
</body>
</html>`If the CSS is already in the HTML, the client usually does not need to call themeApply() again unless you later change registered themes.
For the full API surface, see API.