Files
WealthySmart/docs/a2ui-theming-findings.md
Carlos Escalante 097fe9c4cf
All checks were successful
Deploy to VPS / deploy (push) Successful in 1m17s
Point sync-db at old-vps and add A2UI theming notes
The production SSH alias is old-vps; the placeholder "production"
alias does not exist. Also captures research findings on theming
the A2UI basic catalog without overriding component internals,
for later reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:02:58 -06:00

5.0 KiB
Raw Blame History

A2UI Theming Findings

Captured 2026-04-28 while researching how to beautify the table the agent renders via render_a2ui without overriding component internals.

TL;DR

  • A2UI v0.9 docs prescribe theming through a small set of documented top-level CSS variables declared on :where(:root) (zero specificity, so host CSS wins without !important).
  • Default theme uses light-dark() so tokens self-toggle. Recommended dark-mode coupling: add the documented a2ui-dark class on <html> whenever Tailwind's .dark is on; that flips color-scheme: dark and resolves every light-dark() to its dark branch.
  • In our app, this approach has near-zero visible effect. We render through @copilotkit/a2ui-renderer (React variant), not @a2ui/lit. Inspecting the compiled React renderer (node_modules/.pnpm/@copilotkit+a2ui-renderer@1.56.4/.../catalog/basic/components/*.mjs), every component uses hardcoded inline styles; the only CSS variable consumed across the whole basic catalog is --a2ui-primary-color.
  • This matches open issues google/A2UI#977 and #1285 ("React renderer parity is incomplete").

Documented token surface (Lit / Angular)

From tech_docs/A2UI/renderers/web_core/src/v0_9/basic_catalog/styles/default.ts:

  • Color palette: --a2ui-color-background, --a2ui-color-on-background, --a2ui-color-surface, --a2ui-color-on-surface, --a2ui-color-primary, --a2ui-color-primary-light/-dark/-hover, --a2ui-color-on-primary, --a2ui-color-secondary, --a2ui-color-secondary-light/-dark/-hover, --a2ui-color-on-secondary, --a2ui-color-input, --a2ui-color-on-input, --a2ui-color-border.
  • Borders: --a2ui-border, --a2ui-border-width, --a2ui-border-radius.
  • Typography: --a2ui-font-family-title, --a2ui-font-family-monospace, --a2ui-font-size, --a2ui-font-scale, --a2ui-font-size-xs..2xl, --a2ui-line-height-headings, --a2ui-line-height-body.
  • Spacing: --a2ui-grid-base, --a2ui-spacing-xs..xl.

Per-component tokens like --a2ui-card-background exist but the renderer README and the official theming guide explicitly frame them as catalog-internal, not stable spec. Avoid relying on them.

Dark-mode coupling

A2UI default uses light-dark(...) and supports forced modes via a2ui-light / a2ui-dark classes (see the :where(.a2ui-dark) block in default.ts). Best practice for a host that uses Tailwind .dark on <html>: toggle the matching a2ui-dark class in lockstep — one effect-driven useEffect, or add it directly to the root if the app is dark-only. No need to redeclare every token under .dark; the default rules already pick up the right branch through light-dark().

Footguns in v0.9

  • The agent-supplied theme payload (e.g. createSurface.theme.primaryColor) is not yet wired in the basic catalog. Tracked in google/A2UI#979 (Lit) and #977 (React). Don't expect agent-side theming to recolor anything in v0.9; only the host CSS does.
  • hintedStyles (h1h6 mappings) require all keys when overridden — see google/A2UI#602.
  • Tracking issues for the broader theming surface: google/A2UI#1083, #1118.

Why this won't move the table in our app

@copilotkit/a2ui-renderer@1.56.4 (React) is what <CopilotKit a2ui={{}}> plugs in. Its components apply colors and borders as inline style props (backgroundColor: "#fff", border: "1px solid #ccc", etc.). Inline styles can only be overridden with !important in author CSS, which contradicts the "don't take its freedom" goal. So the documented --a2ui-* token contract is currently a Lit-only surface for this catalog.

Practical options for this codebase

  1. Quick win: set --a2ui-primary-color: var(--primary) on :root. Real but small effect (primary buttons, links).
  2. Prompt-side polish: instruct the agent to compose richer A2UI structures — wrap the table in a Card, use usageHint: "h3" for the section title, insert a Divider, group columns with Row + Column weights — so the renderer's existing inline styles produce a cleaner result. No code change.
  3. Switch renderer: replace @copilotkit/a2ui-renderer with @a2ui/lit (or wait for @a2ui/react parity). Then every documented --a2ui-* token actually applies. Non-trivial migration.
  4. Custom catalog overrides: register replacement React components for Row/List/Card via createA2UICatalog; full control, most invasive, accepts ownership of those components forever.
  • tech_docs/A2UI/docs/guides/theming.md
  • tech_docs/A2UI/renderers/web_core/src/v0_9/basic_catalog/styles/default.ts
  • tech_docs/A2UI/renderers/lit/README.md §"CSS-based Basic Catalog Theming"
  • https://github.com/google/A2UI/issues/977 (React renderer theming gap)
  • https://github.com/google/A2UI/issues/1285 (React renderer parity)
  • https://github.com/google/A2UI/issues/1118 (theming surface roadmap)
  • https://github.com/google/A2UI/issues/979 (Lit theme payload not wired)
  • https://github.com/google/A2UI/pull/1079 (v0.9 CSS-variable rollout)