Theming and Brand Setup
Configure Facet theme, brand, and density axes without changing authored utilities.
Facet theming is resolver-driven. A theme is not a separate stylesheet fork; it is a coordinate in the resolver mode space.
The checked-in source resolver uses three axes:
theme:light,darkbrand:acme,globex,verdantdensity:comfortable,compact
That creates 12 resolved permutations. The compiler emits the all-default coordinate at :root,
then emits diffs under selectors such as .dark, [data-brand="globex"], and
[data-density="compact"].
Resolver shape
{
"modifiers": {
"theme": {
"values": ["light", "dark"],
"default": "light",
"selector": { "light": ":root", "dark": ".dark" }
},
"brand": {
"values": ["acme", "globex", "verdant"],
"default": "acme",
"selector": { "acme": ":root", "globex": "[data-brand=\"globex\"]", "verdant": "[data-brand=\"verdant\"]" }
},
"density": {
"values": ["comfortable", "compact"],
"default": "comfortable",
"selector": { "comfortable": ":root", "compact": "[data-density=\"compact\"]" }
}
}
}
Brand files
Brand sets keep semantic token names stable while changing brand values. The default acme brand
defines an OKLCH seed, derives adjacent stops, and maps component semantics to those stops.
{
"color": {
"$type": "color",
"brand": {
"500": { "$value": { "colorSpace": "oklch", "components": [0.555, 0.205, 268], "hex": "#5865f2" } },
"600": {
"$value": { "colorSpace": "oklch", "components": [0.5, 0.2, 268] },
"$extensions": { "com.facet": { "derive": { "fn": "oklch.darken", "from": "{color.brand.500}", "by": 0.06 } } }
},
"400": {
"$value": { "colorSpace": "oklch", "components": [0.62, 0.18, 268] },
"$extensions": { "com.facet": { "derive": { "fn": "oklch.lighten", "from": "{color.brand.500}", "by": 0.07 } } }
}
},
"action": { "$value": "{color.brand.500}" },
"action-hover": { "$value": "{color.brand.600}" },
"accent": { "$value": "{color.brand.400}" }
}
}
The globex brand uses the same semantic names. Its on-action maps to dark text because the
source notes that white fails contrast on its orange action color.
{
"action": { "$value": "{color.brand.500}" },
"action-hover": { "$value": "{color.brand.600}" },
"accent": { "$value": "{color.brand.400}" },
"on-action": {
"$value": "{color.text.strong}",
"$extensions": { "com.facet": { "a11y": [
{ "pairsWith": "{color.action}", "minContrast": 4.5, "metric": "WCAG21" }
] } }
}
}
Runtime switching
Authored utilities do not change between brands:
<button class="bg-action fg-on-action radius-control px-inset-lg py-inset-md hover:bg-action-hover focus-visible:ring @md:px-inset-lg">
Primary action
</button>
Switch modes by changing the selectors declared in the resolver:
document.documentElement.classList.toggle("dark");
document.documentElement.dataset.brand = "globex";
document.documentElement.dataset.density = "compact";
Generated brand helper
The source includes a pre-release generation command for a brand set:
facet theme tokens/app.resolver.json --brief "calm natural growth" --name verdant --write
verdant.facet.json is present in the sources and is marked as AI-generated and contrast-correct.
Treat this as a helper around the same token graph model, not a replacement for reviewing brand
tokens.