Branding
elements man style/theming/branding Read as markdownBranding an Elements app means repointing the accent tokens. There are two
levels: a light-touch accent override, and a full brand ramp. Both go in
@layer tokens, imported after @elements/style.
The accent block
Five tokens carry the brand:
| Token | Role |
|---|---|
--accent |
The brand color |
--accent-deep |
Hover, pressed, gradient end |
--accent-soft |
Tinted accent backgrounds (selected rows, accent callouts) |
--accent-ink |
Text and icons sitting on an --accent fill |
--accent-edge |
Accent borders on tinted surfaces |
Every accent-bearing component reads these. Set them once and buttons, focus rings, checkboxes, selected tabs, and accent surfaces all follow.
Link color
A bare <a> is the one thing the accent block does not cover, because a link
has to stay readable inside a dark panel or a brand-colored hero as well as on
the page. It takes its lightness from the text around it and its color from two
tokens of its own:
| Token | Role |
|---|---|
--link-c |
Link chroma, roughly 0.10 to 0.18 for a saturated brand |
--link-h |
Link hue in degrees, the same hue as your brand ramp |
:root {
--link-c: 0.120;
--link-h: 235;
}
Leave them unset and links render as the surrounding ink a shade deeper, plus the underline. That is always legible, which is why it is the default, but it is not branded. See base for how the adaptive color works.
Light touch
If you just want a brand color, set the accent block. In a scaffolded app the
place for this is app/shared/styles/vars.css (imported by both the page and
email baselines):
:root {
--accent: #4a7bc4;
--accent-deep: #2f5a9e; /* hover / pressed / gradient end */
--accent-soft: #eef3fb; /* tinted accent backgrounds */
--accent-ink: #ffffff; /* text on the accent fill */
}
--accent alone is enough to color the system. The soft and ink companions are
needed for tinted regions (an accent callout, a selected-row background, text on
an accent fill); skip them and components fall back to --accent everywhere,
which works but reads flatter.
You can also point fonts here in the same file:
:root {
--font-sans: "Your Sans", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Your Mono", ui-monospace, monospace;
}
Full brand ramp
For a polished result across light and dark mode, define a full brand ramp and
point the accent block at it. Copy themes/customer-example.css into
app/shared/styles/, edit the values, and import it from page.css after
@elements/style. The recipe has three steps.
Step 1: your brand ramp. Anchor your brand color at 500 and fill in a
50–950 ramp. Match the lightness curve of the ramps in themes/default.css so
your brand behaves like the status ramps (an OKLCH ramp generator seeded with
your hex gets you there):
@layer tokens {
:root {
--brand-50: oklch(0.97 0.015 0);
--brand-100: oklch(0.93 0.040 0);
--brand-200: oklch(0.87 0.080 0);
--brand-300: oklch(0.80 0.130 0);
--brand-400: oklch(0.72 0.180 0);
--brand-500: oklch(0.62 0.220 0); /* your brand color */
--brand-600: oklch(0.54 0.215 0);
--brand-700: oklch(0.46 0.180 0);
--brand-800: oklch(0.36 0.140 0);
--brand-900: oklch(0.26 0.100 0);
--brand-950: oklch(0.16 0.065 0);
}
}
Step 2: repoint the accent block at the ramp. This is the only block that does the branding work:
@layer tokens {
:root {
--accent: var(--brand-500);
--accent-deep: var(--brand-700);
--accent-soft: var(--brand-50);
--accent-ink: var(--brand-900);
--accent-edge: var(--brand-200);
--focus: var(--accent);
--focus-ring: 0 0 0 3px color-mix(in oklch, var(--focus) 40%, transparent);
}
}
Step 2b (optional): the checkbox glyph. The checkmark is an inline SVG data
URI, so it can't read --accent-ink. If your --accent-ink isn't near-white,
repoint --checkbox-check to a stroke matching it (copy the URL from
themes/default.css and change the stroke='%23…' hex).
Step 3 (optional): dark-mode brand override. Use a lighter ramp step on dark
backgrounds so the color stays vibrant. Follow the same two-block pattern the
default theme uses, the OS-preference block and the data-theme block, kept
identical:
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--accent: var(--brand-400);
--accent-deep: var(--brand-300);
--accent-soft: var(--brand-950);
--accent-ink: var(--brand-100);
--accent-edge: var(--brand-800);
}
}
:root[data-theme="dark"] {
--accent: var(--brand-400);
--accent-deep: var(--brand-300);
--accent-soft: var(--brand-950);
--accent-ink: var(--brand-100);
--accent-edge: var(--brand-800);
}
The shipped example: themes/elements.css
themes/elements.css is the canonical worked example: the Elements brand blue.
It's opt-in, not part of the default entry, and follows exactly the recipe
above:
@import "@elements/style";
@import "@elements/style/themes/elements";
It defines an Elements-blue ramp (--elements-blue-*, anchored at #49A9E7 /
#164C77), repoints the accent block at it, updates --focus and
--focus-ring, sets a white checkbox glyph (its --accent-ink is white in both
modes), and adds the dark-mode accent override. Read it as a complete, real
branding file.