@adia-ai/web-components 0.8.23 → 0.8.25

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 (69) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/components/agent-questions/agent-questions.yaml +0 -2
  3. package/components/alert/alert.class.js +5 -0
  4. package/components/alert/alert.yaml +2 -0
  5. package/components/anchor-bar/anchor-bar.a2ui.json +139 -0
  6. package/components/anchor-bar/anchor-bar.class.js +124 -0
  7. package/components/anchor-bar/anchor-bar.css +140 -0
  8. package/components/anchor-bar/anchor-bar.d.ts +35 -0
  9. package/components/anchor-bar/anchor-bar.examples.md +32 -0
  10. package/components/anchor-bar/anchor-bar.js +17 -0
  11. package/components/anchor-bar/anchor-bar.yaml +167 -0
  12. package/components/calendar-grid/calendar-grid.yaml +3 -0
  13. package/components/card/card.css +12 -0
  14. package/components/chart/chart.a2ui.json +16 -0
  15. package/components/chart/chart.class.js +105 -27
  16. package/components/chart/chart.css +27 -0
  17. package/components/chart/chart.d.ts +4 -0
  18. package/components/chart/chart.yaml +28 -0
  19. package/components/combobox/combobox.class.js +1 -1
  20. package/components/combobox/combobox.yaml +0 -1
  21. package/components/date-range-picker/date-range-picker.class.js +1 -1
  22. package/components/date-range-picker/date-range-picker.yaml +0 -1
  23. package/components/datetime-picker/datetime-picker.class.js +1 -1
  24. package/components/datetime-picker/datetime-picker.yaml +0 -1
  25. package/components/drawer/drawer.class.js +5 -0
  26. package/components/drawer/drawer.yaml +2 -0
  27. package/components/feed/feed.class.js +5 -0
  28. package/components/feed/feed.yaml +2 -0
  29. package/components/index.js +1 -0
  30. package/components/input/input.class.js +5 -0
  31. package/components/input/input.yaml +3 -0
  32. package/components/list/list.css +13 -4
  33. package/components/modal/modal.class.js +5 -0
  34. package/components/modal/modal.yaml +2 -0
  35. package/components/nav-group/nav-group.class.js +5 -0
  36. package/components/nav-group/nav-group.yaml +2 -0
  37. package/components/pagination/pagination.yaml +3 -0
  38. package/components/select/select.class.js +2 -2
  39. package/components/select/select.yaml +0 -1
  40. package/components/stat/stat.a2ui.json +8 -0
  41. package/components/stat/stat.css +39 -0
  42. package/components/stat/stat.d.ts +2 -0
  43. package/components/stat/stat.js +5 -0
  44. package/components/stat/stat.yaml +11 -0
  45. package/components/swiper/swiper.class.js +5 -0
  46. package/components/swiper/swiper.yaml +3 -0
  47. package/components/toolbar/toolbar.class.js +20 -0
  48. package/components/toolbar/toolbar.yaml +2 -0
  49. package/components/tooltip/tooltip.class.js +17 -11
  50. package/dist/host.min.css +1 -1
  51. package/dist/host.sheet.js +1 -1
  52. package/dist/theme-provider.min.js +2 -2
  53. package/dist/web-components.min.css +1 -1
  54. package/dist/web-components.min.js +117 -117
  55. package/dist/web-components.sheet.js +1 -1
  56. package/package.json +1 -1
  57. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +3 -3
  58. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +81 -93
  59. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +12 -25
  60. package/patterns/bulk-action-toolbar/bulk-action-toolbar.html +8 -52
  61. package/patterns/changelog-feed/changelog-feed.examples.html +3 -3
  62. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +4 -4
  63. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +2 -2
  64. package/patterns/marketing-engagement/marketing-engagement.examples.html +8 -8
  65. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  66. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +2 -2
  67. package/styles/api/parametric-scope.css +81 -0
  68. package/styles/components.css +1 -0
  69. package/styles/tokens.css +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.8.23",
3
+ "version": "0.8.25",
4
4
  "description": "AdiaUI web components \u2014 vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",
@@ -41,7 +41,7 @@
41
41
  <icon-ui slot="icon" name="key" size="sm"></icon-ui>
42
42
  <span slot="description"><code>sk_live_…A4F2</code> · last used 4 minutes ago</span>
43
43
  <row-ui slot="action" gap="2" align="center">
44
- <tag-ui size="sm" variant="primary">read · write</tag-ui>
44
+ <tag-ui size="sm" variant="accent">read · write</tag-ui>
45
45
  <button-ui icon="dots-three-vertical" variant="ghost" size="sm" aria-label="Manage"></button-ui>
46
46
  </row-ui>
47
47
  </list-item-ui>
@@ -57,7 +57,7 @@
57
57
  <icon-ui slot="icon" name="key" size="sm"></icon-ui>
58
58
  <span slot="description"><code>sk_test_…7f0e</code> · last used 18 days ago</span>
59
59
  <row-ui slot="action" gap="2" align="center">
60
- <tag-ui size="sm" variant="muted">test</tag-ui>
60
+ <tag-ui size="sm" variant="default">test</tag-ui>
61
61
  <button-ui icon="dots-three-vertical" variant="ghost" size="sm" aria-label="Manage"></button-ui>
62
62
  </row-ui>
63
63
  </list-item-ui>
@@ -217,7 +217,7 @@
217
217
  <list-item-ui text="Workspace · JSON">
218
218
  <icon-ui slot="icon" name="download-simple" size="sm"></icon-ui>
219
219
  <span slot="description">Ready 14 days ago · link expired</span>
220
- <tag-ui slot="action" size="sm" variant="muted">Expired</tag-ui>
220
+ <tag-ui slot="action" size="sm" variant="default">Expired</tag-ui>
221
221
  </list-item-ui>
222
222
  </list-ui>
223
223
  </section>
@@ -2,7 +2,7 @@
2
2
  <div>
3
3
  <h1>Bulk Action Toolbar</h1>
4
4
  <div data-actions>
5
- <tag-ui size="sm">table-toolbar-ui</tag-ui> <tag-ui size="sm">toolbar-ui</tag-ui> <tag-ui size="sm">table-ui</tag-ui> <tag-ui size="sm">card-ui</tag-ui>
5
+ <tag-ui size="sm">table-toolbar-ui</tag-ui> <tag-ui size="sm">toolbar-ui</tag-ui> <tag-ui size="sm">table-ui</tag-ui> <tag-ui size="sm">card-ui</tag-ui> <tag-ui size="sm">anchor-bar-ui</tag-ui>
6
6
  </div>
7
7
  </div>
8
8
  <p>When a table has row selection, surface a contextual bulk-action bar. Two variants: swap the standard <code>&lt;table-toolbar-ui&gt;</code> for a bar at the same surface position, or float a card-like elevated bar at the bottom of the table's own scroll container. Selection count anchors the left, actions flow from the right, destructive action last.</p>
@@ -67,7 +67,7 @@
67
67
 
68
68
  <section data-section data-property="floating-variant" data-chunk="bulk-action-toolbar-floating" data-chunk-kind="block"
69
69
  data-chunk-domain="data"
70
- data-chunk-description="Floating bottom-anchored bulk-action bar — a card-like elevated toolbar (elevation 3) that appears sticky to the bottom of a table's own scroll container (not the browser viewport) when rows are selected, with selection count, an optional per-flow controls slot, actions with destructive last, and an explicit Clear button. Composes table-ui's select event with card-ui + toolbar-ui; no focus steal on appearance, Escape clears selection when the bar owns focus, count is aria-live=polite."
70
+ data-chunk-description="Floating bottom-anchored bulk-action bar — an anchor-bar-ui elevated toolbar that appears sticky to the bottom of a table's own scroll container (not the browser viewport) when rows are selected, with selection count, an optional per-flow controls slot, actions with destructive last, and an explicit Clear button. Composes table-ui's select event with anchor-bar-ui + toolbar-ui; no focus steal on appearance, Escape clears selection when the bar owns focus, count is aria-live=polite."
71
71
  data-chunk-keywords="floating bulk action bar bottom anchored sticky selection toolbar card elevated table scroll container escape clear aria-live gmail linear notion">
72
72
  <h2 variant="section">Floating (bottom-anchored)</h2>
73
73
  <p data-note>
@@ -83,38 +83,36 @@
83
83
  <div data-table-scroll>
84
84
  <table-ui frameless id="pat-bulk-float-table" selectable sortable striped></table-ui>
85
85
 
86
- <card-ui id="pat-bulk-float-bar" elevation="3" data-bulk-bar-floating>
87
- <section>
88
- <!-- align="space-between" pushes the trailing group right
89
- NOT a flex-grow spacer span or an auto margin on a content
90
- item. toolbar-ui's overflow reflow sums each top-level
91
- child's RENDERED width to decide what fits; any child that
92
- absorbs free space (flex-grow, or an auto margin resolved
93
- to real pixels post-layout) has already expanded to fill
94
- that space by the time it's measured, so the sum always
95
- reads "full" and spuriously spills content. `justify-content`
96
- only repositions flex items it doesn't touch their
97
- individual widths (toolbar-ui's own CSS pins every direct
98
- child at `flex: 0 0 auto`) — so grouping into two
99
- `<toolbar-group-ui>` atoms and letting `align="space-between"`
100
- do the pushing keeps the reflow's width sum accurate. -->
101
- <toolbar-ui role="toolbar" aria-label="Bulk actions" align="space-between">
102
- <toolbar-group-ui>
103
- <text-ui strong aria-live="polite"><span data-bulk-float-count>0</span> selected</text-ui>
104
- <span data-bulk-controls></span>
105
- </toolbar-group-ui>
106
- <toolbar-group-ui>
107
- <button-ui icon="archive" icon-only aria-label="Archive" variant="ghost" size="sm" data-bulk-action="archive"></button-ui>
108
- <button-ui icon="download-simple" icon-only aria-label="Export" variant="ghost" size="sm" data-bulk-action="export"></button-ui>
109
- <button-ui icon="folder-simple" icon-only aria-label="Move" variant="ghost" size="sm" data-bulk-action="move"></button-ui>
110
- <divider-ui vertical></divider-ui>
111
- <button-ui icon="trash" icon-only aria-label="Delete" variant="ghost" size="sm" data-bulk-action="delete"></button-ui>
112
- <divider-ui vertical></divider-ui>
113
- <button-ui text="Clear" variant="ghost" size="sm" data-bulk-clear></button-ui>
114
- </toolbar-group-ui>
115
- </toolbar-ui>
116
- </section>
117
- </card-ui>
86
+ <anchor-bar-ui id="pat-bulk-float-bar" anchor="bottom">
87
+ <!-- align="space-between" pushes the trailing group right —
88
+ NOT a flex-grow spacer span or an auto margin on a content
89
+ item. toolbar-ui's overflow reflow sums each top-level
90
+ child's RENDERED width to decide what fits; any child that
91
+ absorbs free space (flex-grow, or an auto margin resolved
92
+ to real pixels post-layout) has already expanded to fill
93
+ that space by the time it's measured, so the sum always
94
+ reads "full" and spuriously spills content. `justify-content`
95
+ only repositions flex items it doesn't touch their
96
+ individual widths (toolbar-ui's own CSS pins every direct
97
+ child at `flex: 0 0 auto`) so grouping into two
98
+ `<toolbar-group-ui>` atoms and letting `align="space-between"`
99
+ do the pushing keeps the reflow's width sum accurate. -->
100
+ <toolbar-ui role="toolbar" aria-label="Bulk actions" align="space-between">
101
+ <toolbar-group-ui>
102
+ <text-ui strong aria-live="polite"><span data-bulk-float-count>0</span> selected</text-ui>
103
+ <span data-bulk-controls></span>
104
+ </toolbar-group-ui>
105
+ <toolbar-group-ui>
106
+ <button-ui icon="archive" icon-only aria-label="Archive" variant="ghost" size="sm" data-bulk-action="archive"></button-ui>
107
+ <button-ui icon="download-simple" icon-only aria-label="Export" variant="ghost" size="sm" data-bulk-action="export"></button-ui>
108
+ <button-ui icon="folder-simple" icon-only aria-label="Move" variant="ghost" size="sm" data-bulk-action="move"></button-ui>
109
+ <divider-ui vertical></divider-ui>
110
+ <button-ui icon="trash" icon-only aria-label="Delete" variant="ghost" size="sm" data-bulk-action="delete"></button-ui>
111
+ <divider-ui vertical></divider-ui>
112
+ <button-ui text="Clear" variant="ghost" size="sm" data-bulk-clear></button-ui>
113
+ </toolbar-group-ui>
114
+ </toolbar-ui>
115
+ </anchor-bar-ui>
118
116
  </div>
119
117
  </section>
120
118
  </card-ui>
@@ -165,10 +163,12 @@ table.addEventListener('select', (e) =&gt; {
165
163
  <p data-note>
166
164
  <code>[data-table-scroll]</code> wraps the table and IS its scroll container (bounded height +
167
165
  <code>overflow: auto</code>); the floating bar is a sibling of <code>&lt;table-ui&gt;</code>
168
- inside it, positioned with <code>position: sticky</code> so it pins to that box's bottom edge
166
+ inside it. The bar is an <code>&lt;anchor-bar-ui anchor="bottom"&gt;</code> (gh#495) its
167
+ default mode positions with <code>position: sticky</code> so it pins to that box's bottom edge
169
168
  — never the browser viewport, and never a child of <code>&lt;table-ui&gt;</code> (it owns its
170
- own light DOM). The bar is a <code>&lt;card-ui elevation="3"&gt;</code> for the card-like
171
- elevated container, holding a standard <code>&lt;toolbar-ui&gt;</code>.
169
+ own light DOM) holding a standard <code>&lt;toolbar-ui&gt;</code>. <code>anchor-bar-ui</code>
170
+ owns the elevated-surface chrome (bg/border/radius/shadow) directly; no wrapping
171
+ <code>&lt;card-ui&gt;</code> needed.
172
172
  </p>
173
173
  <code-ui language="html">&lt;section bleed&gt;
174
174
  &lt;table-toolbar-ui for="resources" text="Resources" count="12"&gt;&lt;/table-toolbar-ui&gt;
@@ -176,66 +176,54 @@ table.addEventListener('select', (e) =&gt; {
176
176
  &lt;div data-table-scroll&gt; &lt;!-- position: relative; overflow: auto; max-height: … --&gt;
177
177
  &lt;table-ui id="resources" selectable sortable&gt;&lt;/table-ui&gt;
178
178
 
179
- &lt;!-- Floating bar — position: sticky; bottom: in the pattern's CSS.
180
- No [hidden]/display:none toggle see the pattern's CSS comment
181
- on why (toolbar-ui's overflow measurement gets stuck at 0px). --&gt;
182
- &lt;card-ui elevation="3" data-bulk-bar-floating&gt;
183
- &lt;section&gt;
184
- &lt;!-- align="space-between" pushes the action group right — NOT a
185
- flex-grow spacer span or an auto margin on a content item.
186
- toolbar-ui's overflow reflow sums each top-level child's
187
- RENDERED width to decide what fits; anything that absorbs free
188
- space has already expanded to fill it by the time it's
189
- measured, so the sum always reads "full" and spuriously spills
190
- content. Two &lt;toolbar-group-ui&gt; atoms + align="space-between"
191
- keeps the width sum accurate (see Rules). --&gt;
192
- &lt;toolbar-ui role="toolbar" aria-label="Bulk actions" align="space-between"&gt;
193
- &lt;toolbar-group-ui&gt;
194
- &lt;text-ui strong aria-live="polite"&gt;&lt;span data-bulk-count&gt;0&lt;/span&gt; selected&lt;/text-ui&gt;
195
- &lt;span data-bulk-controls&gt;&lt;/span&gt; &lt;!-- optional per-flow controls, e.g. a shared-rationale input --&gt;
196
- &lt;/toolbar-group-ui&gt;
197
- &lt;toolbar-group-ui&gt;
198
- &lt;!-- Icon-only (icon-only + aria-label) — a compact floating
199
- pill has less width to spend than the full-width top-swap
200
- bar. --&gt;
201
- &lt;button-ui icon="archive" icon-only aria-label="Archive" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
202
- &lt;button-ui icon="download-simple" icon-only aria-label="Export" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
203
- &lt;button-ui icon="folder-simple" icon-only aria-label="Move" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
204
- &lt;divider-ui vertical&gt;&lt;/divider-ui&gt;
205
- &lt;button-ui icon="trash" icon-only aria-label="Delete" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
206
- &lt;divider-ui vertical&gt;&lt;/divider-ui&gt;
207
- &lt;button-ui text="Clear" variant="ghost" size="sm" data-bulk-clear&gt;&lt;/button-ui&gt;
208
- &lt;/toolbar-group-ui&gt;
209
- &lt;/toolbar-ui&gt;
210
- &lt;/section&gt;
211
- &lt;/card-ui&gt;
179
+ &lt;!-- Floating bar — anchor-bar-ui's default mode is position:sticky
180
+ within the nearest scroll container. [open] controls visibility
181
+ via opacity/visibility, never [hidden]/display:none see the
182
+ pattern's CSS comment on why (toolbar-ui's overflow measurement
183
+ gets stuck at 0px). --&gt;
184
+ &lt;anchor-bar-ui anchor="bottom"&gt;
185
+ &lt;!-- align="space-between" pushes the action group right NOT a
186
+ flex-grow spacer span or an auto margin on a content item.
187
+ toolbar-ui's overflow reflow sums each top-level child's
188
+ RENDERED width to decide what fits; anything that absorbs free
189
+ space has already expanded to fill it by the time it's
190
+ measured, so the sum always reads "full" and spuriously spills
191
+ content. Two &lt;toolbar-group-ui&gt; atoms + align="space-between"
192
+ keeps the width sum accurate (see Rules). --&gt;
193
+ &lt;toolbar-ui role="toolbar" aria-label="Bulk actions" align="space-between"&gt;
194
+ &lt;toolbar-group-ui&gt;
195
+ &lt;text-ui strong aria-live="polite"&gt;&lt;span data-bulk-count&gt;0&lt;/span&gt; selected&lt;/text-ui&gt;
196
+ &lt;span data-bulk-controls&gt;&lt;/span&gt; &lt;!-- optional per-flow controls, e.g. a shared-rationale input --&gt;
197
+ &lt;/toolbar-group-ui&gt;
198
+ &lt;toolbar-group-ui&gt;
199
+ &lt;!-- Icon-only (icon-only + aria-label) a compact floating
200
+ pill has less width to spend than the full-width top-swap
201
+ bar. --&gt;
202
+ &lt;button-ui icon="archive" icon-only aria-label="Archive" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
203
+ &lt;button-ui icon="download-simple" icon-only aria-label="Export" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
204
+ &lt;button-ui icon="folder-simple" icon-only aria-label="Move" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
205
+ &lt;divider-ui vertical&gt;&lt;/divider-ui&gt;
206
+ &lt;button-ui icon="trash" icon-only aria-label="Delete" variant="ghost" size="sm"&gt;&lt;/button-ui&gt;
207
+ &lt;divider-ui vertical&gt;&lt;/divider-ui&gt;
208
+ &lt;button-ui text="Clear" variant="ghost" size="sm" data-bulk-clear&gt;&lt;/button-ui&gt;
209
+ &lt;/toolbar-group-ui&gt;
210
+ &lt;/toolbar-ui&gt;
211
+ &lt;/anchor-bar-ui&gt;
212
212
  &lt;/div&gt;
213
213
  &lt;/section&gt;</code-ui>
214
- <code-ui language="javascript">// Enter/exit toggles [data-open]/[data-closing]; the CSS does the rest
215
- // including the delayed `visibility: hidden` on close (see the pattern's
216
- // own &lt;style&gt; block). No [hidden]/display:none toggle: toolbar-ui's
217
- // spillover system measures its own width via ResizeObserver on first
218
- // connect, and a display:none ancestor at that moment gives it a 0px
219
- // reading it does not reliably re-measure later items it moved to the
220
- // "more" overflow menu at that 0px reading stay stuck there even once the
221
- // bar has real width. `visibility` keeps the box laid out (real width,
222
- // always measurable) while still pulling it out of the a11y tree, tab
223
- // order, and click target when closed.
224
-
225
- function showBar(bar) {
226
- bar.removeAttribute('data-closing');
227
- bar.setAttribute('data-open', ''); // no .focus() — never steal focus on appearance
228
- }
229
-
230
- function hideBar(bar) {
231
- bar.setAttribute('data-closing', '');
232
- bar.removeAttribute('data-open');
233
- }
214
+ <code-ui language="javascript">// anchor-bar-ui's [open] IS the visibility state no [hidden]/display:none
215
+ // toggle: toolbar-ui's spillover system measures its own width via
216
+ // ResizeObserver on first connect, and a display:none ancestor at that
217
+ // moment gives it a 0px reading it does not reliably re-measure later —
218
+ // items it moved to the "more" overflow menu at that 0px reading stay
219
+ // stuck there even once the bar has real width. anchor-bar-ui's own CSS
220
+ // handles the enter/exit opacity/visibility transition; no .focus() call
221
+ // anywhere appearance never steals focus.
234
222
 
235
223
  table.addEventListener('select', (e) =&gt; {
236
224
  const n = e.detail?.selected?.length ?? 0;
237
225
  countEl.textContent = String(n);
238
- n &gt; 0 ? showBar(bar) : hideBar(bar);
226
+ bar.open = n &gt; 0;
239
227
  });
240
228
 
241
229
  // Escape clears selection ONLY when the bar owns focus (listener lives
@@ -258,10 +246,10 @@ bar.addEventListener('press', (e) =&gt; {
258
246
  <li>Default state uses <code>&lt;table-toolbar-ui&gt;</code> — the modern foundation. It already wires Filter / Sort / Columns / Search to the table via <code>[for]</code>; don't hand-roll those. The floating variant keeps it visible at all times (it doesn't swap).</li>
259
247
  <li>Every bulk bar uses <code>&lt;toolbar-ui&gt;</code> with <code>role="toolbar"</code> and <code>aria-label="Bulk actions"</code>.</li>
260
248
  <li><strong>Toolbar swap:</strong> match its <code>--toolbar-py</code> / <code>--toolbar-px</code> to <code>--table-toolbar-py</code> / <code>--table-toolbar-px</code> so the swap is seamless; both toolbars sit inside the same <code>&lt;section bleed&gt;</code> as the table, flush with the first table cell.</li>
261
- <li><strong>Floating:</strong> wrap the bar in <code>&lt;card-ui elevation="3"&gt;</code> for the card-like elevated surface (tokens only <code>--a-shadow-*</code> via <code>[elevation]</code>, never a raw color/shadow); position it with <code>position: sticky</code> inside a <code>[data-table-scroll]</code> wrapper that IS the table's own scroll container (bounded height + <code>overflow: auto</code>) — never <code>position: fixed</code> to the viewport, and never a child of <code>&lt;table-ui&gt;</code> itself (it owns its own light DOM).</li>
249
+ <li><strong>Floating:</strong> use <code>&lt;anchor-bar-ui anchor="bottom"&gt;</code> (gh#495) for the elevated-surface chromeits default mode is <code>position: sticky</code> inside a <code>[data-table-scroll]</code> wrapper that IS the table's own scroll container (bounded height + <code>overflow: auto</code>) — never a child of <code>&lt;table-ui&gt;</code> itself (it owns its own light DOM). Don't set <code>[viewport]</code> here — that opts into <code>position: fixed</code> to the browser viewport, which this pattern deliberately avoids.</li>
262
250
  <li>Selection count leads every bar. Group the count (+ optional controls) and the action buttons into two <code>&lt;toolbar-group-ui&gt;</code> atoms and set <code>&lt;toolbar-ui align="space-between"&gt;</code> to push the action group right — <strong>not</strong> a flex-grow <code>&lt;span&gt;</code> spacer or an auto margin on a content item. <code>&lt;toolbar-ui&gt;</code>'s overflow reflow sums each top-level child's rendered width to decide what fits; anything that absorbs free space (flex-grow, or an auto margin resolved to real pixels post-layout) has already expanded to fill that space by the time it's measured, so the sum always reads "full" and spuriously spills the trailing action(s) into an unreachable "more" menu — this is a real, previously-shipped bug in this pattern's own swap-variant demo (Delete was silently unreachable), fixed alongside the floating variant (gh#494 audit finding). <code>justify-content</code> only repositions flex items, never their widths, so it doesn't trip the same measurement. On the floating bar the count sits in an <code>aria-live="polite"</code> element so screen readers hear the running total; an optional <code>&lt;span data-bulk-controls&gt;</code> slot follows it for per-flow controls (e.g. a shared-rationale input) — empty by default.</li>
263
251
  <li>Destructive actions go last and are visually separated with a <code>&lt;divider-ui vertical&gt;</code>. The floating bar additionally ends with an explicit Clear button (its own divider before it) — don't make "untick every row" the only way to dismiss it.</li>
264
252
  <li>Keep bulk actions to 3–5; overflow belongs in a <code>&lt;popover-ui&gt;</code> "More" menu trailing the divider.</li>
265
- <li>Drive both variants from the table's <code>select</code> event (<code>detail.selected: number[]</code>) — don't unmount. The toolbar swap toggles <code>hidden</code>. The floating bar toggles <code>[data-open]</code>/<code>[data-closing]</code> instead — never <code>[hidden]</code>/<code>display:none</code>, which resets <code>&lt;toolbar-ui&gt;</code>'s overflow measurement to 0px and strands items in its "more" menu even once the bar has real width again. The floating bar additionally: never calls <code>.focus()</code> on appearance (no focus steal), and its <code>keydown</code> listener lives on the bar itself so Escape only clears selection while the bar owns focus.</li>
253
+ <li>Drive both variants from the table's <code>select</code> event (<code>detail.selected: number[]</code>) — don't unmount. The toolbar swap toggles <code>hidden</code>. The floating bar sets <code>anchor-bar-ui</code>'s <code>[open]</code> property instead — never <code>[hidden]</code>/<code>display:none</code>, which resets <code>&lt;toolbar-ui&gt;</code>'s overflow measurement to 0px and strands items in its "more" menu even once the bar has real width again. The floating bar additionally: never calls <code>.focus()</code> on appearance (no focus steal — <code>anchor-bar-ui</code> doesn't either), and its <code>keydown</code> listener lives on the bar itself so Escape only clears selection while the bar owns focus.</li>
266
254
  </ul>
267
255
  </section>
@@ -6,8 +6,8 @@
6
6
  * Default state shows <table-toolbar-ui> (Filter/Sort/Columns/Search,
7
7
  * wired via [for]). When rows are selected: the swap variant hides it
8
8
  * for <toolbar-ui data-bulk-bar>; the floating variant leaves it
9
- * visible and rises a <card-ui elevation="3"> bar from the bottom of
10
- * the table's own scroll container.
9
+ * visible and rises an <anchor-bar-ui anchor="bottom"> (gh#495) from the
10
+ * bottom of the table's own scroll container.
11
11
  */
12
12
 
13
13
  const columns = [
@@ -99,32 +99,19 @@ function setupFloating(router) {
99
99
  table.columns = columns;
100
100
  table.data = floatData;
101
101
 
102
- // Enter/exit toggles [data-open]/[data-closing]; the CSS does the rest
103
- // (packages/web-components/patterns/bulk-action-toolbar/bulk-action-toolbar.html),
104
- // including the delayed `visibility: hidden` on close. Deliberately NOT
105
- // [hidden]/display:none: toolbar-ui's spillover system measures its own
106
- // width via ResizeObserver on first connect, and a display:none ancestor
107
- // at that moment gives it a 0px reading it does not reliably re-measure
108
- // later items it moves to the "more" overflow menu at that 0px reading
109
- // stay stuck there even once the bar has real width (found composing
110
- // this pattern; not a toolbar-ui fix in scope here). `visibility` keeps
111
- // the box laid out (real, measurable width) while still pulling it out
112
- // of the a11y tree, tab order, and click target when closed. No
113
- // .focus() call anywhere — appearance never steals focus.
114
- function showBar() {
115
- bar.removeAttribute('data-closing');
116
- bar.setAttribute('data-open', '');
117
- }
118
-
119
- function hideBar() {
120
- bar.setAttribute('data-closing', '');
121
- bar.removeAttribute('data-open');
122
- }
123
-
102
+ // anchor-bar-ui's [open] property IS the visibility state (gh#495) — its
103
+ // own CSS handles the opacity/visibility enter/exit transition.
104
+ // Deliberately never [hidden]/display:none: toolbar-ui's spillover
105
+ // system measures its own width via ResizeObserver on first connect,
106
+ // and a display:none ancestor at that moment gives it a 0px reading it
107
+ // does not reliably re-measure later items it moves to the "more"
108
+ // overflow menu at that 0px reading stay stuck there even once the bar
109
+ // has real width (found composing this pattern; not a toolbar-ui fix in
110
+ // scope here). No .focus() call anywhere appearance never steals focus.
124
111
  table.addEventListener('select', (e) => {
125
112
  const n = e.detail?.selected?.length ?? 0;
126
113
  if (countEl) countEl.textContent = String(n);
127
- if (n > 0) showBar(); else hideBar();
114
+ bar.open = n > 0;
128
115
  });
129
116
 
130
117
  // Escape clears selection ONLY when the bar owns focus — the listener
@@ -10,6 +10,7 @@
10
10
  <link rel="stylesheet" href="../../styles/tokens.css">
11
11
 
12
12
  <!-- Component CSS — primitives this pattern composes -->
13
+ <link rel="stylesheet" href="../../components/anchor-bar/anchor-bar.css">
13
14
  <link rel="stylesheet" href="../../components/badge/badge.css">
14
15
  <link rel="stylesheet" href="../../components/button/button.css">
15
16
  <link rel="stylesheet" href="../../components/card/card.css">
@@ -24,6 +25,7 @@
24
25
  <link rel="stylesheet" href="../../components/toolbar/toolbar.css">
25
26
 
26
27
  <!-- Component JS -->
28
+ <script type="module" src="../../components/anchor-bar/anchor-bar.js"></script>
27
29
  <script type="module" src="../../components/badge/badge.js"></script>
28
30
  <script type="module" src="../../components/button/button.js"></script>
29
31
  <script type="module" src="../../components/card/card.js"></script>
@@ -45,63 +47,17 @@
45
47
 
46
48
  /* ── Floating bulk-action bar (gh#494) ──
47
49
  [data-table-scroll] IS the table's scroll container — bounded
48
- height + overflow: auto — so `position: sticky` on the floating
49
- bar pins it to THAT box's bottom edge, never the browser viewport.
50
- This is the compose-from-primitives shape gh#495's anchored-surface
51
- primitive would eventually own; card-ui + toolbar-ui + sticky
52
- positioning gets there without it. */
50
+ height + overflow: auto — so anchor-bar-ui's default (non-
51
+ [viewport]) `position: sticky` pins it to THAT box's bottom edge,
52
+ never the browser viewport. This used to be a hand-rolled
53
+ card-ui + position:sticky recipe; gh#495 promoted it to
54
+ <anchor-bar-ui>'s own default mode (see anchor-bar.css) — this
55
+ pattern is now that primitive's canonical consumer. */
53
56
  [data-table-scroll] {
54
57
  position: relative;
55
58
  overflow: auto;
56
59
  max-height: 16rem;
57
60
  }
58
- /* `width: fit-content` here would create a circular sizing trap:
59
- toolbar-ui's own overflow reflow measures ITS available width from
60
- this box, but this box's fit-content size would in turn be derived
61
- from the toolbar's (possibly already-collapsed) rendered content —
62
- a self-reinforcing collapse the first time reflow ever runs while
63
- hidden (0 available width) spills items into the overflow popover.
64
- A fixed `max-width` breaks the loop: the box's width is deterministic
65
- (bounded by the scroll container, capped at 40rem), independent of
66
- the toolbar's own spillover state. */
67
- [data-bulk-bar-floating] {
68
- position: sticky;
69
- bottom: var(--a-space-3);
70
- z-index: 1;
71
- margin-inline: auto;
72
- width: 100%;
73
- max-width: min(46rem, calc(100% - var(--a-space-6)));
74
- opacity: 0;
75
- transform: translateY(0.75rem);
76
- transition: opacity var(--a-duration) var(--a-easing-out),
77
- transform var(--a-duration) var(--a-easing-out),
78
- visibility 0s linear var(--a-duration);
79
- /* `visibility`, not `[hidden]`/`display:none` — closed state still
80
- has box + layout, kept out of the a11y tree + tab order + click
81
- target instead. toolbar-ui's spillover system measures its own
82
- width via ResizeObserver on first connect; a `display:none`
83
- ancestor at that moment gives it a 0px reading it never reliably
84
- re-measures after unhiding (observed empirically: items it moved
85
- to the "more" overflow menu at that 0px reading stayed stuck
86
- there even once the bar had 700px+ to work with). Never toggling
87
- display avoids the trap entirely — no toolbar-ui change needed. */
88
- visibility: hidden;
89
- }
90
- [data-bulk-bar-floating][data-open] {
91
- opacity: 1;
92
- transform: translateY(0);
93
- visibility: visible;
94
- transition: opacity var(--a-duration) var(--a-easing-out),
95
- transform var(--a-duration) var(--a-easing-out);
96
- }
97
- [data-bulk-bar-floating][data-closing] {
98
- opacity: 0;
99
- transform: translateY(0.75rem);
100
- }
101
-
102
- @media (prefers-reduced-motion: reduce) {
103
- [data-bulk-bar-floating] { transition: none; }
104
- }
105
61
  </style>
106
62
  </head>
107
63
  <body>
@@ -34,13 +34,13 @@
34
34
  <row-ui gap="2" align="baseline" justify="between">
35
35
  <row-ui gap="2" align="baseline">
36
36
  <text-ui strong size="lg">v2.3.0</text-ui>
37
- <tag-ui size="sm" variant="primary">Latest</tag-ui>
37
+ <tag-ui size="sm" variant="accent">Latest</tag-ui>
38
38
  </row-ui>
39
39
  <text-ui color="subtle" size="sm">March 12, 2026</text-ui>
40
40
  </row-ui>
41
41
 
42
42
  <col-ui gap="2" data-section="added">
43
- <text-ui variant="kicker" color="primary">Added</text-ui>
43
+ <text-ui variant="kicker" color="accent">Added</text-ui>
44
44
  <ul class="changelog-bullets">
45
45
  <li>Bulk-edit mode for the data-table — multi-select rows, then update column values in one pass.</li>
46
46
  <li>CSV export now respects the visible-columns filter.</li>
@@ -83,7 +83,7 @@
83
83
  </row-ui>
84
84
 
85
85
  <col-ui gap="2" data-section="added">
86
- <text-ui variant="kicker" color="primary">Added</text-ui>
86
+ <text-ui variant="kicker" color="accent">Added</text-ui>
87
87
  <ul class="changelog-bullets">
88
88
  <li>Workspace-level audit log — see who did what across all members.</li>
89
89
  </ul>
@@ -42,7 +42,7 @@
42
42
  <text-ui strong>Avery Chen</text-ui>
43
43
  <text-ui color="subtle" size="sm">2 hours ago</text-ui>
44
44
  </row-ui>
45
- <text-ui>Should the OKLCH ramp here use the chrome or the data scale? <text-ui color="primary">@Kim</text-ui> what's the convention?</text-ui>
45
+ <text-ui>Should the OKLCH ramp here use the chrome or the data scale? <text-ui color="accent">@Kim</text-ui> what's the convention?</text-ui>
46
46
  <row-ui gap="1">
47
47
  <tag-ui size="sm" icon-leading="thumbs-up">2</tag-ui>
48
48
  <tag-ui size="sm" icon-leading="eyes">1</tag-ui>
@@ -57,7 +57,7 @@
57
57
  <text-ui strong>Kim Granlund</text-ui>
58
58
  <text-ui color="subtle" size="sm">1 hour ago</text-ui>
59
59
  </row-ui>
60
- <text-ui>Chrome — the data scale is reserved for visualization series. Spec at <text-ui color="primary">.claude/docs/specs/component-token-contract.md</text-ui>.</text-ui>
60
+ <text-ui>Chrome — the data scale is reserved for visualization series. Spec at <text-ui color="accent">.claude/docs/specs/component-token-contract.md</text-ui>.</text-ui>
61
61
  <row-ui gap="1">
62
62
  <tag-ui size="sm" icon-leading="thumbs-up">3</tag-ui>
63
63
  <button-ui text="Reply" variant="ghost" size="sm"></button-ui>
@@ -71,7 +71,7 @@
71
71
  <text-ui strong>Jordan Lee</text-ui>
72
72
  <text-ui color="subtle" size="sm">12 minutes ago</text-ui>
73
73
  </row-ui>
74
- <text-ui>Filed a follow-up — adding a lint rule so this doesn't happen again. <text-ui color="primary">#linter</text-ui></text-ui>
74
+ <text-ui>Filed a follow-up — adding a lint rule so this doesn't happen again. <text-ui color="accent">#linter</text-ui></text-ui>
75
75
  <row-ui gap="1">
76
76
  <button-ui text="Reply" variant="ghost" size="sm"></button-ui>
77
77
  </row-ui>
@@ -175,7 +175,7 @@
175
175
  data-chunk-description="Reactions bar — emoji reaction tags with counts (thumbs-up 4, party 2, eyes 1, rocket 1; own reaction highlighted) plus an add-reaction button. Rides under a comment or message."
176
176
  data-chunk-keywords="reactions emoji bar counts thumbs-up party add react message comment collaboration engagement" gap="1">
177
177
  <tag-ui size="sm">👍 4</tag-ui>
178
- <tag-ui size="sm" variant="primary">🎉 2</tag-ui>
178
+ <tag-ui size="sm" variant="accent">🎉 2</tag-ui>
179
179
  <tag-ui size="sm">👀 1</tag-ui>
180
180
  <tag-ui size="sm">🚀 1</tag-ui>
181
181
  <button-ui icon="plus" variant="ghost" size="sm" aria-label="Add reaction"></button-ui>
@@ -109,7 +109,7 @@
109
109
  <td><input-ui type="number" value="124" size="sm"></input-ui></td>
110
110
  <td><input-ui type="number" value="98" size="sm"></input-ui></td>
111
111
  <td><input-ui type="number" value="156" size="sm"></input-ui></td>
112
- <td><tag-ui size="sm" variant="muted">clean</tag-ui></td>
112
+ <td><tag-ui size="sm" variant="default">clean</tag-ui></td>
113
113
  </tr>
114
114
  <tr>
115
115
  <td><text-ui strong>Widget B</text-ui></td>
@@ -130,7 +130,7 @@
130
130
  <td><input-ui type="number" value="200" size="sm"></input-ui></td>
131
131
  <td><input-ui type="number" value="245" size="sm"></input-ui></td>
132
132
  <td><input-ui type="number" value="280" size="sm"></input-ui></td>
133
- <td><tag-ui size="sm" variant="muted">clean</tag-ui></td>
133
+ <td><tag-ui size="sm" variant="default">clean</tag-ui></td>
134
134
  </tr>
135
135
  </tbody>
136
136
  </table>
@@ -31,7 +31,7 @@
31
31
  data-chunk-keywords="hero cta centered headline display deck subhead get-started buttons landing marketing banner launch" raw>
32
32
  <section bleed>
33
33
  <col-ui gap="3" align="center" padding="10">
34
- <tag-ui size="sm" variant="muted">v0.0.27 — out today</tag-ui>
34
+ <tag-ui size="sm" variant="default">v0.0.27 — out today</tag-ui>
35
35
  <text-ui variant="display" strong>Generative UI, deterministically.</text-ui>
36
36
  <text-ui variant="deck" color="subtle">AdiaUI emits A2UI JSON the runtime renders into Light-DOM web components. No model, no surprise.</text-ui>
37
37
  <row-ui gap="2">
@@ -57,7 +57,7 @@
57
57
  <section bleed>
58
58
  <grid-ui columns="2" gap="6" align="center" padding="8">
59
59
  <col-ui gap="4" align="start">
60
- <tag-ui size="sm" variant="muted">For teams</tag-ui>
60
+ <tag-ui size="sm" variant="default">For teams</tag-ui>
61
61
  <text-ui variant="display" strong>Ship UI as fast as your model thinks.</text-ui>
62
62
  <text-ui variant="deck" color="subtle">A2UI is a typed JSON protocol your agent emits and the runtime renders. No React rebuild for every prompt.</text-ui>
63
63
  <row-ui gap="2">
@@ -70,7 +70,7 @@
70
70
  <stat-ui label="Cost ratio" value="0.18×"></stat-ui>
71
71
  </row-ui>
72
72
  </col-ui>
73
- <card-ui variant="soft">
73
+ <card-ui variant="filled">
74
74
  <section bleed>
75
75
  <col-ui align="center" justify="center" padding="10">
76
76
  <text-ui color="subtle">[ Product visual ]</text-ui>
@@ -154,7 +154,7 @@
154
154
  <list-item-ui text="riley@example.com">
155
155
  <avatar-ui slot="icon" size="sm" text="riley@example.com"></avatar-ui>
156
156
  <span slot="description">Sent 1 month ago</span>
157
- <tag-ui slot="action" size="sm" variant="muted">Pending</tag-ui>
157
+ <tag-ui slot="action" size="sm" variant="default">Pending</tag-ui>
158
158
  </list-item-ui>
159
159
  </list-ui>
160
160
  </col-ui>
@@ -215,7 +215,7 @@
215
215
  <text-ui variant="display" color="subtle">🏗️</text-ui>
216
216
  <text-ui color="subtle" strong>Builder</text-ui>
217
217
  <text-ui variant="caption" color="subtle">Ship 10 prompts</text-ui>
218
- <tag-ui size="sm" variant="muted">7 of 10</tag-ui>
218
+ <tag-ui size="sm" variant="default">7 of 10</tag-ui>
219
219
  </col-ui>
220
220
  </section>
221
221
  </card-ui>
@@ -225,7 +225,7 @@
225
225
  <text-ui variant="display" color="subtle">🌟</text-ui>
226
226
  <text-ui color="subtle" strong>30-day streak</text-ui>
227
227
  <text-ui variant="caption" color="subtle">A month of daily use</text-ui>
228
- <tag-ui size="sm" variant="muted">12 of 30</tag-ui>
228
+ <tag-ui size="sm" variant="default">12 of 30</tag-ui>
229
229
  </col-ui>
230
230
  </section>
231
231
  </card-ui>
@@ -235,7 +235,7 @@
235
235
  <text-ui variant="display" color="subtle">🤝</text-ui>
236
236
  <text-ui color="subtle" strong>Connector</text-ui>
237
237
  <text-ui variant="caption" color="subtle">Refer 5 friends</text-ui>
238
- <tag-ui size="sm" variant="muted">3 of 5</tag-ui>
238
+ <tag-ui size="sm" variant="default">3 of 5</tag-ui>
239
239
  </col-ui>
240
240
  </section>
241
241
  </card-ui>
@@ -258,7 +258,7 @@
258
258
  <section>
259
259
  <col-ui gap="3" align="center" padding="6">
260
260
  <text-ui variant="display">🔥</text-ui>
261
- <tag-ui size="sm" variant="primary">Achievement unlocked</tag-ui>
261
+ <tag-ui size="sm" variant="accent">Achievement unlocked</tag-ui>
262
262
  <text-ui variant="display" strong>7-day streak</text-ui>
263
263
  <text-ui color="subtle">A full week of daily use. You've shipped 14 prompts and 23 chunks since you started.</text-ui>
264
264
  </col-ui>
@@ -64,7 +64,7 @@
64
64
  <header>
65
65
  <row-ui slot="heading" align="center" gap="2">
66
66
  <text-ui strong>Notifications</text-ui>
67
- <tag-ui size="sm" variant="primary">3 new</tag-ui>
67
+ <tag-ui size="sm" variant="accent">3 new</tag-ui>
68
68
  </row-ui>
69
69
  </header>
70
70
  <section>