packages/a2ui-renderer/src/react-renderer/styles/README.md
The React renderer uses Light DOM (regular HTML elements), unlike the Lit renderer which uses Shadow DOM with built-in style encapsulation. This means all CSS lives in the global document scope and must be carefully organized to avoid conflicts with host-app stylesheets.
All styles are injected into a single <style id="a2ui-structural-styles">
element in the document head via injectStyles().
reset.ts)@layer a2ui-reset {
:where(.a2ui-surface) :where(*) {
all: revert;
}
}
Purpose: Restores browser-default styles (heading margins, list styles,
form appearance, etc.) inside .a2ui-surface. Without this, host-app CSS
resets like Tailwind preflight strip defaults that the renderer expects.
Why it works: CSS @layer styles have the lowest author-level priority.
Every other A2UI style is unlayered and automatically overrides the reset.
The :where() wrapper also zeroes specificity for extra safety.
Why it's needed: The Lit renderer doesn't need this because Shadow DOM isolates its elements from external stylesheets. The React renderer uses Light DOM, so host-app resets leak in.
structuralStyles)Imported from: @a2ui/lit/0.8 → Styles.structuralStyles
Transform: :host { ... } → .a2ui-surface { ... }
Utility classes shared between all renderers, generated from web_core:
| Prefix | Source file | Examples |
|---|---|---|
layout-* | styles/layout.ts | layout-p-2, layout-m-0, layout-w-100 |
typography-* | styles/type.ts | typography-f-sf, typography-sz-tl |
color-* | styles/colors.ts | color-c-n100, color-bgc-p30 |
border-* | styles/border.ts | border-br-12, border-bw-1 |
behavior-* | styles/behavior.ts | behavior-ho-70 |
opacity-* | styles/opacity.ts | opacity-50 |
Specificity: Single class selector — (0,1,0).
These classes are applied to HTML elements via the theme's class maps
(e.g. { 'layout-p-2': true, 'color-bgc-p30': true }), which components
convert to className strings using classMapToString().
componentSpecificStyles)Hand-written CSS that replicates each Lit component's static styles.
Covers host-level layout (display, flex) and element-level defaults.
Two specificity tiers:
Host styles — .a2ui-surface .a2ui-{component} — specificity (0,2,0)
.a2ui-surface .a2ui-card {
display: block;
flex: var(--weight);
}
Element styles — :where(.a2ui-surface .a2ui-{component}) element — specificity (0,0,1)
:where(.a2ui-surface .a2ui-image) img {
display: block;
width: 100%;
}
:where() zeroes the wrapper specificity so that theme utility classes
(specificity (0,1,0)) can override element defaults.
theme.components.*)Applied per-component as CSS class names. The theme object provides
Record<string, boolean> maps that reference utility classes from layer 2.
// From the theme object
Button: { 'color-bgc-p30': true, 'color-c-n100': true, ... }
Components merge these with classMapToString() and apply as className:
<button className={classMapToString(theme.components.Button)}>
Specificity: Same as utility classes — (0,1,0).
theme.elements.*)Applied to raw HTML elements rendered inside components (e.g. <h1>,
<input>, <button> inside Text, TextField, Button). Same mechanism
as component class maps.
theme.additionalStyles.*)Applied as inline styles via React's style prop. Used for CSS custom
properties and direct property overrides.
// Theme definition
additionalStyles: {
Button: { '--n-35': 'var(--n-100)' },
Card: { padding: '32px' },
}
// Component application
<button style={stylesToObject(theme.additionalStyles?.Button)}>
Specificity: Inline styles — (1,0,0). Always wins over class-based styles.
Components set --weight and other layout vars as inline styles based on
component properties:
<div className="a2ui-card" style={{ '--weight': node.weight }}>
Specificity: (1,0,0) — same as additionalStyles.
The renderer expects color palette variables to be defined by the host
application on :root or a parent element. These are consumed by the
utility classes:
/* Neutral */ --n-0 through --n-100, --nv-0 through --nv-100
/* Primary */ --p-0 through --p-100
/* Secondary */ --s-0 through --s-100
/* Tertiary */ --t-0 through --t-100
/* Error */ --e-0 through --e-100
In the Lit renderer, @copilotkit/a2ui-renderer bundles these internally.
For the React renderer, the host app must provide them (e.g. via
a2ui-palette.css).
Highest ──┐
│ Inline styles (additionalStyles, --weight) (1,0,0)
│ Theme utility classes / element classes (0,1,0)
│ Component host styles (.a2ui-surface .a2ui-*) (0,2,0)
│ Component element styles (:where(...) elem) (0,0,1)
│ Structural utility classes (0,1,0)
│ Browser default reset (@layer a2ui-reset) layered
Lowest ───┘
Note: Component host styles have higher specificity (0,2,0) than utility
classes (0,1,0), but they only set structural properties (display,
flex, overflow) that theme utilities don't typically target — so there
is no conflict in practice.
| File | Purpose |
|---|---|
reset.ts | all: revert in @layer — restores browser defaults |
index.ts | Structural utilities, component CSS, injection logic |