Embedding AI widgets (Shadow DOM)
A widget in a shadow root is isolated from the host page's CSS, but a <head> stylesheet cannot reach it either. Pass the root:
ts
import { BrowserRuntime } from '@barocss/browser';
const host = document.querySelector('ai-widget')!;
const root = host.attachShadow({ mode: 'open' }); // 'closed' works too: the embedder holds the reference
root.innerHTML = modelHtml;
const runtime = new BrowserRuntime({ root, config }); // or baroStart({ root, config })
// later: runtime.destroy() when the widget is removedCall runtime.destroy() when the widget unmounts (for example in a custom element's disconnectedCallback). Otherwise a removed host keeps its rule references and its shared-sheet registry entry, so those rules are never reclaimed.
- The runtime observes
root(with an initial scan) and puts all of its CSS inside it: utilities, theme variables (:root,:host),@property,@keyframesand preflight. The host page's styling is not changed. @propertyis also registered in the document (#384). Browsers ignore@propertyinside a shadow root, and without the registrations gradients (bg-linear-*/bg-gradient-*withfrom-*/to-*),shadow-*,ring-*andtranslate-*compute tonone. So the runtime copies only its@propertyrules into one shared document sheet: by default one constructable sheet indocument.adoptedStyleSheets(#442; no option needed, so a strict CSP such asstyle-src 'self'needs no nonce, and other adopted sheets are kept); only where the browser lacksdocument.adoptedStyleSheets, a<style data-barocss="document-properties">in<head>carrying yournonce. Every root and runtime on the page shares it, each rule is added once, and new utilities add their rules later. No utilities, theme variables or preflight go there.@propertyonly declares a custom property's type and initial value (the--baro-*names), so it cannot restyle host elements. The registrations stay afterdestroy(): they are global, another root may use them, and removing them would change elements that still do.- If the document refuses the sheet (no document access, inserting it throws, or no adopted-sheet support and no
nonce), the runtime instead puts the initial values of those properties in the root, as a first@layer propertiesblock on:host, *, ::before, ::after, ::backdrop, which supplies initial values without document registration. In the Chromium held-out corpus after #445, both the normal document-registration path and the forced fallback match all 575 classes, includingvia-[rgb(0_0_255)]/(--o)with an unset alpha variable. The corrected fallback probe has not been rerun in Firefox or WebKit. - Preflight is scoped to the root.
html/:rootselectors become:host.bodyrules are dropped and their declarations are re-emitted last on:host, withoutmin-height: 100vhandscroll-behavior. So the widget gets the preflight font (it no longer inherits the host'sfont-family) and border reset, as in a Tailwind 4 build. Like Tailwind, preflight does not setcolor, so the host's text colour still inherits into the widget unless you set one (for exampletext-gray-900on the widget's wrapper). - Shared sheets. Runtimes with the same config (and prefix) share one constructable stylesheet that every root adopts through
root.adoptedStyleSheets. Each class is generated once, whichever root uses it first. Rules keep Tailwind's variant order . GC counts per root and across roots: a rule is deleted only when no root still uses its class.runtime.getStats().sharedSheetreports roots, rules and generations of the shared sheet. - Fallback. Without constructable stylesheets, each root gets two
<style data-barocss>elements (prologue and rules) at its start, which mirror the same shared rule list. insertionPoint,styleIdandmaxRulesPerPartitiondo not apply in this mode, and a server-rendered<style data-barocss-ssr>sheet is adopted only in document mode.rootmust be aShadowRoot(ordocument, which is the normal document mode). For a widget in a plain<div>, use the document mode (getRuntime().observe(container)): the host's CSS and the widget's CSS then cascade together, so use a shadow root when you need isolation.- Your site's own CSS does not reach the root. This is how the platform works, not something the runtime can change: a document stylesheet (for example base rules such as
h1, h2, h3 { font-family: var(--font-display) }, link colors, or a.prosecomponent) does not apply inside a shadow root. BaroCSS generates utilities, theme variables and preflight, but it does not know your hand-written CSS. Ship that CSS into the root yourself: add<link rel="stylesheet" href="/site-base.css">to the root, orroot.adoptedStyleSheets = [...root.adoptedStyleSheets, siteSheet]. The runtime keeps a stylesheet you add, never removes it, and places its own sheets before it. Keep your base rules in@layer base(or at element specificity) so that utilities still win. - Inherited host properties still cross the boundary. CSS inheritance goes through shadow roots, so inherited properties the widget doesn't set itself (
color,letter-spacing,line-height,font-*,text-align, …) come from the host element. That's the platform, not BaroCSS: in the #364 measurement a hostile host'sletter-spacingreached the widget identically with BaroCSS and with a prebuilt Tailwind sheet linked in the root, and preflight doesn't resetletter-spacing. A host rule that matches the widget's host element itself (e.g.div { padding }when the host is a<div>) also applies to that box. Options::host { all: initial }in a sheet you add to the root. It cuts every inherited value in one line, but it also resets things the widget wants (the host box becomesdisplay: inline, fonts and colours fall back to browser defaults), so re-setdisplayand your base typography after it. A host rule on the host element still beats:host.- A targeted reset on the widget's root element, e.g.
tracking-normal leading-normal text-gray-900(plus afont-*if you want a fixed font). It sets only what the widget cares about and is the least surprising choice. - Leave it inherited when the widget should follow the host's theme (text colour, font). Then document which properties the widget expects the host to provide.