@adia-ai/web-components

中文文档
0.8.66发布于 2026/09/15TypeScriptES Module近 30 天下载 3031

AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui.

原始使用文档
文档目录15 节
  1. Install
  2. Using with AI agents (Claude Code / Cursor / Windsurf / Cla…
  3. CDN — no bundler (CodePen, marketing pages, static HTML)
  4. Quick start
  5. Layout
  6. Using a component (consumer guide)
  7. Authoring a component
  8. static template is a closure — import what it references
  9. Card-n / drawer-ui composition parity
  10. A2UI runtime
  11. Data streaming via data-stream-* attributes
  12. Build
  13. Themes, density, scale
  14. Dependency direction
  15. License

@adia-ai/web-components

Vanilla web components for AdiaUI. ~130 light-DOM custom elements (ls components | wc -l is the census) + 56 composable traits, a reactive core (signals + tagged-template renderer), form-associated form controls, and integration into the A2UI generation pipeline.

This package ships UI atoms only. Composite shells (admin / chat / editor / simple / theme) live in @adia-ai/web-modules. The A2UI protocol runtime lives in @adia-ai/a2ui; the generation pipeline + pattern corpus in @adia-ai/gen-ui; the MCP server in @adia-ai/mcp (its gen-ui surface).

Consumer guide: USAGE.md — property reactivity, event contract, form participation, lifecycle, registration, TypeScript. Start here if you're integrating AdiaUI into an app.

Live demos for every component at https://ui-kit.exe.xyz/site/components/<name> — <name> is the tag without the -ui suffix (e.g. button, select, slider).

Install

npm install @adia-ai/web-components

For composite shells (admin / chat / editor / simple / theme clusters), pair with @adia-ai/web-modules:

npm install @adia-ai/web-components @adia-ai/web-modules

Using with AI agents (Claude Code / Cursor / Windsurf / Claude Desktop)

When composing UI with an AI coding agent, load the adia-ui-kit skill in the agent's harness. It encodes:

  • The full component catalog (which *-ui tag does what + when to use it)
  • Composition patterns (admin shell, dashboards, forms, chat, editor surfaces)
  • Pre-flight manifest gate (catches "imports without declared deps" before authoring)
  • Plan-Execute-Verify discipline (render the result in a real demo to verify)

The skill lives at .agents/skills/adia-ui-kit/ in the repo; activation phrases include "build a page with AdiaUI", "compose this UI", "wire these components", "use AdiaUI components to compose…", and "what's the pattern for an admin app / dashboard / form / chat surface". Without the skill, an agent works against this README; with the skill, it has the reasoning ladder + catalog + recipes for every component.

CDN — no bundler (CodePen, marketing pages, static HTML)

Since v0.6.30, this package ships pre-flattened + minified bundles under dist/ that work via jsdelivr/unpkg with zero build tooling. Drop these into any HTML page:

<!-- CSS: all components, tokens, resets (443 KB raw / ~50 KB gzipped) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@adia-ai/[email protected]/dist/web-components.min.css">

<!-- JS: registers every component (~250 KB gzipped via Brotli) -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@adia-ai/[email protected]/dist/web-components.min.js"></script>

The @0.8 range tracks the latest 0.8.x patch automatically (won't jump to a breaking 0.9). For reproducible builds, pin an exact version instead — e.g. @adia-ai/[email protected]/dist/web-components.min.css.

For composite shells, add the corresponding bundle from @adia-ai/web-modules — see its README or the CDN usage guide. The kitchen-sink path is @adia-ai/web-modules/dist/everything.min.js (all components + all 4 shells; ~424 KB gzipped) — one tag for CodePen demos.

Pick ONE bundle path. Mixing (e.g. everything.min.js + a separate web-components.min.js) causes customElements.define to throw "name already defined" on dup-load. The choice tree:

  • HTML-first / CodePen / marketing → @adia-ai/web-modules/everything (one tag covers everything)
  • Components only, no shells → @adia-ai/web-components/js/bundled
  • Vite/webpack/esbuild → existing root export (import '@adia-ai/web-components'); the */bundled exports are additive — bundler consumers see no change.

Quick start

ESM-only — bundlers (Vite, esbuild, webpack 5+, Rollup) resolve import for both .js and .css; in plain HTML use <script type="module"> + <link rel="stylesheet">.

import '@adia-ai/web-components';          // registers every *-ui tag
import '@adia-ai/web-components/css';      // every component's CSS (one stylesheet)

Or per-component for tree-shaking-conscious bundles:

import '@adia-ai/web-components/components/button';        // <button-ui> only
import '@adia-ai/web-components/components/button.css';    // its CSS

Then use the tags in markup:

<card-ui>
  <header>
    <span slot="icon"><avatar-ui icon="user"></avatar-ui></span>
    <span slot="heading"><text-ui strong>Hello</text-ui></span>
  </header>
  <section>Composition works out of the box — no framework.</section>
</card-ui>

The package sideEffects entry keeps the import above from being tree-shaken; importing index.js side-effect-registers every component.

Layout

web-components/
├── core/           — Reactivity + base classes
│   ├── signals.js       signal() / computed() / effect() / batch() / untracked()
│   ├── template.js      tagged templates: html, css, repeat, stamp
│   ├── element.js       UIElement — light-DOM reactive base
│   ├── form.js          UIFormElement — form-associated + ElementInternals + validation
│   ├── provider.js      global provider registration + router-ui
│   ├── anchor.js        popover + anchor-positioning
│   ├── markdown.js      lightweight markdown renderer
│   ├── transport.js     SSE / streaming helpers for LLM adapters
│   └── data-stream.js   `data-stream-*` attribute trait (HTTP/SSE/WS,
│                        signal-backed, refcounted shared transports)
│
├── components/     — ~130 *-ui custom elements (components)
│   └── <tag>/
│       ├── <tag>.js       class definition (extends UIElement / UIFormElement)
│       ├── <tag>.css      @scope(tag-ui) two-block: tokens + styles
│       ├── <tag>.yaml     authoring contract (props, slots, events, examples)
│       └── <tag>.a2ui.json  generated — do NOT edit
│
│  Composite shells (admin-shell, chat-shell, editor-shell, simple-shell,
│  theme-panel, gen-root, a2ui-root) ship in the sibling
│  `@adia-ai/web-modules` package — see ADR-0012 for the three-tier
│  rationale.
│
├── traits/         — 56 composable behaviors via defineTrait() + the
│                     <traits-host> wrapper for raw-HTML declarative
│                     composition. Generated catalog at _catalog.json
│                     drives the MCP get_traits tool + per-trait demo
│                     pages. Full contract in .claude/docs/specs/traits.md.
│
└── styles/         — Global tokens and CSS layering
    ├── tokens.css              all --a-* design tokens
    ├── colors/                 primitives / semantics / scrims
    ├── typography.css, space.css, radius.css, shadow.css
    └── themes/*.css            8 themes (ocean, forest, sunset, …)

Build + dev utilities (including build-a2ui-data.mjs, qa-training.mjs, a2ui-to-html.cjs, mcp-call.cjs, mcp-pipeline.cjs, screenshot.cjs) live at the repo-root scripts/ directory rather than inside this package — they span the monorepo (MCP server, a2ui-corpus data, component catalog) and aren't scoped to web-components alone.

Using a component (consumer guide)

The complete consumer surface — property reactivity, event contract, form participation, lifecycle, registration, TypeScript — lives in USAGE.md. Start there if you're integrating AdiaUI into an app rather than authoring a new component.

Quick reference:

// Property reactivity — every prop is signal-backed
slider.value = 75;            // triggers re-render

// Event contract — CustomEvent with typed detail (since v0.4.5)
slider.addEventListener('change', (e) => console.log(e.detail.value));

// Form participation — 15 components extend UIFormElement
new FormData(document.getElementById('settings'));  // includes every <input-ui>, <select-ui>, etc.

// Lifecycle — UIElement provides connected/render/updated/disconnected hooks
class MyWidget extends UIElement {
  connected() { /* set up */ }
  render() { /* update DOM */ }
  disconnected() { /* tear down */ }
}

// Registration — conflict-safe via defineIfFree
import { defineIfFree } from '@adia-ai/web-components/core';
defineIfFree('my-widget', MyWidget);

TypeScript users: types ship in the package since v0.4.6. import type { UISlider, SliderChangeEvent } from '@adia-ai/web-components' for hand-typed handlers; HTMLElementTagNameMap is augmented so document.querySelector('slider-ui') returns UISlider.

Authoring a component

If you're contributing a new component (rather than consuming one), this is for you. Every component is a single-file class extending UIElement (or UIFormElement for form fields). All styling lives in a sibling .css file using two-block @scope:

@scope (button-ui) {
  :where(:scope) {
    /* Tokens only — zero specificity, parent overrides win */
    --button-bg:     var(--a-accent-bg);
    --button-fg:     var(--a-accent-fg);
    --button-radius: var(--a-radius);
  }
  :scope {
    /* Styles — consume component tokens only, never global directly */
    background: var(--button-bg);
    color:      var(--button-fg);
    border-radius: var(--button-radius);
  }
  :scope[variant="danger"] {
    --button-bg: var(--a-danger-bg);   /* variants override tokens, not styles */
  }
}

Authoring rules (enforced by ui-audit-coherence):

  1. No raw colors in component CSS — every color goes through a token.
  2. Variants change tokens, modes change layout. A selector that sets display, padding, flex, grid, etc. is a mode and needs a Sanctioned Mode Attributes entry in the contract doc.
  3. Boolean props default to false. If the expected default is "on", flip the name (closable → permanent).
  4. Event listeners in connected() have matching removeEventListener in disconnected(). Handlers are stable #field arrows, never inline.
  5. Light DOM only. No ::part(), ::slotted(), shadow roots. Use attribute selectors on children: :scope > [slot="icon"].

static template is a closure — import what it references

The static template property is a regular JavaScript closure (typically a tagged template literal — () => html followed by the template body). Any signal, variable, or function it references must be lexically in scope at the file where the class is defined. This is JavaScript scoping, not anything AdiaUI-specific — but it surprises authors coming from frameworks where templates magically receive props.

// ❌ WRONG — ReferenceError at runtime: minL is not defined
//   (no import; the template function can't see minL just because
//    it's exported elsewhere in the monorepo)
class MyPanel extends UIElement {
  static template = () => html`<div>L: ${minL.value}</div>`;
}

// ✅ RIGHT — import the signal locally
import { minL } from './state.js';

class MyPanel extends UIElement {
  static template = () => html`<div>L: ${minL.value}</div>`;
}

The error surfaces in the browser console as ReferenceError: minL is not defined at first render — not at module load, not at tsc --noEmit. The trace points at the template function body, not at the missing import, which is the disorienting part.

If your component needs external reactive state that varies per-instance, expose it as a property rather than reaching for module-scoped signals:

// ✅ Best — per-instance reactive prop
class MyPanel extends UIElement {
  static properties = {
    minL: { type: Number, default: 0 },
  };
  static template = el => html`<div>L: ${el.minL}</div>`;
}

// Consumer:
<my-panel .minL=${minL}></my-panel>  // signal binds reactively per ADR-template-binding

The el parameter is the element instance — every signal-backed property is reactively read.

Full authoring contract: .claude/docs/specs/component-token-contract.md. The adia-ui-authoring skill encodes the 20 non-negotiable rules.

Card-n / drawer-ui composition parity

Both accept bare <header> / <section> / <footer> tags OR explicit [slot="header|body|footer"]. Both activate a 3-column header grid when any direct [slot="icon|heading|description|action"] child is present (:has(> …) — direct-child only). Drawer supports multi-section bodies with sticky header/footer. See the drawer component page for worked examples.

A2UI runtime

The A2UI runtime lives in the sibling zero-dependency @adia-ai/a2ui package; this package is the component catalog its renderer resolves tags against:

import { A2UIRenderer } from '@adia-ai/a2ui';
import '@adia-ai/web-components';   // register the custom elements

const renderer = new A2UIRenderer(document.getElementById('canvas'));
renderer.process({
  type: 'updateComponents',
  components: [
    { id: 'root', component: 'Card', children: ['hdr', 'body'] },
    { id: 'hdr',  component: 'Header', slots: { heading: 'Generated UI' } },
    { id: 'body', component: 'Section', children: ['btn'] },
    { id: 'btn',  component: 'Button', attrs: { variant: 'primary' }, content: 'Click' },
  ],
});

Core message kinds: createSurface, updateComponents, updateDataModel, wireComponents, deleteSurface (plus updateStyles/removeStyles). The registry normalizes LLM-emitted aliases (e.g. Carousel → swiper-ui) so generated output is robust to name drift.

Data streaming via data-stream-* attributes

Any element with a settable .data property — chart-ui, table-ui, heatmap-ui, stat-ui, list-ui, etc. — can be fed from a backing source via attributes alone. No per-component opt-in:

<!-- HTTP one-shot fetch, JSON -->
<chart-ui type="area" x="month" y="revenue"
          data-stream-src="/api/revenue?range=3m"
          data-stream-path="data"></chart-ui>

<!-- HTTP polling every 5s -->
<table-ui sortable striped
          data-stream-src="/api/orders"
          data-stream-interval="5000"></table-ui>

<!-- Server-Sent Events, append on each message -->
<heatmap-ui type="matrix" rows="7" cols="52"
            data-stream-src="/sse/activity"
            data-stream-mode="sse"
            data-stream-merge="append"></heatmap-ui>

<!-- Spread a multi-property response onto the element -->
<stat-ui data-stream-src="/api/kpi"
         data-stream-target="*"></stat-ui>

Modes: HTTP (one-shot or polling), sse (EventSource), ws (WebSocket). Formats: json (default), csv, tsv, jsonl, text — auto-detected from URL extension or content-type. Two elements with attribute-identical streams share one transport (refcounted, signal-backed); explicit data-stream-id lets unrelated configs share intentionally. Programmatic access via the streams registry export from core/data-stream.js.

Implementation: core/data-stream.js (~360 lines). Full attribute table + live demos: /site/components/chart#data-stream.

Build

npm run build:components    # regenerate all .a2ui.json from YAML

The build also writes packages/gen-ui/a2ui/catalog/catalog-a2ui_1_0.json and catalog-a2ui_1_0_rules.txt — the flat-file catalog the MCP server and generation engine consume (ADR-0050: the catalog is @adia-ai/a2ui's contract).

Themes, density, scale

<div theme="ocean" density="compact" size="sm">
  …all descendants re-theme / re-densify / re-scale automatically…
</div>
  • [theme] — 8 themes: default, ocean, forest, sunset, lavender, rose, slate, midnight
  • [density] — compact (0.85×) · spacious (1.15×)
  • [size] — sm|md|lg shifts the entire typescale + component dimensions
  • [radius] — sharp (0) · rounded (1) · round (2)

Each is a CSS-variable override; no class toggles, no re-imports.

Dependency direction

gen-ui (compose/) ──reads──>  gen-ui (corpus/)  ←─reads──  web-components
mcp               ──reads──>  gen-ui

Web-components never imports from @adia-ai/gen-ui or @adia-ai/mcp. The A2UI renderer consumes a protocol, not a generator — anything that emits valid A2UI messages drives it.

License

MIT

版本与发布记录

@adia-ai/web-components 共发布 220 个版本,首次发布于 2026/04/21;过去 12 个月发布了 220 个版本。

版本发布日期距今
0.8.66latest2026/09/151 个月前
0.8.652026/09/141 个月前
0.8.642026/09/141 个月前
0.8.632026/09/131 个月前
0.8.622026/09/111 个月前
0.8.612026/09/111 个月前

关于 @adia-ai/web-components 的常见问题

@adia-ai/web-components 是什么?

AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui.

如何安装 @adia-ai/web-components?

在项目目录运行 npm install @adia-ai/web-components,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 adia-ui-doc、adia-ui-audit-template-children,可以用 npx adia-ui-doc 直接运行。

@adia-ai/web-components 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@adia-ai/web-components 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@adia-ai/web-components 可以商用吗?

@adia-ai/web-components 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@adia-ai/web-components 的下载量有多少?

根据 npm 官方下载统计,@adia-ai/web-components 在 2026/10/05 – 2026/11/03 这 30 天内被下载 3,031 次,日均约 101 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adia-ai/web-components 还在维护吗?

最新版本 0.8.66 发布于 2026/09/15(1 个月前);至今共发布 220 个版本,其中过去 12 个月发布了 220 个;首次发布于 2026/04/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。