Symptom
Components emitted by @a2ui/react/v0_9 render with no padding, border, color, or hover state when used via the published npm tarball (@a2ui/react@0.9.1, sha ea8e3a3c7d3dca12921cb230ef06144d95454098). Three components reproduce:
<Button> → <button class="undefined undefined">label</button>
<TextField> → <div class="undefined"><input class="undefined" /></div>
<ChoicePicker> → similar empty class refs
<Card>, <Column>, <Row>, <Text>, <Icon>, <Image>, <Divider>, <CheckBox>, <Slider> are unaffected — they use inline-style objects in source (no .module.css import) or rely on the .a2uiText / .a2uiCaption plain class names that DO ship in v0_9/index.css.
Reproduction (minimal, deterministic)
mkdir /tmp/a2ui-repro && cd /tmp/a2ui-repro
npm init -y
npm install @a2ui/react@0.9.1
grep -A6 'var Button_default' node_modules/@a2ui/react/v0_9/index.js | head -8
Output (verified 2026-04-30 against the published tarball):
// src/v0_9/catalog/basic/components/Button.module.css
var Button_default = {};
var Button = createComponentImplementation(ButtonApi, ({ props, buildChild }) => {
useBasicCatalogStyles();
const classes = [Button_default.button];
if (props.variant === "primary") {
classes.push(Button_default.primary);
Button_default is the JS-side object that should hold the CSS-module-hashed class names from Button.module.css (e.g. {button: "Button-button-abc123", primary: "Button-primary-def456"}). It's empty in the published tarball, so classes.join(" ") produces the literal string "undefined undefined". Same applies to TextField_default and ChoicePicker_default (grep them in the same file to confirm).
The published package.json exports "./styles/structural.css": "./structural.css", but find node_modules/@a2ui/react -name structural.css returns nothing — the file isn't actually shipped in the tarball. node_modules/@a2ui/react/v0_9/index.css exists but contains only .a2uiText / .a2uiCaption rules (no .a2uiButton, .a2uiPrimary, .a2uiBorderless, etc.).
Why the official samples/client/react/shell/ works (and users see broken output)
samples/client/react/shell/package.json declares:
"@a2ui/react": "file:../../../../renderers/react"
This is a file: dep pointing at local source, so Vite handles .module.css natively at build time and class names get populated correctly. The shell sample never goes through the npm publish/install path, so its visual parity tests don't catch this regression. Users running npm install @a2ui/react get the broken bundle.
The shell's App.css ships zero [data-a2ui-surface] button rules — confirms the renderer is supposed to be self-styled by its CSS modules. There's no documented "you must also import @a2ui/react/styles/..." step that would compensate.
Source vs published bundle (root cause)
Source: renderers/react/src/v0_9/catalog/basic/components/Button.tsx
import styles from './Button.module.css';
export const Button = createComponentImplementation(ButtonApi, ({props, buildChild}) => {
useBasicCatalogStyles();
const classes = [styles.button];
if (props.variant === 'primary') classes.push(styles.primary);
// ...
});
The source import styles from './Button.module.css' is preserved as var Button_default in the bundle, but the build pipeline (tsup per renderers/react/package.json scripts.build) is publishing it as {} — the module's exports aren't being captured. CheckBox/Slider work because their source uses const containerStyle = {...} inline objects rather than CSS-module imports.
Related issues
Workaround for users
Two options:
-
Use file: dep like the official shell sample (requires git clone https://github.com/google/A2UI.git + building locally on each consumer machine):
"@a2ui/react": "file:../path/to/A2UI/renderers/react"
-
Element-selector CSS shim scoped to the surface wrapper. Doesn't reproduce SDK branding exactly but restores baseline visual feedback:
[data-a2ui-surface] button { padding: 0.5rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; ... }
[data-a2ui-surface] input,
[data-a2ui-surface] textarea { padding: 0.5rem 0.75rem; border: 1px solid var(--border); ... }
Environment
@a2ui/react@0.9.1 (latest published; 0.9.0, 0.9.0-alpha.3/4 show same shape per npm view)
@a2ui/web_core@0.9.2
@a2ui/markdown-it@0.0.3
- Next.js 16 + React 19 + Tailwind v4 host application
- Node 20.20.2 / npm 10
This report was prepared with AI assistance (Claude Code). Repro commands and source/bundle excerpts above were executed against the public tarball and the v0.9 source on main.
Symptom
Components emitted by
@a2ui/react/v0_9render with no padding, border, color, or hover state when used via the published npm tarball (@a2ui/react@0.9.1, shaea8e3a3c7d3dca12921cb230ef06144d95454098). Three components reproduce:<Button>→<button class="undefined undefined">label</button><TextField>→<div class="undefined"><input class="undefined" /></div><ChoicePicker>→ similar empty class refs<Card>,<Column>,<Row>,<Text>,<Icon>,<Image>,<Divider>,<CheckBox>,<Slider>are unaffected — they use inline-style objects in source (no.module.cssimport) or rely on the.a2uiText/.a2uiCaptionplain class names that DO ship inv0_9/index.css.Reproduction (minimal, deterministic)
Output (verified 2026-04-30 against the published tarball):
Button_defaultis the JS-side object that should hold the CSS-module-hashed class names fromButton.module.css(e.g.{button: "Button-button-abc123", primary: "Button-primary-def456"}). It's empty in the published tarball, soclasses.join(" ")produces the literal string"undefined undefined". Same applies toTextField_defaultandChoicePicker_default(grep them in the same file to confirm).The published
package.jsonexports"./styles/structural.css": "./structural.css", butfind node_modules/@a2ui/react -name structural.cssreturns nothing — the file isn't actually shipped in the tarball.node_modules/@a2ui/react/v0_9/index.cssexists but contains only.a2uiText/.a2uiCaptionrules (no.a2uiButton,.a2uiPrimary,.a2uiBorderless, etc.).Why the official
samples/client/react/shell/works (and users see broken output)samples/client/react/shell/package.jsondeclares:This is a
file:dep pointing at local source, so Vite handles.module.cssnatively at build time and class names get populated correctly. The shell sample never goes through the npm publish/install path, so its visual parity tests don't catch this regression. Users runningnpm install @a2ui/reactget the broken bundle.The shell's
App.cssships zero[data-a2ui-surface] buttonrules — confirms the renderer is supposed to be self-styled by its CSS modules. There's no documented "you must also import@a2ui/react/styles/..." step that would compensate.Source vs published bundle (root cause)
Source:
renderers/react/src/v0_9/catalog/basic/components/Button.tsxThe source
import styles from './Button.module.css'is preserved asvar Button_defaultin the bundle, but the build pipeline (tsupperrenderers/react/package.jsonscripts.build) is publishing it as{}— the module's exports aren't being captured. CheckBox/Slider work because their source usesconst containerStyle = {...}inline objects rather than CSS-module imports.Related issues
[ci] npm publishing manifest format is malformed, opened 2026-04-29) — confirms the publishing pipeline is currently broken; manifest lists@a2ui/react: 0.9.1and@a2ui/web_core: 0.9.2. Likely the upstream root cause but a different symptom.Component Style Overrides Missing in React Renderer) — feature request for override API; orthogonal to this rendering bug.Wire up theme in React renderer basic catalog) — about--a2ui-primary-colortheme wiring; separate concern.Publish @a2ui/react to npm) — still open as P1, suggesting stable v0.9 publish hasn't formally landed.Workaround for users
Two options:
Use
file:dep like the official shell sample (requiresgit clone https://github.com/google/A2UI.git+ building locally on each consumer machine):Element-selector CSS shim scoped to the surface wrapper. Doesn't reproduce SDK branding exactly but restores baseline visual feedback:
Environment
@a2ui/react@0.9.1(latest published;0.9.0,0.9.0-alpha.3/4show same shape pernpm view)@a2ui/web_core@0.9.2@a2ui/markdown-it@0.0.3This report was prepared with AI assistance (Claude Code). Repro commands and source/bundle excerpts above were executed against the public tarball and the v0.9 source on
main.