White-label
Put data-brand on an element and set up to four custom properties. Lyra re-derives its accent
group from that seed, so the components in that subtree use your brand without per-component
overrides.
See it working
Three brands in the current theme
Each card scopes its own data-brand. Use the site theme toggle in the header to see all three
brands re-derive in place, without a rebuild. Tab to an input or primary button to see the
brand-derived focus ring.
Harbor
Violet
Sunrise
Define a brand
--brand is the required seed. The other three tokens have defaults. Lyra derives readable primary
ink automatically; set --brand-contrast only when you want to choose that ink yourself.
| Token | Default | Purpose |
|---|---|---|
--brand | Required | The brand-color seed. |
--brand-contrast | Automatic black or white | Optional override for text and icons on the primary accent. |
--brand-radius | 10px | The --radius-md value. |
--brand-font | "Plus Jakarta Sans", system-ui, sans-serif | Both --font-sans and --font-display. |
Here is a complete brand definition:
html[data-brand='harbor'] {
--brand: #0d9488;
--brand-contrast: #ffffff;
--brand-radius: 12px;
--brand-font: 'Plus Jakarta Sans', system-ui, sans-serif;
}Never set data-brand without a matching --brand definition. The attribute alone activates the
derivation layer, and with no seed the whole accent group resolves to nothing — primary buttons
lose their fill. To show Lyra's baseline look (for example a "default" option in a brand
switcher), remove the attribute instead of defining an empty brand.
Use sentence case for labels
Overline-style labels default to uppercase with their original tracking. For a brand or locale that uses sentence case, add the optional typography tokens to the same brand scope. They work for table headers, sidebar and menu groups, calendar labels, and other overline labels:
html[data-brand='harbor'] {
--brand: #0d9488;
--overline-transform: none;
--overline-tracking: normal;
}--overline-transform: none preserves the case supplied in the markup; write labels in sentence
case (for example, Delivery status). Neither token is part of the four brand seed tokens or
automatically derived from --brand.
Choose the scope
Put data-brand on <html> when the whole application belongs to one brand. Every descendant
uses the derived values.
Put it on a container when only part of the page needs a different identity, such as an embedded workspace preview or a tenant-specific area. Only that container and its descendants receive the brand values, which is how the three cards above coexist.
What Lyra re-derives
In light mode, Lyra keeps --accent as the raw --brand, lightens hover and active with white
at 3% and 4% in srgb, and derives --accent-soft, --accent-soft-text, and --focus-ring with
color-mix() in oklab. It also sets --on-accent, --text-link, --border-accent,
--radius-md, --font-sans, and --font-display.
Inside data-theme="dark", the branded accent, hover, and active mixes use white at 14%, 26%, and
30% in oklab; soft-text and link retain their own white mixes.
The dark rule works whether data-theme="dark" and data-brand are on the same element or the
branded element is inside the themed one.
Theme islands
A data-theme="dark" container inside a light page works, including a branded descendant. This is
useful for a permanently dark panel. The inverse does not work today: light tokens are the defaults
on :root, not a [data-theme="light"] block, so data-theme="light" cannot restore light tokens
inside a dark ancestor. Use the site theme toggle to preview each brand in both themes.
Keep primary text readable
Lyra derives --on-accent from the resolved accent, choosing neutral black or white automatically.
Use --brand-contrast only to pin a specific ink. In browsers that support relative color syntax,
the automatic rule meets AA-large (3.0:1) for all supported brand/theme combinations. The known light-mode crossover is #E11D48, which reaches
4.47:1 rather than normal-text AA's 4.5:1; check a custom override before shipping it.
If you also customise --danger to a light colour, set --on-danger to a darker ink: solid danger
fills such as .lyra-btn--danger use it and default to #FFFFFF.
What this contract does not change
The contract does not re-derive --surface-* tokens, text colors, spacing, or radii other than
--radius-md. It does not override borders beyond --border-accent. If your product needs those
to change per tenant, define that layer separately instead of expecting data-brand to do it.