Root

import "@taprootio/espalier/root";

<esp-root>

Root container for the Espalier design system.

Provides the full design-token pipeline — colors, scales, and fonts — to every descendant component. All tokens are JS-computed from a pair of Base64-encoded theme objects and emitted as CSS custom properties on the host element.

Theme API

Supply a light-theme and/or dark-theme attribute containing a Base64-encoded JSON partial theme. Fields you omit fall back to built-in defaults. Switch between them with scheme.

Nested theme previews and scoped events

A nested root creates a scoped theme for its descendants. It does not create a separate event bus: every root publishes theme changes through the same Espalier singleton. Subscribe through the root whose changes you own so a nested preview cannot be mistaken for an application-level theme change.

const applicationRoot = document.querySelector<EspalierRoot>("#application-theme")!;

const unsubscribe = applicationRoot.subscribeScoped("scheme-changed", ({ scheme }) => {
  localStorage.setItem("scheme", scheme);
});

// Initial state is read directly; scoped events report later changes only.
renderScheme(applicationRoot.scheme);

// Call when the owning integration is disposed.
unsubscribe();

This contract covers scheme-changed, seed-color-changed, theme-changed, and icon-sprite-url-changed. Components derived from EspalierElementBase use the same closest-root filter internally.

The root also owns the application's banner-texture vocabulary: EspalierRoot.registerTexture(name, definition) registers an image-backed texture preset that any esp-image banner in the application selects with texture="<name>", and EspalierRoot.registeredTextures() enumerates the presets for building texture pickers.

Slots

<esp-root> has a slot:

Default

Place Espalier components and content here.

Attributes

<esp-root> has the following attributes:

light-theme

light-theme has a default value of "".

Base64-encoded JSON partial theme for the light scheme.

Only fields you wish to override need to be present — all others inherit from the built-in light defaults.

dark-theme

dark-theme has a default value of "".

Base64-encoded JSON partial theme for the dark scheme.

font-css-root

font-css-root has a default value of "/css/fonts/".

Root path for locally-cached font CSS files used by <esp-font-picker> to load font previews.

The directory must contain the individual font CSS files generated by npm run build-fonts. These files must be served at stable, predictable paths (no filename hashing) because the picker constructs URLs by font family name.

font-definitions-url

font-definitions-url has a default value of "".

Full URL to the font-definitions.json catalog used by <esp-font-picker>.

When set, the picker fetches from this URL instead of {fontCSSRoot}font-definitions.json. This allows the JSON to be served through a cache-busting asset pipeline while the individual font CSS files remain at stable paths.

google-font-loading

google-font-loading has a default value of "auto".

Controls whether Espalier may inject runtime Google Fonts CDN stylesheet links for selected Google Fonts.

auto preserves the default behavior: theme font families and selected Google font-picker values may load full fonts from fonts.googleapis.com. none prevents those runtime Google Fonts links, leaving local preview CSS and web-safe font stacks available for locked-down, offline, or strict-CSP environments. Static generators should compile the resolved light/dark themes with compileFontPlan(), embed its CSS in the page head, and set this policy to none; the predeclared target and metric-matched fallback faces then participate in first layout without any runtime catalog request.

icon-sprite-url

icon-sprite-url has a default value of "/assets/icons.svg".

Full URL to the SVG sprite used by component-owned icon attributes.

Components append the validated icon name as a fragment, so this should point to the sprite file itself, without #icon. Use this when your application serves the sprite from a subpath, CDN, or cache-busted asset pipeline. Set it to an empty string to disable generated sprite icon URLs for this root.

theme-pending

theme-pending has a default value of false.

Hold descendant paint until the first author theme resolves (ESP0177) — opt in when the theme is assigned from script.

A script-mounted theme lands after the elements upgrade, so without this the page paints one frame on the default palette and then repaints on the brand's. Declaring theme-pending keeps the subtree invisible (it still takes its space, so nothing shifts) until the theme applies.

Server-rendered light-theme / dark-theme attributes close a different window: the theme is present when the root upgrades, so it settles on its first update and never renders the defaults. They do not cover the frame before this module runs, when the host is still an unknown element painting its light DOM unstyled — a server-rendered page whose bundle may load after first paint wants this attribute too, plus the page-level gate, and its hold releases the moment it upgrades.

The hold always ends: if no theme arrives within THEME_SETTLE_TIMEOUT_MS the root reveals anyway and warns, so a broken mount degrades to the default palette instead of a blank page.

default-scheme

default-scheme has a default value of "light".

Initial color scheme to apply when scheme is not set.

Use light or dark for a fixed published-site default, or system to follow the visitor's prefers-color-scheme before they toggle the active scheme.

scheme

scheme has a default value of "light".

The active color scheme.

Light scheme

Dark scheme

Methods

<esp-root> has the following methods:

registerTexture

Register (or replace) a named, image-backed banner-texture preset. Any esp-image banner under a root selects it with texture="<name>"; banners already pointing at the name re-render, so registration at mount and markup order never race. Static delegate to registerImageTexture so script-only contexts (docs demos, published sites) can reach it from the root element without module imports.

registeredTextures

Snapshot of the registered texture presets, for building pickers.

subscribeScoped

Subscribe to a theme event published by this root only.

All roots share Espalier's global bus. This method filters foreign and nested-root payloads by correlationId and returns a cleanup function. It reports changes after mount; read initial state from this root.

whenThemeSettled

Resolves when themeSettled turns true — the honest moment to judge whether a named context is genuinely missing rather than merely not mounted yet.

CSS Properties

<esp-root> has the following CSS properties:

--esp-font-body

Always-emitted family for body and UI text. Defaults to system-ui, sans-serif when fontBody is omitted or empty.

--esp-font-headings

Consumer-authored heading family. Emitted only when configured; heading recipes otherwise fall back to --esp-font-body.

--esp-font-brand

Consumer-authored brand family. Emitted only when configured; brand recipes otherwise fall back through headings to --esp-font-body.

--esp-font-menu

Consumer-authored navigation family. Emitted only when configured; menu items otherwise fall back to --esp-font-body, never to headings.

--esp-font-weight-menu

Font weight for horizontal navigation menu items. Defaults to bold.

--esp-font-monospace

Family for code and monospace text. Defaults to monospace.

--esp-color-series-1

First categorical data-series color.

--esp-color-series-2

Second categorical data-series color.

--esp-color-series-3

Third categorical data-series color.

--esp-color-series-4

Fourth categorical data-series color.

--esp-color-series-5

Fifth categorical data-series color.

--esp-color-series-6

Sixth categorical data-series color.

--esp-color-series-7

Seventh categorical data-series color.

--esp-color-series-8

Eighth categorical data-series color.

--esp-color-series-1-wash

First series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-1-tint

First series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-1-ink

First series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-2-wash

Second series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-2-tint

Second series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-2-ink

Second series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-3-wash

Third series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-3-tint

Third series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-3-ink

Third series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-4-wash

Fourth series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-4-tint

Fourth series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-4-ink

Fourth series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-5-wash

Fifth series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-5-tint

Fifth series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-5-ink

Fifth series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-6-wash

Sixth series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-6-tint

Sixth series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-6-ink

Sixth series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-7-wash

Seventh series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-7-tint

Seventh series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-7-ink

Seventh series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-series-8-wash

Eighth series washed toward the local background (75% identity in OKLab) for large fills; theme zones re-emit it against their own canvas.

--esp-color-series-8-tint

Eighth series tinted over the local background (12% identity in OKLab) for grounds behind ordinary text; theme zones re-emit it against their own canvas.

--esp-color-series-8-ink

Eighth series pushed to the text contrast tier against the local background (best-contrast pole when a mid-lightness surface makes the tier unreachable).

--esp-color-type-overline

Overline ink enforced to Lc 90 against the local background.

--esp-color-type-label

Label ink enforced to Lc 75 against the local background.

--esp-color-type-lead

Lead-paragraph ink enforced to Lc 75 against the local background.

--esp-color-type-caption

Caption ink enforced to Lc 90 against the local background.

--esp-type-overline-font-size

Fluid font size for overline text.

--esp-type-overline-font-weight

Font weight for overline text.

--esp-type-overline-letter-spacing

Letter spacing for overline text.

--esp-type-overline-text-transform

Text transform for overline text.

--esp-type-overline-color

Context-local color alias for overline text.

--esp-type-label-font-size

Fluid font size for label text.

--esp-type-label-font-weight

Font weight for label text.

--esp-type-label-letter-spacing

Letter spacing for label text.

--esp-type-label-color

Context-local color alias for label text.

--esp-type-lead-font-size

Fluid font size for lead paragraphs.

--esp-type-lead-font-weight

Font weight for lead paragraphs.

--esp-type-lead-letter-spacing

Letter spacing for lead paragraphs.

--esp-type-lead-color

Context-local color alias for lead paragraphs.

--esp-type-caption-font-size

Fluid font size for captions.

--esp-type-caption-font-weight

Font weight for captions.

--esp-type-caption-letter-spacing

Letter spacing for captions.

--esp-type-caption-color

Context-local color alias for captions.

--esp-type-display-font-family

Heading font family for opt-in display text.

--esp-type-display-font-size

Fluid display size spanning the large mobile floor through the huge desktop ceiling.

--esp-type-display-font-weight

Heading font weight for opt-in display text.

--esp-type-display-line-height

Line height for opt-in display text.

--esp-type-display-letter-spacing

Letter spacing for opt-in display text.

--esp-type-display-color

Context-local heading-color alias for opt-in display text.

Components API Guides Getting started Styling Espalier Browser support GitHub npm package Taproot I/O