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.