Configuration
One plain object describes everything you can configure. Apply it as CSS for the whole app, or as a scope for one region: a tenant's portal, an embedded widget, a preview. Overlays opened inside a scope keep its configuration.
Loading the live example…
The configuration
The palette, font, weight and cut become the ten source tokens (see Theming); density, theme and motion become the mode attributes (see Modes). The helpers ship in the keyline-config item:
The whole app
For a configuration that never changes at runtime, write the tokens as CSS and set the mode attributes on <html>. Generate the CSS with configToCss, or write it by hand: it is plain custom properties, unlayered, after Tailwind.
One region
KeylineConfigScope renders a div with data-kl-config, the mode attributes and the tokens as inline style. Everything inside it, and every overlay opened from inside it, takes the configuration.
Without the component, the same scope is an attribute and a rule. Derived colours are recomputed on any element with data-kl-config, so the palette tokens have to sit on one:
KeylineConfigScope
Theme "system" sets no class, so the scope follows the page. A scope's density is always written, so a default-density scope inside a compact page is default.
Helpers
Everything below is exported from @/components/keyline/lib/keyline-config. Use them to build a theme from settings your users choose: a tenant's brand colour, a density preference, a saved configuration.
Portals
Menus, selects, popovers, date panels, dialogs, sheets, tooltips and toasts render in a portal under <body>. Each leaves a hidden marker where it is written and, when it opens, copies that place's resolved configuration onto its own root: the ten source tokens, .dark or .light, density, motion and dir. It re-reads while open, so a theme switch reaches an open menu. There is no provider.
All overlays share one level (--kl-z-overlay) and stack in the order they open, so a select inside a dialog opens above it. Floating toasts sit above them on --kl-z-toast.
Your own overlay
An overlay you build yourself carries the configuration the same way, with useKeylineScope and scopeProps from the keyline-scope item. Render the marker where the component is written, the probe inside the portal, and spread scopeProps(scope) on the portaled root:
useKeylineScope(open, container) returns [marker, scope, probe]. With open true it reads after each render of its owner; with false the probe reads only while the portal is mounted. Pass container when the portal is not under <body>. Keyline's own overlays already do this; use it only for one of yours.