@adia-ai/web-components 0.6.47 → 0.6.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/components/badge/badge.d.ts +14 -0
  3. package/components/button/button.a2ui.json +1 -4
  4. package/components/button/button.d.ts +1 -1
  5. package/components/button/button.yaml +0 -3
  6. package/components/calendar-grid/calendar-grid.a2ui.json +5 -0
  7. package/components/calendar-grid/calendar-grid.class.js +8 -1
  8. package/components/calendar-grid/calendar-grid.css +20 -11
  9. package/components/calendar-grid/calendar-grid.d.ts +2 -0
  10. package/components/calendar-grid/calendar-grid.yaml +8 -0
  11. package/components/calendar-picker/calendar-picker.css +19 -10
  12. package/components/card/card.a2ui.json +2 -5
  13. package/components/card/card.css +3 -1
  14. package/components/card/card.d.ts +2 -2
  15. package/components/card/card.yaml +2 -5
  16. package/components/date-range-picker/date-range-picker.class.js +9 -0
  17. package/components/date-range-picker/date-range-picker.css +10 -1
  18. package/components/field/field.test.js +7 -2
  19. package/components/heatmap/heatmap.a2ui.json +2 -0
  20. package/components/heatmap/heatmap.d.ts +1 -1
  21. package/components/heatmap/heatmap.yaml +2 -0
  22. package/components/index.js +1 -0
  23. package/components/preview/preview.a2ui.json +93 -0
  24. package/components/preview/preview.class.js +178 -0
  25. package/components/preview/preview.css +176 -0
  26. package/components/preview/preview.d.ts +24 -0
  27. package/components/preview/preview.js +22 -0
  28. package/components/preview/preview.yaml +100 -0
  29. package/components/progress/progress.a2ui.json +2 -7
  30. package/components/progress/progress.d.ts +2 -2
  31. package/components/progress/progress.yaml +3 -8
  32. package/components/progress-row/progress-row.a2ui.json +1 -3
  33. package/components/progress-row/progress-row.d.ts +1 -1
  34. package/components/progress-row/progress-row.yaml +0 -2
  35. package/components/select/select.a2ui.json +2 -4
  36. package/components/select/select.yaml +2 -2
  37. package/components/tabs/tabs.a2ui.json +1 -4
  38. package/components/tabs/tabs.d.ts +2 -2
  39. package/components/tabs/tabs.yaml +2 -2
  40. package/core/anchor.js +5 -1
  41. package/dist/web-components.min.css +1 -1
  42. package/dist/web-components.min.js +75 -73
  43. package/index.css +6 -6
  44. package/package.json +1 -1
  45. package/styles/README.md +71 -36
  46. package/styles/api/layout.css +19 -0
  47. package/styles/api/sizing.css +225 -0
  48. package/styles/api/text.css +106 -0
  49. package/styles/colors/semantics/aliases.css +32 -0
  50. package/styles/colors/semantics/buckets.css +64 -0
  51. package/styles/colors/semantics/core.css +317 -0
  52. package/styles/colors/semantics/data-viz.css +129 -0
  53. package/styles/colors/semantics/features.css +114 -0
  54. package/styles/colors/semantics.css +10 -619
  55. package/styles/components.css +1 -0
  56. package/styles/foundation/elevation.css +29 -0
  57. package/styles/foundation/index.css +11 -0
  58. package/styles/foundation/motion.css +10 -0
  59. package/styles/foundation/radius.css +27 -0
  60. package/styles/foundation/size.css +33 -0
  61. package/styles/foundation/space.css +47 -0
  62. package/styles/index.css +14 -0
  63. package/styles/resets.css +17 -25
  64. package/styles/tokens.css +16 -384
  65. package/styles/type/elements.css +225 -0
  66. package/styles/type/roles.css +419 -0
  67. package/styles/type/scale.css +89 -0
  68. package/styles/typography.css +11 -809
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Non-side-effect class export for `<preview-ui>`.
3
+ *
4
+ * Importing this file gives you the class without auto-registering the tag.
5
+ * The auto-register path is `@adia-ai/web-components/components/preview`.
6
+ *
7
+ * @see ../../USAGE.md#registration--auto-vs-explicit
8
+ */
9
+
10
+ /**
11
+ * <preview-ui> — live code + render, single source of truth.
12
+ *
13
+ * Wrap any authored AdiaUI markup; the component keeps it LIVE in a render
14
+ * stage and shows the SAME markup as escaped, syntax-highlighted, copyable
15
+ * HTML beside it — so a demo's code and its rendered output can never drift
16
+ * (the code IS the render's source).
17
+ *
18
+ * <preview-ui>
19
+ * <button-ui text="Save" variant="primary"></button-ui>
20
+ * </preview-ui>
21
+ *
22
+ * Batteries-included + HTML-first by construction: the author writes one
23
+ * block of HTML, no inline styles, no JS wiring — both panes appear.
24
+ *
25
+ * Attributes:
26
+ * [layout="split"] — render + code side-by-side (default: stacked)
27
+ * [code-first] — show the code pane above/before the render
28
+ * [language="…"] — code-ui language hint (default: html)
29
+ *
30
+ * Light-DOM (ADR-0033): the render pane holds real, live custom elements
31
+ * (re-parsed from the captured source on connect), so interactive demos
32
+ * actually work. `slot=` here is decorative; positioning is by CSS.
33
+ */
34
+
35
+ import { UIElement } from '../../core/element.js';
36
+
37
+ /** Prepare captured markup for display: strip shared indentation + surrounding
38
+ * blank lines, and collapse `attr=""` → `attr` (the DOM serializes boolean
39
+ * attributes like `truncate` / `disabled` with an empty value — show the
40
+ * cleaner boolean form a consumer would actually type). */
41
+ function dedent(src) {
42
+ let lines = src
43
+ .replace(/=""(?=[\s/>])/g, '') // boolean attr: truncate="" → truncate
44
+ .replace(/\t/g, ' ')
45
+ .split('\n');
46
+ while (lines.length && lines[0].trim() === '') lines.shift();
47
+ while (lines.length && lines[lines.length - 1].trim() === '') lines.pop();
48
+ const indentOf = (l) => l.match(/^ */)[0].length;
49
+ // Measure the common indent from the BODY (everything after the first line),
50
+ // not from every line: markup captured via outerHTML/innerHTML puts the
51
+ // opening tag at column 0 while its children + closing tag carry the source
52
+ // file's nesting depth. Including that 0-indent first line would make min=0
53
+ // and leave the whole body deeply indented (the original bug). Clamp each
54
+ // slice to the line's own indent so the 0-indent opener is never truncated.
55
+ const body = lines.slice(1).filter((l) => l.trim());
56
+ const measured = (body.length ? body : lines.filter((l) => l.trim())).map(indentOf);
57
+ const min = measured.length ? Math.min(...measured) : 0;
58
+ return lines.map((l) => l.slice(Math.min(min, indentOf(l)))).join('\n');
59
+ }
60
+
61
+ export class UIPreview extends UIElement {
62
+ static properties = {
63
+ layout: { type: String, default: '', reflect: true },
64
+ codeFirst: { type: Boolean, default: false, reflect: true, attribute: 'code-first' },
65
+ language: { type: String, default: 'html', reflect: true },
66
+ rows: { type: Boolean, default: false, reflect: true },
67
+ };
68
+
69
+ static template = () => null;
70
+
71
+ #fitObserver = null;
72
+
73
+ /** A [render-cell | code-cell] pair from one source string. */
74
+ #pane(source, lang, codeFirst) {
75
+ const render = document.createElement('div');
76
+ render.setAttribute('data-preview-render', '');
77
+ render.innerHTML = source; // re-parse → fresh, live custom elements (keeps
78
+ // any data-chunk corpus markers for harvest)
79
+ const codeWrap = document.createElement('div');
80
+ codeWrap.setAttribute('data-preview-code', '');
81
+ const code = document.createElement('code-ui');
82
+ code.setAttribute('language', lang);
83
+ // The CODE pane shows the clean consumer-facing HTML — strip doc-only
84
+ // corpus markers (data-chunk / -kind / -slot) that the render keeps.
85
+ code.textContent = source.replace(/\s+data-chunk(?:-[a-z]+)?="[^"]*"/g, '');
86
+ codeWrap.appendChild(code);
87
+ return codeFirst ? [codeWrap, render] : [render, codeWrap];
88
+ }
89
+
90
+ connected() {
91
+ // Idempotent — never re-stamp (peer mutation / re-connect safe).
92
+ if (this.querySelector(':scope > [data-preview-render], :scope > [data-preview-row]')) return;
93
+
94
+ // Side-by-side (render | code) is the default presentation. `rows` mode lays
95
+ // out its own per-example grid, and an explicit layout="…" (e.g. "stack")
96
+ // opts back into the stacked render-over-code form — so only default when
97
+ // neither is set. Setting the attribute (not the property default) keeps
98
+ // rows-mode previews free of a conflicting layout="split".
99
+ if (!this.hasAttribute('rows') && !this.hasAttribute('layout')) {
100
+ this.setAttribute('layout', 'split');
101
+ }
102
+
103
+ const lang = this.getAttribute('language') || 'html';
104
+ const codeFirst = this.hasAttribute('code-first');
105
+
106
+ // ── rows mode ── each direct child is one example; lay each out as its
107
+ // own [render | code] row so a multi-example gallery reads as paired
108
+ // side-by-side rows instead of all-renders-then-one-code-dump. The code
109
+ // shown is the child's OWN markup (zero drift). An optional
110
+ // [data-preview-label] surfaces a caption per row.
111
+ if (this.hasAttribute('rows')) {
112
+ const children = [...this.children];
113
+ const out = [];
114
+ for (const child of children) {
115
+ const label = child.getAttribute('data-preview-label');
116
+ if (label != null) child.removeAttribute('data-preview-label');
117
+ // A synthetic wrapper (multi-element row grouped by the host) marks
118
+ // itself [data-preview-unwrap] so we show its children's markup, not the
119
+ // throwaway wrapper tag — the copied code stays exactly what the author
120
+ // would paste.
121
+ const unwrap = child.hasAttribute('data-preview-unwrap');
122
+ if (unwrap) child.removeAttribute('data-preview-unwrap');
123
+ const source = dedent(unwrap ? child.innerHTML : child.outerHTML);
124
+ const row = document.createElement('div');
125
+ row.setAttribute('data-preview-row', '');
126
+ const panes = this.#pane(source, lang, codeFirst);
127
+ // The caption pill renders via [data-preview-render]::before, whose
128
+ // attr() can only read its OWN element — so the label goes on the
129
+ // render cell, not the row.
130
+ if (label) {
131
+ const renderEl = panes.find((el) => el.hasAttribute('data-preview-render'));
132
+ renderEl?.setAttribute('data-preview-label', label);
133
+ }
134
+ row.append(...panes);
135
+ out.push(row);
136
+ }
137
+ if (out.length) { this.replaceChildren(...out); this.#observeFit(); return; }
138
+ }
139
+
140
+ // ── single-block mode ── the whole slotted markup → one render + one code.
141
+ const source = dedent(this.innerHTML);
142
+ this.replaceChildren(...this.#pane(source, lang, codeFirst));
143
+ this.#observeFit();
144
+ }
145
+
146
+ // Side-by-side clips content wider than its half-width cell (a full dashboard
147
+ // composite, a wide control). After layout, if a render cell overflows, fall
148
+ // back to the stacked full-width form so the demo is never cut off. Monotonic
149
+ // (split → stack, never back; a row gains [data-stack] once), so it settles in
150
+ // one pass and never oscillates. Runs on a rAF + a ResizeObserver so it also
151
+ // adapts when the viewport narrows. Self-correcting — no per-component "wide"
152
+ // list to maintain.
153
+ #fit = () => {
154
+ if (this.hasAttribute('rows')) {
155
+ for (const row of this.querySelectorAll(':scope > [data-preview-row]')) {
156
+ const r = row.querySelector(':scope > [data-preview-render]');
157
+ if (r && r.scrollWidth > r.clientWidth + 4) row.setAttribute('data-stack', '');
158
+ }
159
+ } else if (this.getAttribute('layout') === 'split') {
160
+ const r = this.querySelector(':scope > [data-preview-render]');
161
+ if (r && r.scrollWidth > r.clientWidth + 4) this.setAttribute('layout', 'stack');
162
+ }
163
+ };
164
+
165
+ #scheduleFit = () => requestAnimationFrame(this.#fit);
166
+
167
+ #observeFit() {
168
+ this.#scheduleFit();
169
+ if (typeof ResizeObserver !== 'undefined') {
170
+ this.#fitObserver = new ResizeObserver(this.#scheduleFit);
171
+ this.#fitObserver.observe(this);
172
+ }
173
+ }
174
+
175
+ disconnected() {
176
+ this.#fitObserver?.disconnect();
177
+ }
178
+ }
@@ -0,0 +1,176 @@
1
+ /* ============================================================
2
+ <preview-ui> — live code + render, single source of truth.
3
+
4
+ A framed demo surface: a "stage" holding the live components on top
5
+ (or beside) the same markup shown as escaped, highlighted, copyable
6
+ HTML via a nested <code-ui>. The frame owns the chrome; the inner
7
+ code-ui's own border/radius are flattened so the two panes read as
8
+ one card.
9
+
10
+ Two-block @scope per docs/specs/component-token-contract.md.
11
+ ============================================================ */
12
+
13
+ @scope (preview-ui) {
14
+ /* ── Block 1 — TOKENS ── */
15
+ :where(:scope) {
16
+ --preview-border-default: 1px solid var(--a-border-subtle);
17
+ --preview-radius-default: var(--a-radius-lg);
18
+ --preview-render-bg-default: var(--a-canvas-1);
19
+ --preview-render-pad-default: var(--a-space-6);
20
+ --preview-render-gap-default: var(--a-space-3);
21
+ /* split column floor — below this the grid wraps to a single column
22
+ (responsive without container-type; see ADR note in css-patterns). */
23
+ --preview-split-min-default: 20rem;
24
+ }
25
+
26
+ /* ── Block 2 — BASE ── */
27
+ :scope {
28
+ display: block;
29
+ border: var(--preview-border, var(--preview-border-default));
30
+ border-radius: var(--preview-radius, var(--preview-radius-default));
31
+ overflow: hidden;
32
+ background: var(--preview-render-bg, var(--preview-render-bg-default));
33
+ }
34
+
35
+ /* Render stage — wraps live demo nodes; flex row so multiple sibling
36
+ samples (e.g. button variants) sit on one line and wrap. overflow-x:auto is
37
+ the last-resort affordance: a single unwrappable sample wider than even the
38
+ full-width stage (a page-scale composite) scrolls instead of being clipped
39
+ by the frame's overflow:hidden. Most demos fit (the component's overflow
40
+ check stacks split→full-width first), so the scrollbar only appears when a
41
+ demo is genuinely wider than the docs column. */
42
+ [data-preview-render] {
43
+ display: flex;
44
+ flex-wrap: wrap;
45
+ align-items: center;
46
+ gap: var(--preview-render-gap, var(--preview-render-gap-default));
47
+ padding: var(--preview-render-pad, var(--preview-render-pad-default));
48
+ background: var(--preview-render-bg, var(--preview-render-bg-default));
49
+ overflow-x: auto;
50
+ }
51
+
52
+ /* Code pane — divider line between panes; flatten the nested code-ui
53
+ chrome so the preview frame owns the outer border + radius. */
54
+ [data-preview-code] {
55
+ border-block-start: var(--preview-border, var(--preview-border-default));
56
+ min-width: 0; /* allow the code-ui to shrink in split grid */
57
+ }
58
+ [data-preview-code] code-ui {
59
+ display: block;
60
+ --code-border: transparent;
61
+ --code-radius: 0;
62
+ --code-bg: var(--a-bg);
63
+ }
64
+
65
+ /* code-first — divider moves to the bottom of the (now-leading) code pane */
66
+ :scope[code-first] [data-preview-code] {
67
+ border-block-start: none;
68
+ border-block-end: var(--preview-border, var(--preview-border-default));
69
+ }
70
+
71
+ /* Stacked layout — render above code. This is the BASE behaviour (above), so
72
+ [layout="stack"] just names it explicitly: it's the opt-out from the
73
+ side-by-side default that a bare <preview-ui> stamps on connect, and the
74
+ value the docs pin onto wide self-framing demos (card / shell / table). */
75
+ :scope[layout="stack"] {
76
+ display: block;
77
+ }
78
+
79
+ /* ── Split layout — render + code side-by-side ──
80
+ auto-fit grid collapses to one column under --preview-split-min with
81
+ NO container-type (avoids the flex-child-collapse trap; see memory
82
+ `container_name_convention`). When wrapped, the inter-pane divider
83
+ reverts to the stacked top-border via the base rule above. */
84
+ :scope[layout="split"] {
85
+ display: grid;
86
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--preview-split-min, var(--preview-split-min-default))), 1fr));
87
+ }
88
+ :scope[layout="split"] [data-preview-code] {
89
+ border-block-start: none;
90
+ border-inline-start: var(--preview-border, var(--preview-border-default));
91
+ /* Stretch code-ui to the grid row height — the render side is often
92
+ taller, so a short snippet would otherwise leave an empty code surface
93
+ below it (same fill as rows mode). */
94
+ display: grid;
95
+ }
96
+ :scope[layout="split"] [data-preview-code] code-ui {
97
+ height: 100%;
98
+ }
99
+ /* Single-column fallback (grid wrapped): restore the horizontal divider.
100
+ :scope[layout="split"] with one effective column → the code pane is on
101
+ its own row, so the inline-start border reads as a stray left edge.
102
+ Guard with a width media as a pragmatic floor. */
103
+ @media (max-width: 32rem) {
104
+ :scope[layout="split"] [data-preview-code] {
105
+ border-inline-start: none;
106
+ border-block-start: var(--preview-border, var(--preview-border-default));
107
+ }
108
+ }
109
+
110
+ /* ══════════ rows mode — a gallery of [render | code] rows ══════════
111
+ Each example is its own row: live render on the left, its source on the
112
+ right, paired side-by-side (no all-renders-then-one-code-dump). Rows
113
+ stack with a divider between them; each row is a 2-col grid that
114
+ collapses to stacked on narrow widths. */
115
+ :scope[rows] {
116
+ display: block;
117
+ }
118
+ :scope[rows] [data-preview-row] {
119
+ display: grid;
120
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
121
+ align-items: stretch;
122
+ }
123
+ :scope[rows] [data-preview-row] + [data-preview-row] {
124
+ border-block-start: var(--preview-border, var(--preview-border-default));
125
+ }
126
+ /* In rows mode the render/code panes butt horizontally — divider is the
127
+ inline-start border on the code cell, not the stacked top-border. The
128
+ cell is a grid so the nested code-ui STRETCHES to the row height (the
129
+ render side is often taller), filling the dark code surface instead of
130
+ leaving empty space below a short snippet. */
131
+ :scope[rows] [data-preview-row] > [data-preview-code] {
132
+ border-block-start: none;
133
+ border-inline-start: var(--preview-border, var(--preview-border-default));
134
+ display: grid;
135
+ }
136
+ :scope[rows] [data-preview-row] > [data-preview-code] code-ui {
137
+ height: 100%;
138
+ }
139
+ /* Optional per-row caption pill (from [data-preview-label] on the render
140
+ cell — attr() reads the element's OWN attribute). The render cell becomes
141
+ a column so the pill sits above the live sample. */
142
+ :scope[rows] [data-preview-render][data-preview-label] {
143
+ flex-direction: column;
144
+ align-items: flex-start;
145
+ }
146
+ :scope[rows] [data-preview-render][data-preview-label]::before {
147
+ content: attr(data-preview-label);
148
+ align-self: start;
149
+ font-family: var(--a-font-family-mono);
150
+ font-size: var(--a-ui-sm);
151
+ color: var(--a-fg-muted);
152
+ background: var(--a-bg-muted);
153
+ padding: 0.125rem 0.375rem;
154
+ border-radius: var(--a-radius-sm);
155
+ }
156
+ /* Narrow: collapse each row to stacked render-over-code. */
157
+ @media (max-width: 40rem) {
158
+ :scope[rows] [data-preview-row] {
159
+ grid-template-columns: 1fr;
160
+ }
161
+ :scope[rows] [data-preview-row] > [data-preview-code] {
162
+ border-inline-start: none;
163
+ border-block-start: var(--preview-border, var(--preview-border-default));
164
+ }
165
+ }
166
+ /* Content-driven fallback — a single row whose live sample is wider than the
167
+ half-width cell gets [data-stack] from the component's overflow check, so it
168
+ lays out render-over-code at full width (wide controls / composites). */
169
+ :scope[rows] [data-preview-row][data-stack] {
170
+ grid-template-columns: 1fr;
171
+ }
172
+ :scope[rows] [data-preview-row][data-stack] > [data-preview-code] {
173
+ border-inline-start: none;
174
+ border-block-start: var(--preview-border, var(--preview-border-default));
175
+ }
176
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `<preview-ui>` — Live code + render in one frame, from a single source. Wrap any authored AdiaUI markup: the same HTML renders LIVE in a stage and appears beside (or below) it as escaped, syntax-highlighted, copyable source via a nested <code-ui>. The code can never drift from the render — it IS the render's source. Batteries-included + HTML-first by construction: write one block of HTML, no inline styles, no JS wiring, and both panes appear. Use it for every component/recipe example so the primary snippet is always real, copyable HTML shown next to the working component.
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/preview
5
+ *
6
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
11
+ */
12
+
13
+ import { UIElement } from '../../core/element.js';
14
+
15
+ export class UIPreview extends UIElement {
16
+ /** Show the code pane before (above / left of) the render pane. Default is render-first. */
17
+ codeFirst: boolean;
18
+ /** Language hint forwarded to the nested <code-ui> for syntax highlighting. Demos are HTML, so this defaults to `html`. */
19
+ language: string;
20
+ /** Pane arrangement. Defaults to `split` — render and code side-by-side in a responsive 2-column grid that collapses to stacked on narrow widths (a bare `<preview-ui>` stamps `layout="split"` on connect). Use `stack` to force the render stacked above the code; the docs auto-apply `stack` to wide self-framing demos (card / shell / table) that read cramped at half width. The `rows` attribute is a separate gallery mode and overrides this. */
21
+ layout: 'split' | 'stack';
22
+ /** Gallery mode — treat each direct child as a SEPARATE example and lay each out as its own `[render | code]` row (live sample left, its own source right), stacked with dividers. Without `rows`, the whole slotted markup is one render + one code block. An optional `data-preview-label` on a child surfaces a caption above that row's sample. */
23
+ rows: boolean;
24
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `<preview-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIPreview } from '@adia-ai/web-components/components/preview/class';
8
+ *
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
10
+ */
11
+
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIPreview } from './preview.class.js';
14
+
15
+ // preview-ui composes <code-ui> for the source pane — pull it in so a bare
16
+ // `import '.../preview'` is batteries-included (the code pane works without
17
+ // the consumer separately importing code-ui).
18
+ import '../code/code.js';
19
+
20
+ defineIfFree('preview-ui', UIPreview);
21
+
22
+ export { UIPreview };
@@ -0,0 +1,100 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
3
+ name: UIPreview
4
+ tag: preview-ui
5
+ status: stable
6
+ component: Preview
7
+ category: display
8
+ version: 1
9
+ description: >-
10
+ Live code + render in one frame, from a single source. Wrap any authored
11
+ AdiaUI markup: the same HTML renders LIVE in a stage and appears beside (or
12
+ below) it as escaped, syntax-highlighted, copyable source via a nested
13
+ <code-ui>. The code can never drift from the render — it IS the render's
14
+ source. Batteries-included + HTML-first by construction: write one block of
15
+ HTML, no inline styles, no JS wiring, and both panes appear. Use it for
16
+ every component/recipe example so the primary snippet is always real,
17
+ copyable HTML shown next to the working component.
18
+ props:
19
+ layout:
20
+ description: >-
21
+ Pane arrangement. Defaults to `split` — render and code side-by-side in a
22
+ responsive 2-column grid that collapses to stacked on narrow widths (a bare
23
+ `<preview-ui>` stamps `layout="split"` on connect). Use `stack` to force the
24
+ render stacked above the code; the docs auto-apply `stack` to wide
25
+ self-framing demos (card / shell / table) that read cramped at half width.
26
+ The `rows` attribute is a separate gallery mode and overrides this.
27
+ type: string
28
+ default: ""
29
+ reflect: true
30
+ enum:
31
+ - split
32
+ - stack
33
+ codeFirst:
34
+ description: >-
35
+ Show the code pane before (above / left of) the render pane. Default is
36
+ render-first.
37
+ type: boolean
38
+ default: false
39
+ reflect: true
40
+ attribute: code-first
41
+ language:
42
+ description: >-
43
+ Language hint forwarded to the nested <code-ui> for syntax highlighting.
44
+ Demos are HTML, so this defaults to `html`.
45
+ type: string
46
+ default: html
47
+ reflect: true
48
+ rows:
49
+ description: >-
50
+ Gallery mode — treat each direct child as a SEPARATE example and lay
51
+ each out as its own `[render | code]` row (live sample left, its own
52
+ source right), stacked with dividers. Without `rows`, the whole slotted
53
+ markup is one render + one code block. An optional `data-preview-label`
54
+ on a child surfaces a caption above that row's sample.
55
+ type: boolean
56
+ default: false
57
+ reflect: true
58
+ slots:
59
+ default:
60
+ description: >-
61
+ The authored markup to preview. Captured verbatim on connect: re-parsed
62
+ into the live render stage AND shown literally in the code pane. Any
63
+ valid AdiaUI HTML — one element or many siblings.
64
+ states:
65
+ - name: idle
66
+ description: Default, the only state.
67
+ keywords:
68
+ - preview
69
+ - example
70
+ - demo
71
+ - code-preview
72
+ - live-preview
73
+ - playground
74
+ - codepen
75
+ synonyms:
76
+ preview: [example, demo, sandbox, live-preview]
77
+ related:
78
+ - Code
79
+ - Card
80
+ a2ui:
81
+ rules:
82
+ - rule: >-
83
+ Use <preview-ui> to demonstrate ANY component or recipe — wrap the
84
+ example markup once; it shows the live component and its copyable HTML
85
+ source together. Prefer it over a bare <code-ui> snippet (which shows
86
+ code but no render) or a bare rendered sample (which shows output but
87
+ no copyable HTML).
88
+ reason: 'Single source of truth — code and render cannot drift.'
89
+ - rule: >-
90
+ Author the slotted markup as plain AdiaUI HTML with attributes only —
91
+ no inline `style=`, no `<script>` wiring. If a sample needs inline
92
+ styles to look right, the component is missing an attribute; fix the
93
+ component, not the demo.
94
+ reason: 'Demos are the reference the next author copies; keep them honest.'
95
+ - rule: >-
96
+ Side-by-side ([layout="split"]) is the default. Set [layout="stack"] for
97
+ wide self-framing examples (a full card / shell / table reads cramped at
98
+ half width). Use [code-first] when the code is the teaching point and the
99
+ render is confirmation.
100
+ reason: 'Layout matches the reading order of the lesson.'
@@ -22,16 +22,11 @@
22
22
  "default": null
23
23
  },
24
24
  "variant": {
25
- "description": "Display variant. Structural values (bar, spinner) set the render mode; semantic values (primary/success/warning/danger/info) set the fill color. Prefer the `color` global attribute for color; variant-as-color accepted for back-compat with older exemplars.",
25
+ "description": "Render mode `bar` (default) or `spinner`. For fill color set the `--progress-fill` token (defaults to `--a-accent`); there is no variant-as-color (use the token).",
26
26
  "type": "string",
27
27
  "enum": [
28
28
  "bar",
29
- "spinner",
30
- "primary",
31
- "success",
32
- "warning",
33
- "danger",
34
- "info"
29
+ "spinner"
35
30
  ],
36
31
  "default": "bar"
37
32
  }
@@ -15,6 +15,6 @@ import { UIElement } from '../../core/element.js';
15
15
  export class UIProgress extends UIElement {
16
16
  /** Current progress. 0..max (or 0..1 when max omitted). null = indeterminate (legacy -1 accepted for back-compat). */
17
17
  value: number;
18
- /** Display variant. Structural values (bar, spinner) set the render mode; semantic values (primary/success/warning/danger/info) set the fill color. Prefer the `color` global attribute for color; variant-as-color accepted for back-compat with older exemplars. */
19
- variant: 'bar' | 'spinner' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
18
+ /** Render mode `bar` (default) or `spinner`. For fill color set the `--progress-fill` token (defaults to `--a-accent`); there is no variant-as-color (use the token). */
19
+ variant: 'bar' | 'spinner';
20
20
  }
@@ -17,19 +17,14 @@ props:
17
17
  type: number
18
18
  default: null
19
19
  variant:
20
- description: Display variant. Structural values (bar, spinner) set the render mode; semantic values (primary/success/warning/danger/info)
21
- set the fill color. Prefer the `color` global attribute for color; variant-as-color accepted for back-compat with older
22
- exemplars.
20
+ description: Render mode `bar` (default) or `spinner`. For fill color set
21
+ the `--progress-fill` token (defaults to `--a-accent`); there is no
22
+ variant-as-color (use the token).
23
23
  type: string
24
24
  default: bar
25
25
  enum:
26
26
  - bar
27
27
  - spinner
28
- - primary
29
- - success
30
- - warning
31
- - danger
32
- - info
33
28
  events: {}
34
29
  slots:
35
30
  fill:
@@ -36,11 +36,9 @@
36
36
  "type": "string",
37
37
  "enum": [
38
38
  "default",
39
- "accent",
40
39
  "success",
41
40
  "warning",
42
- "danger",
43
- "info"
41
+ "danger"
44
42
  ],
45
43
  "default": "default"
46
44
  }
@@ -20,5 +20,5 @@ export class UIProgressRow extends UIElement {
20
20
  /** Progress value 0-100 (null = indeterminate; legacy -1 accepted for back-compat) */
21
21
  value: number;
22
22
  /** Color variant for the progress fill */
23
- variant: 'default' | 'accent' | 'success' | 'warning' | 'danger' | 'info';
23
+ variant: 'default' | 'success' | 'warning' | 'danger';
24
24
  }
@@ -32,11 +32,9 @@ props:
32
32
  default: default
33
33
  enum:
34
34
  - default
35
- - accent
36
35
  - success
37
36
  - warning
38
37
  - danger
39
- - info
40
38
  events: {}
41
39
  slots:
42
40
  label:
@@ -152,13 +152,11 @@
152
152
  "default": ""
153
153
  },
154
154
  "variant": {
155
- "description": "Visual variant — default chrome, `outline` for bordered, `ghost` for inline.",
155
+ "description": "Visual variant — `default` chrome or `ghost` for inline.",
156
156
  "type": "string",
157
157
  "enum": [
158
158
  "default",
159
- "outline",
160
- "ghost",
161
- "soft"
159
+ "ghost"
162
160
  ],
163
161
  "default": "default"
164
162
  }
@@ -31,10 +31,10 @@ props:
31
31
  type: string
32
32
  default: ""
33
33
  variant:
34
- description: Visual variant — default chrome, `outline` for bordered, `ghost` for inline.
34
+ description: Visual variant — `default` chrome or `ghost` for inline.
35
35
  type: string
36
36
  default: default
37
- enum: [default, outline, ghost, soft]
37
+ enum: [default, ghost]
38
38
  size:
39
39
  description: >-
40
40
  Sizing scale via universal `[size]` attribute system
@@ -27,13 +27,10 @@
27
27
  "default": ""
28
28
  },
29
29
  "variant": {
30
- "description": "Visual variant — default underline strip, `pills` for filled pill tabs, `underline` (explicit).",
30
+ "description": "Visual variant — `default` (underline strip) or `bordered`.",
31
31
  "type": "string",
32
32
  "enum": [
33
33
  "default",
34
- "pills",
35
- "underline",
36
- "segmented",
37
34
  "bordered"
38
35
  ],
39
36
  "default": "default"
@@ -24,8 +24,8 @@ export class UITabs extends UIElement {
24
24
  orientation: string;
25
25
  /** Value of the currently active tab. Set programmatically or auto-assigned to the first non-disabled tab on connect. */
26
26
  value: string;
27
- /** Visual variant — default underline strip, `pills` for filled pill tabs, `underline` (explicit). */
28
- variant: 'default' | 'pills' | 'underline' | 'segmented' | 'bordered';
27
+ /** Visual variant — `default` (underline strip) or `bordered`. */
28
+ variant: 'default' | 'bordered';
29
29
 
30
30
  addEventListener<K extends keyof HTMLElementEventMap>(
31
31
  type: K,