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

Accent soft

Violet

Accent soft

Sunrise

Accent soft

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.

TokenDefaultPurpose
--brandRequiredThe brand-color seed.
--brand-contrastAutomatic black or whiteOptional override for text and icons on the primary accent.
--brand-radius10pxThe --radius-md value.
--brand-font"Plus Jakarta Sans", system-ui, sans-serifBoth --font-sans and --font-display.

Here is a complete brand definition:

css
html[data-brand='harbor'] {
  --brand: #0d9488;
  --brand-contrast: #ffffff;
  --brand-radius: 12px;
  --brand-font: 'Plus Jakarta Sans', system-ui, sans-serif;
}

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, darkens hover and active with black, 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 accent, hover, active, soft-text, and link mixes use white instead. 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.

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.