Skip to content
BrandCloud

Theming

A brand is a small JSON file. Everything else, light and dark, button states, dark sections and offer levels, is derived from it and checked for contrast at build time.

Make a brand

Four inputs: an accent, an ink, a paper and a body font. The brand builder does this in the browser; make-brand does it on the command line and writes packages/tokens/brands/<name>.json.

Terminal

node packages/tokens/scripts/make-brand.mjs --name acme --label "Acme Roofing" \
  --accent "#0f766e" --ink "#111827" --paper "#fbfaf7" \
  --font-body "'Hanken Grotesk Variable', Arial, sans-serif" \
  --font-display "'Fraunces Variable', Georgia, serif"
# or: --input brief.json (same keys), --dry-run, --report report.json, --appearance solid|depth

It exits with an error and writes nothing when a required pair fails AA. --force writes the file for review, but the build still refuses it. After adding a brand, add its ./brands/<name>.css entry to the package exports and rebuild.

Brands and modes

Two attributes, on <html> or on any element: data-brand picks the palette and data-theme picks light or dark. Nesting works both ways.

HTML

<html data-brand="harbour" data-theme="light">
  <section data-theme="dark">Still Harbour, now in its dark palette.</section>
</html>

This site, light

This site, dark

Brands that ship in the kit
BrandModesDefault buttonStylesheet
brandui (default)light, dark, operatorsolid@brandcloud/tokens/themes.css
docslight, darkdepth@brandcloud/tokens/brands/docs.css
harbourlight, darkdepth@brandcloud/tokens/brands/harbour.css
marigoldlight, darksolid@brandcloud/tokens/brands/marigold.css
orchardlight, darkdepth@brandcloud/tokens/brands/orchard.css

The depth button tokens

The depth button is a tonal gradient that models light on a physical button. All of it comes from tokens, so it follows every brand.

Depth button roles
RoleUse
action-depth-from, action-depth-toBody gradient, lighter top to deeper bottom, same hue
action-depth-glow, action-depth-glow-reachLight rising inside from the bottom edge, and how far it reaches (30%: further drops the label below AA)
action-depth-sheenThin white sheen over the top half
action-depth-edge, action-depth-rimLit 1px top edge and the inset rim
action-depth-textThe label
action-depth-fillReady-made background-image: glow plus body
shadow-action-depth, shadow-action-depth-hoverContact shadow plus a same-hue glow underneath

CSS

.button {
  background-image: var(--brand-action-depth-fill);
  box-shadow: var(--brand-shadow-action-depth);
  color: var(--brand-action-depth-text);
}
.button:hover { box-shadow: var(--brand-shadow-action-depth-hover); }

Offer level tokens

Five levels, sky, blue, navy, pearl and obsidian, for ranking offers and plans. Print the level name next to the colour so colour is never the only cue.

  • Skylevel-sky-*
  • Bluelevel-blue-*
  • Navylevel-navy-*
  • Pearllevel-pearl-*
  • Obsidianlevel-obsidian-*
Level roles
Role (L is the level)Use
level-L-highlightTop stop of a level button or tile
level-L-baseBottom stop, and the fill of a solid level button
level-L-deepContact shadow and rim. Never a text background
level-L-haloSoft same-hue glow underneath
level-L-textLabel on highlight, base and hover (checked on all three)
level-L-markFlat identity colour for marks, dots and rules
level-L-inkLevel-coloured text on the page
level-L-fillReady-made linear-gradient(highlight, base)

The gradient rule

Gradients are welcome when they model light on an object: one hue, lighter at the top, a thin sheen and a same-hue shadow, on buttons, level tiles and at most one brand-colour section. The build refuses any brand whose depth, level or WhatsApp stops span more than 40 degrees of hue, and the UI lint flags multi-hue gradients and gradient text in your code.

Terminal

pnpm exec brand-ui-lint                  # check against the baseline
pnpm exec brand-ui-lint --update-baseline

Colour roles for this site's brand

Every role exists in every brand and mode and compiles to --brand-<role>. These are the values for the docs brand.

Show 44 colour roles, light and dark
Colour roles of the docs brand
RoleLightDark
accent#b8430d#d45b2e
accent-hover#9e3603#e96e42
accent-subtle#f8ece7#392115
accent-text#ffffff#17140f
action-ink#17140f#f6f1e8
action-ink-hover#33302c#ffffff
action-ink-text#ffffff#17140f
borderrgb(23 20 15 / 0.12)rgb(255 255 255 / 0.12)
border-strongrgb(23 20 15 / 0.28)rgb(255 255 255 / 0.3)
canvas#f6f1e8#17140f
danger#be123c#d56782
focus#b8430d#d45b2e
focus-ringrgb(184 67 13 / 0.24)rgb(212 91 46 / 0.32)
inverse-borderrgb(255 255 255 / 0.12)rgb(255 255 255 / 0.14)
inverse-canvas#17140f#0c0a08
inverse-surface#25221d#23201b
inverse-surface-muted#2e2c27#2e2c27
inverse-text#f6f1e8#f6f1e8
inverse-text-muted#afaaa3#b3afa7
status-danger-border#fecdd3#9f1239
status-danger-subtle#ffe4e6#4c1726
status-info#0369a1#38bdf8
status-info-border#bae6fd#075985
status-info-subtle#e0f2fe#0c354d
status-success#157e3c#4ade80
status-success-border#bbf7d0#166534
status-success-subtle#dcfce7#123524
status-warning#9b5f07#fbbf24
status-warning-accent#d97706#fbbf24
status-warning-border#fde68a#854d0e
status-warning-subtle#fef3c7#422f0a
status-warning-text#854d0e#fbbf24
surface#ffffff#23201b
surface-interactive#e6e2d9#2e2c27
surface-muted#ebe6dd#23201b
surface-raised#ffffff#2a2722
surface-subtle#f6f1e8#1e1b16
text#17140f#f6f1e8
text-muted#5a5650#b3afa7
text-subtle#716d67#98948d
whatsapp#1fbf5a#1fbf5a
whatsapp-highlight#3ee07c#3ee07c
whatsapp-hover#25d366#25d366
whatsapp-text#062612#062612