Tokens & Overrides
Theme tokens come in two layers. Layer 1 is the palette (--hz-palette-*):
single-value hues, authored per theme. Layer 2 is the semantic roles (--hz-color-*) and intents (--hz-intent-*), pure var() indirection that chains through
Layer 1. Override one hue and the change follows everywhere it is used.
Remap or extend Layer 2 directly when you want different wiring instead of a palette override.
Token names and defaults live on Colors & Intent. Duration and easing tokens (--hz-duration-* / --hz-ease-*) follow the same override rules and are documented on Motion. That page also introduces @hyzer-labs/ui/motion: script-side helpers built on those tokens, including
transitions, a scroll-reveal attachment, and a view-transition wrapper.
Dark may override any tier in[data-theme='dark'], including the palette. Your components and the reference theme never read--hz-palette-*directly, so they keep resolving through roles and intents either way.
Dark is not a special case in the config, either: it is one entry in a themes map, and you can add as many more as you like. Section themes covers naming them and scoping one to part of
a page. In plain CSS, with no build step
Import your stylesheet after tokens.css and redefine what you need. These are the four
recipes that cover nearly everything:
/* Override any hue once, in the --hz-palette-* namespace; every role
and intent that references it follows automatically. */
:root {
--hz-palette-primary: #0f766e;
--hz-palette-gray: #64748b; /* border, text-muted, and surface tints follow */
}/* The intent layer is where you rewire: point an intent at a different
palette hue than its default, or add an intent of your own. */
:root {
--hz-intent-danger: var(--hz-palette-secondary); /* remap */
--hz-intent-fairway: #3f6212; /* extend */
}/* Dark may override any tier, including the palette: the same hook the
base sheet uses. Override a hue here and its intents, borders, and
muted tints follow in dark only. Your components keep resolving
through roles and intents either way. */
[data-theme='dark'] {
--hz-palette-primary: #2dd4bf;
}
/* Intents can be re-authored inside a theme too, if you want a mapping
that applies in dark only: */
[data-theme='dark'] {
--hz-intent-fairway: #a3e635;
}
/* Two things you get without writing them:
- the sheet already follows the system preference, so you only need
a script to OVERRIDE it, never to obey it;
- data-theme works on ANY element, not just <html>. One section can
be dark inside a light page, and data-theme="light" puts a section
back to the default. See Section themes. */:root {
--hz-radius-md: 0.625rem; /* buttons, cards, fields */
--hz-density: 0.5rem; /* rescales every near/away distance */
--hz-font-family-sans: 'Inter', system-ui, sans-serif;
}Or describe it once, in a config
Everything above is hand-written CSS, which is the whole point of this tier: no build step, no
tooling. You can instead keep your system in one typed file and have the sheet generated for
you, with every pairing contrast-graded on each run. Config & CLI covers hyzer.config.ts end to end: the option surface, the hyzer generate modes, the trimmed icon barrel, and the utilities sheet.
The two routes reach the same place. A config resolves to the same two-layer model this page describes, so nothing you learn here is wasted if you adopt one later.
Verify your palette
The contrast math behind the generate report is public API. contrastRatio gives
the raw WCAG ratio for two hex colors. gradeContrast turns a
foreground/background pair into pass/fail grades per level and text size. bestLevel and bestLevelLarge name the best level a ratio reaches. mixSrgb, relativeLuminance, hexToRgb, and rgbToHex are the conversion pieces.
All of them are pure functions over hex strings, with no DOM access, so they are safe to run
on the server. They are exported from the package root and @hyzer-labs/ui/utils,
and the resolved palette is importable from @hyzer-labs/ui/tokens.
Assert the pairings your override touches in a unit test:
import { gradeContrast, contrastRatio, mixSrgb } from '@hyzer-labs/ui';
import { palette } from '@hyzer-labs/ui/tokens';
// Your override for --hz-palette-primary
const brand = '#0f766e';
gradeContrast(brand, palette.white).aaNormal; // text on surface
gradeContrast(palette.white, brand).aaNormal; // solid button text
// On surface-muted: the same 6% color-mix the theme derives
contrastRatio(brand, mixSrgb(palette.gray, palette.white, 0.06));For the full methodology and a live pairing checker over every shipped token, see Contrast & Accessibility.