@adia-ai/web-components 0.8.34 → 0.8.36

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 (89) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/components/agent-questions/agent-questions.class.js +0 -1
  3. package/components/badge/badge.css +27 -3
  4. package/components/badge/badge.examples.md +4 -4
  5. package/components/card/card.css +44 -1
  6. package/components/check/check.a2ui.json +25 -11
  7. package/components/check/check.class.js +5 -0
  8. package/components/check/check.css +21 -0
  9. package/components/check/check.d.ts +2 -0
  10. package/components/check/check.examples.md +4 -4
  11. package/components/check/check.yaml +26 -7
  12. package/components/demo-toggle/demo-toggle.css +4 -0
  13. package/components/demo-toggle/demo-toggle.examples.md +4 -4
  14. package/components/feed/feed-item.a2ui.json +3 -2
  15. package/components/feed/feed-item.yaml +7 -1
  16. package/components/feed/feed.css +28 -5
  17. package/components/field/field.examples.md +4 -4
  18. package/components/image/image.class.js +8 -0
  19. package/components/nav-group/nav-group.css +12 -1
  20. package/components/option-card/option-card.a2ui.json +10 -10
  21. package/components/option-card/option-card.css +32 -16
  22. package/components/option-card/option-card.yaml +19 -15
  23. package/components/radio/radio.examples.md +4 -4
  24. package/components/range/range.css +8 -4
  25. package/components/slider/slider.a2ui.json +6 -3
  26. package/components/slider/slider.css +28 -12
  27. package/components/slider/slider.yaml +11 -3
  28. package/components/stat/stat.examples.md +4 -4
  29. package/components/swatch/swatch.css +14 -6
  30. package/components/switch/switch.examples.md +4 -4
  31. package/components/table-toolbar/table-toolbar.css +10 -1
  32. package/components/tabs/tabs.css +10 -4
  33. package/components/tag/tag.class.js +14 -1
  34. package/components/tag/tag.css +63 -5
  35. package/components/text/text.examples.md +4 -4
  36. package/components/timeline/timeline-item.a2ui.json +2 -2
  37. package/components/timeline/timeline-item.yaml +2 -2
  38. package/components/timeline/timeline.css +33 -5
  39. package/components/timeline/timeline.d.ts +2 -2
  40. package/components/toast/toast.css +28 -5
  41. package/custom-elements.json +469 -46
  42. package/dist/theme-provider.min.js +1 -1
  43. package/dist/web-components.min.css +1 -1
  44. package/dist/web-components.min.js +4 -4
  45. package/dist/web-components.sheet.js +1 -1
  46. package/package.json +1 -1
  47. package/patterns/access-requests/access-requests.examples.html +3 -3
  48. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +3 -3
  49. package/patterns/admin-shell/admin-shell.examples.html +3 -4
  50. package/patterns/agent-cost/agent-cost.examples.html +3 -3
  51. package/patterns/agent-memory/agent-memory.examples.html +3 -3
  52. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +3 -3
  53. package/patterns/agent-tool-call/agent-tool-call.examples.html +3 -3
  54. package/patterns/alert-dialog/alert-dialog.examples.html +3 -3
  55. package/patterns/app-nav/app-nav.examples.html +4 -4
  56. package/patterns/approvals/approvals.examples.html +3 -3
  57. package/patterns/audit-log/audit-log.examples.html +3 -3
  58. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +19 -9
  59. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +2 -2
  60. package/patterns/changelog-feed/changelog-feed.examples.html +3 -3
  61. package/patterns/chart-in-card/chart-in-card.examples.html +37 -3
  62. package/patterns/chart-in-card/chart-in-card.examples.js +12 -0
  63. package/patterns/chart-in-card/chart-in-card.html +6 -0
  64. package/patterns/chat-shell/chat-shell.examples.html +3 -4
  65. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +3 -3
  66. package/patterns/conversion-funnel/conversion-funnel.examples.html +3 -3
  67. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +3 -3
  68. package/patterns/diff-review/diff-review.examples.html +3 -3
  69. package/patterns/editor-shell/editor-shell.examples.html +3 -4
  70. package/patterns/export-flow/export-flow.examples.html +3 -3
  71. package/patterns/filter-bar/filter-bar.examples.html +3 -3
  72. package/patterns/form-drawer/form-drawer.examples.html +3 -3
  73. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +3 -3
  74. package/patterns/inline-dialog/inline-dialog.examples.html +3 -3
  75. package/patterns/kanban-board/kanban-board.examples.html +3 -3
  76. package/patterns/layout/layout.examples.html +4 -7
  77. package/patterns/marketing-engagement/marketing-engagement.examples.html +3 -3
  78. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +3 -3
  79. package/patterns/permissions-matrix/permissions-matrix.examples.html +3 -3
  80. package/patterns/permissions-role-picker/permissions-role-picker.examples.html +3 -3
  81. package/patterns/permissions-sharing/permissions-sharing.examples.html +3 -3
  82. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +3 -3
  83. package/patterns/record-detail-drawer/record-detail-drawer.examples.html +3 -3
  84. package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.examples.html +3 -3
  85. package/patterns/retention-cohort/retention-cohort.examples.html +3 -3
  86. package/patterns/search-discovery/search-discovery.examples.html +3 -3
  87. package/patterns/system-banners/system-banners.examples.html +3 -3
  88. package/patterns/timezone-selector/timezone-selector.examples.html +3 -3
  89. package/patterns/wizard-drawer/wizard-drawer.examples.html +3 -3
@@ -30,26 +30,38 @@ option-card-ui[checked] [slot="icon"] {
30
30
 
31
31
  @scope (option-card-ui) {
32
32
  :where(:scope) {
33
- /* ── Container ── */
33
+ /* ── Container (gh#1018: tonal surface, not a bordered box on page
34
+ bg) ── */
34
35
  --option-card-padding-block: var(--a-space-3);
35
36
  --option-card-padding-inline: var(--a-space-4);
36
37
  --option-card-radius: var(--a-radius-md);
37
- --option-card-bg: var(--md-sys-color-neutral-background);
38
- --option-card-border: var(--md-sys-color-neutral-outline);
38
+ --option-card-bg: var(--md-sys-color-neutral-container-low);
39
+ /* No outline as the primary boundary — the tonal bg IS the
40
+ boundary. Transparent (not removed) so a consumer override that
41
+ sets a real color doesn't cause a layout reflow. */
42
+ --option-card-border: transparent;
39
43
  --option-card-gap-x: var(--a-space-3);
40
44
  --option-card-gap-y: var(--a-space-1);
41
45
 
42
46
  /* ── State: hover ── */
43
- --option-card-bg-hover: var(--a-bg-muted);
44
- --option-card-border-hover: var(--md-sys-color-neutral-on-surface-variant);
47
+ /* One step up from the container-low base (--a-bg-muted resolves to
48
+ the SAME role as the base bg now, gh#1018) so hover stays visibly
49
+ distinct instead of collapsing to a no-op. */
50
+ --option-card-bg-hover: var(--md-sys-color-neutral-container);
51
+ --option-card-border-hover: transparent;
45
52
 
46
53
  /* ── State: checked ── */
54
+ /* Kept as the accent border+fill (design decision, gh#1018): the
55
+ checked state is the one state that must stay unmistakable, so it
56
+ keeps the token-ladder rung idle/hover gave up. */
47
57
  --option-card-bg-checked: var(--md-sys-color-primary-container);
48
58
  --option-card-border-checked: var(--md-sys-color-primary);
49
59
 
50
60
  /* ── Indicator (CSS radio circle, same size + recipe as <radio-ui>). */
51
61
  --option-card-radio-size: var(--a-toggle-size);
52
- --option-card-radio-bg: var(--md-sys-color-neutral-background);
62
+ /* Matches the card bg (gh#1018) so the ring reads flush with the new
63
+ tonal surface instead of floating as a mismatched lighter cutout. */
64
+ --option-card-radio-bg: var(--md-sys-color-neutral-container-low);
53
65
  --option-card-radio-border: var(--md-sys-color-neutral-outline);
54
66
  --option-card-radio-fill: var(--md-sys-color-primary);
55
67
  /* Mirror radio.css's --radio-fg-checked recipe: an always-light
@@ -82,7 +94,8 @@ option-card-ui[checked] [slot="icon"] {
82
94
  }
83
95
 
84
96
  /* ── Base — grid: indicator | (heading + description). Optional icon
85
- sits between indicator and heading via :has() rules below. ── */
97
+ sits at the trailing end of the header row via :has() rules below
98
+ (gh#1018: was a leading column between indicator and heading). ── */
86
99
  :scope {
87
100
  box-sizing: border-box;
88
101
  display: grid;
@@ -105,17 +118,19 @@ option-card-ui[checked] [slot="icon"] {
105
118
  box-shadow var(--option-card-duration) var(--option-card-easing);
106
119
  }
107
120
 
108
- /* When an icon slot is present, insert a third column for it
109
- between indicator and heading. */
121
+ /* When an icon slot is present, insert a trailing column for it
122
+ alongside the heading only — description still spans the full
123
+ content width beneath (gh#1018). */
110
124
  :scope:has([slot="icon"]) {
111
- grid-template-columns: auto auto minmax(0, 1fr);
125
+ grid-template-columns: auto minmax(0, 1fr) auto;
112
126
  grid-template-areas:
113
- "indicator icon heading"
114
- "indicator icon description";
127
+ "indicator heading icon"
128
+ "indicator description description";
115
129
  }
116
130
  :scope [slot="icon"] {
117
131
  grid-area: icon;
118
- align-self: start;
132
+ align-self: center;
133
+ justify-self: end;
119
134
  color: var(--option-card-icon-color);
120
135
  --a-icon-size: var(--option-card-icon-size);
121
136
  transition: color var(--option-card-duration) var(--option-card-easing);
@@ -158,13 +173,14 @@ option-card-ui[checked] [slot="icon"] {
158
173
  gutter). Common pattern: an "Other" option with a free-text
159
174
  textarea, conditional follow-up fields, etc. ── */
160
175
  :scope > :not([slot]) {
176
+ /* 2/-1 spans every content column (heading/description, plus the
177
+ trailing icon column when present, gh#1018) — the icon column now
178
+ sits at the END, so this span already covers it without a
179
+ separate :has() override. */
161
180
  grid-column: 2 / -1;
162
181
  margin-block-start: var(--a-space-3);
163
182
  display: none;
164
183
  }
165
- :scope:has([slot="icon"]) > :not([slot]) {
166
- grid-column: 3 / -1;
167
- }
168
184
  /* hover + [checked] state rules moved outside @scope — see Safari 17.x bug note at top.
169
185
  The :scope[checked]::before recipe lives at top-of-file: an accent disc
170
186
  with a centered dot via radial-gradient, mirroring radio-ui's recipe. */
@@ -9,9 +9,12 @@ version: 1
9
9
  description: >-
10
10
  Selectable card with radio semantics. A "rich radio" — single-select
11
11
  one of N where each option carries a heading, optional description,
12
- and optional leading icon. Siblings sharing a `name` form a
13
- radiogroup. The whole card is the click target; a CSS-rendered radio
14
- circle in the top-left signals state. Form-associated, so `name=value`
12
+ and an optional icon at the trailing end of the header row. Siblings
13
+ sharing a `name` form a radiogroup. The whole card is the click
14
+ target; a CSS-rendered radio circle in the top-left signals state.
15
+ Renders on a container-low tonal surface (no outline as the primary
16
+ boundary); the checked state keeps an accent border + fill so
17
+ selection stays unmistakable. Form-associated, so `name=value`
15
18
  submits with the parent form when checked.
16
19
  # Per ADR-0027 — primitives that programmatically create other primitives
17
20
  # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
@@ -52,16 +55,17 @@ props:
52
55
  default: ""
53
56
  reflect: true
54
57
  icon:
55
- description: Optional Phosphor icon name. Stamped as a leading <icon-ui slot="icon"> when set.
58
+ description: Optional Phosphor icon name. Stamped as a trailing <icon-ui slot="icon"> when set.
56
59
  type: string
57
60
  default: ""
58
61
  reflect: true
59
62
  layout:
60
63
  description: >-
61
- Internal layout. `default` puts the indicator on the left and the icon
62
- adjacent. `tile` stacks vertically icon top-left, indicator top-right,
63
- heading + description belowfor hero pickers (source / role / plan
64
- tiles) where the icon is a primary brand cue.
64
+ Internal layout. `default` puts the indicator on the left and, when
65
+ [icon] is set, the icon at the trailing end of the header row
66
+ alongside the heading. `tile` stacks verticallyicon top-left,
67
+ indicator top-right, heading + description below for hero pickers
68
+ (source / role / plan tiles) where the icon is a primary brand cue.
65
69
  type: string
66
70
  default: default
67
71
  enum: [default, tile]
@@ -112,17 +116,17 @@ tokens:
112
116
  --option-card-radius:
113
117
  description: Card corner radius.
114
118
  --option-card-bg:
115
- description: Default background.
119
+ description: Default background (container-low tonal surface, gh#1018).
116
120
  --option-card-border:
117
- description: Default border color.
121
+ description: Default border color (transparent — the tonal bg is the primary boundary, not an outline; gh#1018).
118
122
  --option-card-gap-x:
119
123
  description: Horizontal gap between indicator (and icon) and content.
120
124
  --option-card-gap-y:
121
125
  description: Vertical gap between heading and description.
122
126
  --option-card-bg-hover:
123
- description: Hover background (non-checked).
127
+ description: Hover background (non-checked) — one tonal step up from --option-card-bg so hover stays visibly distinct.
124
128
  --option-card-border-hover:
125
- description: Hover border color (non-checked).
129
+ description: Hover border color (non-checked; transparent by default, matching the no-outline base).
126
130
  --option-card-bg-checked:
127
131
  description: Background when checked.
128
132
  --option-card-border-checked:
@@ -152,11 +156,11 @@ tokens:
152
156
  --option-card-desc-line-height:
153
157
  description: Description line height.
154
158
  --option-card-icon-color:
155
- description: Leading icon color when the card is not checked.
159
+ description: Trailing icon color when the card is not checked.
156
160
  --option-card-icon-color-checked:
157
- description: Leading icon color when the card is checked.
161
+ description: Trailing icon color when the card is checked.
158
162
  --option-card-icon-size:
159
- description: Leading icon size (sets `--a-icon-size` on the slotted icon-ui).
163
+ description: Trailing icon size (sets `--a-icon-size` on the slotted icon-ui).
160
164
  --option-card-disabled-opacity:
161
165
  description: Opacity multiplier when disabled.
162
166
  --option-card-focus-ring:
@@ -11,8 +11,8 @@
11
11
  ```html
12
12
  <row-ui gap="6" align="start" wrap>
13
13
  <col-ui gap="2">
14
- <text-ui variant="caption" color="muted">verse</text-ui>
15
- <div verse>
14
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
15
+ <div scale="ui-sm">
16
16
  <radio-ui label="Standard" name="reg-verse" checked></radio-ui>
17
17
  </div>
18
18
  </col-ui>
@@ -23,8 +23,8 @@
23
23
  </div>
24
24
  </col-ui>
25
25
  <col-ui gap="2">
26
- <text-ui variant="caption" color="muted">prose</text-ui>
27
- <div prose>
26
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
27
+ <div scale="content-md">
28
28
  <radio-ui label="Standard" name="reg-prose" checked></radio-ui>
29
29
  </div>
30
30
  </col-ui>
@@ -9,11 +9,15 @@ range-ui:not([disabled]) [slot="field"]:hover [data-layer="fill"] {
9
9
  :where(:scope) {
10
10
  /* ── Tokens (wired to --a-ui-*) ── */
11
11
  --range-bg: var(--a-ui-bg);
12
- --range-fill-bg: var(--a-ui-bg);
13
- --range-fill-bg-hover: var(--a-ui-bg-hover);
14
- --range-fill-bg-active: var(--a-ui-bg-active);
12
+ /* Brand-primary fill (gh#1080): the neutral --a-ui-bg fill read as
13
+ near-invisible against the track at partial values; slider-ui's
14
+ --slider-fill-bg already uses the primary family — same convention
15
+ here, with the on-primary ink pair for the clipped fill-layer text. */
16
+ --range-fill-bg: var(--a-primary-bg);
17
+ --range-fill-bg-hover: var(--a-primary-bg-hover);
18
+ --range-fill-bg-active: var(--a-primary-bg-active);
15
19
  --range-fill-bg-disabled: var(--a-canvas-muted);
16
- --range-fill-fg: var(--md-sys-color-neutral-on-surface);
20
+ --range-fill-fg: var(--md-sys-color-primary-on-primary);
17
21
  --range-fill-fg-disabled: var(--a-ui-text-disabled);
18
22
  --range-fill-label-fg: var(--a-ui-text-subtle);
19
23
  --range-border: var(--a-ui-border);
@@ -175,16 +175,19 @@
175
175
  "description": "Filled portion / progress color (was mixed accent, now primary)"
176
176
  },
177
177
  "--slider-thumb-height": {
178
- "description": "Thumb pill height (track-height − 2× inset)"
178
+ "description": "Thumb pill height (--slider-thumb-size − 2× inset)"
179
+ },
180
+ "--slider-thumb-size": {
181
+ "description": "Thumb (grab handle) control size — scales via the universal [size] attribute (16 sm / 20 md / 24 lg at density=1). Independent of --slider-track-height (gh#1012); the thumb overhangs the thin rail."
179
182
  },
180
183
  "--slider-thumb-width": {
181
- "description": "Thumb pill width (2× thumb-height, driven by track-height)"
184
+ "description": "Thumb pill width (2× thumb-visual-height, driven by --slider-thumb-size)"
182
185
  },
183
186
  "--slider-track": {
184
187
  "description": "Unfilled track background color"
185
188
  },
186
189
  "--slider-track-height": {
187
- "description": "Full track height (scales via universal [size] attribute)"
190
+ "description": "Rail thickness — a slim strip (a few px, house space scale), NOT control height (gh#1012 re-baseline; was --a-toggle-size, making the whole track read as a toggle-height progress bar)."
188
191
  }
189
192
  },
190
193
  "traits": [],
@@ -7,13 +7,19 @@
7
7
  Variables
8
8
  W = track width (100% of the track element)
9
9
  t = thumb width (full outer width including internal padding)
10
- h = track height (var(--slider-track-height))
10
+ h = thumb height (var(--slider-thumb-size) — control size,
11
+ gh#1012: DECOUPLED from track height. The
12
+ rail (var(--slider-track-height)) is a thin
13
+ conventional-slider strip; the thumb
14
+ overhangs it above and below, centered via
15
+ top:50% + translate(-50%,-50%) on the track.)
11
16
  pad = internal padding on the thumb (var(--slider-thumb-padding))
12
17
  p = progress (0.0 → 1.0, written by JS as --slider-pct)
13
18
 
14
19
  The thumb consists of two layers:
15
20
  • Container: the geometry element that the fill aligns to.
16
- Sized to t × h. Provides the full-height grab area.
21
+ Sized to t × h. Provides the full grab area,
22
+ independent of the (thin) rail it overhangs.
17
23
  • Visual: the white pill rendered inside the container via
18
24
  ::before. Sized to (t−2·pad) × (h−2·pad), centered
19
25
  by inset = pad on all sides.
@@ -64,17 +70,26 @@
64
70
  --slider-readout-gap: var(--a-space-0-5);
65
71
  --slider-radius: var(--a-radius-full);
66
72
 
67
- /* Track height scales with the universal [size] attribute
68
- via --a-toggle-size: 16 sm / 20 md / 24 lg at density=1.
69
- Override --slider-track-height directly for custom sizes. */
70
- --slider-track-height: var(--a-toggle-size);
73
+ /* Rail thickness (gh#1012 re-baseline) conventional slider anatomy:
74
+ a slim strip, not control height. Was var(--a-toggle-size) (16/20/24px),
75
+ making the whole track read as a toggle-height progress bar. Now a
76
+ few px from the house space scale; the thumb (below) keeps the
77
+ control size independently. */
78
+ --slider-track-height: var(--a-space-1-5);
79
+
80
+ /* Thumb size: the universal [size] attribute's control dimension —
81
+ 16 sm / 20 md / 24 lg at density=1, same scale the rail used to
82
+ carry directly. Decoupled from --slider-track-height so resizing
83
+ the rail never resizes the grab target, and vice versa. */
84
+ --slider-thumb-size: var(--a-toggle-size);
71
85
 
72
86
  /* Thumb padding: internal breathing room on all sides.
73
87
  The visual pill sits pad px away from the container edge. */
74
88
  --slider-thumb-padding: 2px;
75
89
 
76
- /* Visual thumb height: track height minus top and bottom padding. */
77
- --slider-thumb-visual-h: calc(var(--slider-track-height) - 2 * var(--slider-thumb-padding));
90
+ /* Visual thumb height: thumb (control) size minus top/bottom padding
91
+ NOT track height (gh#1012); the thumb overhangs the thin rail. */
92
+ --slider-thumb-visual-h: calc(var(--slider-thumb-size) - 2 * var(--slider-thumb-padding));
78
93
 
79
94
  /* Visual thumb width: 2×1 pill ratio. */
80
95
  --slider-thumb-visual-w: calc(var(--slider-thumb-visual-h) * 2);
@@ -213,16 +228,17 @@
213
228
  * (100% - 2 * var(--slider-thumb-width)));
214
229
  }
215
230
 
216
- /* Thumb CONTAINER: full track height, geometry width.
217
- Transparent background; the white pill is rendered by ::before.
218
- This element provides a generous vertical grab area. */
231
+ /* Thumb CONTAINER: control-size height (independent of the thin rail —
232
+ gh#1012), geometry width. Transparent background; the white pill is
233
+ rendered by ::before. Centered on the rail via top:50% + the
234
+ translate(-50%,-50%) below, so it overhangs above and below. */
219
235
  [slot="thumb"] {
220
236
  position: absolute;
221
237
  top: 50%;
222
238
  left: calc(var(--slider-thumb-width) / 2
223
239
  + var(--slider-pct) * (100% - var(--slider-thumb-width)));
224
240
  width: var(--slider-thumb-width);
225
- height: var(--slider-track-height);
241
+ height: var(--slider-thumb-size);
226
242
  border-radius: var(--slider-radius);
227
243
  background: transparent;
228
244
  transform: translate(-50%, -50%);
@@ -115,13 +115,21 @@ tokens:
115
115
  --slider-fill:
116
116
  description: Filled portion / progress color (was mixed accent, now primary)
117
117
  --slider-thumb-width:
118
- description: Thumb pill width (2× thumb-height, driven by track-height)
118
+ description: Thumb pill width (2× thumb-visual-height, driven by --slider-thumb-size)
119
119
  --slider-thumb-height:
120
- description: Thumb pill height (track-height − 2× inset)
120
+ description: Thumb pill height (--slider-thumb-size − 2× inset)
121
+ --slider-thumb-size:
122
+ description: >-
123
+ Thumb (grab handle) control size — scales via the universal [size]
124
+ attribute (16 sm / 20 md / 24 lg at density=1). Independent of
125
+ --slider-track-height (gh#1012); the thumb overhangs the thin rail.
121
126
  --slider-track:
122
127
  description: Unfilled track background color
123
128
  --slider-track-height:
124
- description: Full track height (scales via universal [size] attribute)
129
+ description: >-
130
+ Rail thickness — a slim strip (a few px, house space scale), NOT
131
+ control height (gh#1012 re-baseline; was --a-toggle-size, making
132
+ the whole track read as a toggle-height progress bar).
125
133
  a2ui:
126
134
  rules:
127
135
  - rule: 'Default mode is single-handle. Form-associated; emits numeric change events with detail.value.'
@@ -11,8 +11,8 @@
11
11
  ```html
12
12
  <row-ui gap="6" align="start" wrap>
13
13
  <col-ui gap="2">
14
- <text-ui variant="caption" color="muted">verse</text-ui>
15
- <div verse style="width:180px">
14
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
15
+ <div scale="ui-sm" style="width:180px">
16
16
  <stat-ui value="$48.2k" label="Revenue" change="+12.5%" trend="up"></stat-ui>
17
17
  </div>
18
18
  </col-ui>
@@ -23,8 +23,8 @@
23
23
  </div>
24
24
  </col-ui>
25
25
  <col-ui gap="2">
26
- <text-ui variant="caption" color="muted">prose</text-ui>
27
- <div prose style="width:180px">
26
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
27
+ <div scale="content-md" style="width:180px">
28
28
  <stat-ui value="$48.2k" label="Revenue" change="+12.5%" trend="up"></stat-ui>
29
29
  </div>
30
30
  </col-ui>
@@ -217,8 +217,10 @@
217
217
  each pip is a positioned child with semantic color. */
218
218
  :scope > [data-badge] {
219
219
  position: absolute;
220
- top: -4px;
221
- right: -4px;
220
+ /* Inside the tile's corner (gh#1086) — the old -4px offsets hung the
221
+ pips outside the host box, reading as misplaced chrome. */
222
+ top: 2px;
223
+ right: 2px;
222
224
  display: inline-flex;
223
225
  align-items: center;
224
226
  gap: 2px;
@@ -316,13 +318,19 @@
316
318
  transition: box-shadow var(--a-duration-fast) var(--a-easing-out);
317
319
  }
318
320
  :scope[selectable]:focus { outline: none; }
321
+ /* Focus keeps an OUTER host ring — a11y wants focus visible beyond the
322
+ element's own silhouette. */
319
323
  :scope[selectable]:focus-visible {
320
324
  box-shadow: 0 0 0 var(--swatch-select-ring-width) var(--swatch-select-ring);
321
325
  }
322
- :scope[selected] {
323
- box-shadow: 0 0 0 var(--swatch-select-ring-width) var(--swatch-select-ring);
326
+ /* Selection + hover rings draw INSET on the tile itself (gh#1086): the
327
+ ring follows the tile's real shape (round for dots, square radius for
328
+ tiles) and the outer silhouette stays identical selected vs not — the
329
+ old outer host ring visually inflated dot swatches by 2px. */
330
+ :scope[selected] > [data-tile] {
331
+ box-shadow: inset 0 0 0 var(--swatch-select-ring-width) var(--swatch-select-ring);
324
332
  }
325
- :scope[selectable]:hover:not([selected]) {
326
- box-shadow: 0 0 0 1px color-mix(in oklab, var(--swatch-select-ring) 50%, transparent);
333
+ :scope[selectable]:hover:not([selected]) > [data-tile] {
334
+ box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--swatch-select-ring) 50%, transparent);
327
335
  }
328
336
  }
@@ -11,8 +11,8 @@
11
11
  ```html
12
12
  <row-ui gap="6" align="start" wrap>
13
13
  <col-ui gap="2">
14
- <text-ui variant="caption" color="muted">verse</text-ui>
15
- <div verse>
14
+ <text-ui variant="caption" color="muted">scale="ui-sm"</text-ui>
15
+ <div scale="ui-sm">
16
16
  <switch-ui label="Enabled" checked></switch-ui>
17
17
  </div>
18
18
  </col-ui>
@@ -23,8 +23,8 @@
23
23
  </div>
24
24
  </col-ui>
25
25
  <col-ui gap="2">
26
- <text-ui variant="caption" color="muted">prose</text-ui>
27
- <div prose>
26
+ <text-ui variant="caption" color="muted">scale="content-md"</text-ui>
27
+ <div scale="content-md">
28
28
  <switch-ui label="Enabled" checked></switch-ui>
29
29
  </div>
30
30
  </col-ui>
@@ -39,6 +39,13 @@
39
39
  [data-toolbar] {
40
40
  display: flex;
41
41
  flex-direction: row;
42
+ /* gh#1076 — narrow containers WRAP the clusters to a new row instead of
43
+ crushing them into each other: the fixed-size control clusters
44
+ (flex 0 0 auto) never shrink, so without wrap all pressure landed on
45
+ [data-search] (min-width:0 → collapsed to 0px, measured live) while
46
+ neighbors painted over the gap. */
47
+ flex-wrap: wrap;
48
+ row-gap: var(--table-toolbar-cluster-gap);
42
49
  align-items: center;
43
50
  gap: var(--table-toolbar-gap);
44
51
  padding: var(--table-toolbar-py) var(--table-toolbar-px);
@@ -95,7 +102,9 @@
95
102
 
96
103
  [data-search] {
97
104
  flex: 1 1 var(--table-toolbar-search-min);
98
- min-width: 0;
105
+ /* gh#1076 — never collapse to 0: floor at a usable width and let the
106
+ flex-wrap above move it to its own row when the container is tight. */
107
+ min-width: min(10rem, 100%);
99
108
  max-width: var(--table-toolbar-search-max);
100
109
  margin-inline-start: auto;
101
110
  }
@@ -4,7 +4,10 @@
4
4
  --tabs-border: transparent;
5
5
  --tabs-bg: transparent;
6
6
  --tabs-radius: var(--a-radius-md);
7
- --tabs-button-height: var(--a-size);
7
+ /* One quarter-rem taller than the shared control height (operator
8
+ ruling 2026-08-12) — tab labels sit at --a-ui-lg since gh#965 and
9
+ needed the extra breathing room. */
10
+ --tabs-button-height: calc(var(--a-size) + 0.25rem);
8
11
  /* One rung UP the --a-ui-* scale (gh#965): tab labels are primary
9
12
  navigation, not control chrome. */
10
13
  --tabs-font-size: var(--a-ui-lg);
@@ -15,9 +18,12 @@
15
18
  --tabs-gap: var(--a-space-3);
16
19
  --tabs-indicator-color: var(--md-sys-color-primary);
17
20
  --tabs-indicator-height: 2px;
18
- --tabs-fg: var(--md-sys-color-neutral-on-surface-variant);
19
- --tabs-fg-hover: var(--md-sys-color-neutral-on-surface);
20
- --tabs-fg-active: var(--a-fg-selected);
21
+ /* State fg follows nav-item-ui's convention (gh#968 ruling, gh#1088):
22
+ idle neutral-high · hover/selected the shared --a-ui-text-* aliases —
23
+ one color language across horizontal (tabs) and vertical (nav) selection. */
24
+ --tabs-fg: var(--md-sys-color-neutral-high);
25
+ --tabs-fg-hover: var(--a-ui-text-hover);
26
+ --tabs-fg-active: var(--a-ui-text-selected);
21
27
  --tabs-fg-disabled: var(--a-ui-text-disabled);
22
28
 
23
29
  /* ── Spacing ── */
@@ -57,7 +57,20 @@ export class UITag extends UIElement {
57
57
  static parts = {
58
58
  // button-ui, not a native <button> (gh issue 276) — the icon slots in
59
59
  // explicitly to keep its bold weight (the icon= prop stamps regular).
60
- dismiss: '<button-ui slot="dismiss" variant="ghost" size="sm" aria-label="Remove"><icon-ui slot="icon" name="x" weight="bold"></icon-ui></button-ui>',
60
+ // gh#1084: the icon carries its own explicit free-form `size` (routes
61
+ // through icon.class.js's inline-style path, --icon-size) rather than
62
+ // relying on tag.css's `[slot="dismiss"] { --a-icon-size: … }` token
63
+ // override — that CSS-custom-property override is DEAD on this button:
64
+ // the button also carries `size="sm"`, and the universal
65
+ // `[size="sm"] { --a-icon-size: 0.875rem; … }` rule (styles/api/
66
+ // sizing.css) lives in the `utilities` layer, which the cascade-layer
67
+ // order (`@layer reset, tokens, elements, components, utilities, …` —
68
+ // styles/index.css) ranks ABOVE the `components` layer tag.css's own
69
+ // rule lives in — so the universal rule always wins on this element
70
+ // regardless of specificity, and any --a-icon-size value tag.css sets
71
+ // there is inert. An icon-level `size="…"` attribute sets an inline
72
+ // style directly (icon.class.js), which outranks every layer.
73
+ dismiss: '<button-ui slot="dismiss" variant="ghost" size="sm" aria-label="Remove"><icon-ui slot="icon" name="x" weight="bold" size="0.75rem"></icon-ui></button-ui>',
61
74
  };
62
75
 
63
76
  static template = () => null;
@@ -28,8 +28,25 @@ tag-ui[removable]:not([disabled]):hover {
28
28
  --tag-border: transparent;
29
29
  --tag-radius: var(--a-radius-full);
30
30
  --tag-px: var(--a-space-1-5);
31
- --tag-py: var(--a-space-0-5);
31
+ /* gh#1084: block padding flattened toward 1px per tier — the
32
+ --tag-min-h floor (below) now owns the chip's vertical size, so
33
+ py no longer has to carry it. Was --a-space-0-5 (2px); the extra
34
+ space min-height used to need from padding is now supplied by
35
+ align-items:center centering the content inside the floored box. */
36
+ --tag-py: var(--a-space-px);
32
37
  --tag-gap: var(--a-space-1);
38
+ /* gh#1084 — min-height floor, decoupled from padding (mirrors
39
+ tags-input-ui's --tags-input-min-h). Component-owned fixed rem
40
+ constants (not density-scaled, matching --tag-dismiss-size's own
41
+ precedent below) calibrated to the CURRENT rendered height of a
42
+ plain (non-removable) tag at each tier — measured 14/17/22px
43
+ sm/md/lg before this change — so the dominant, non-removable case
44
+ has zero visual regression. Removable tags (previously inflated
45
+ by the oversized dismiss button, see --tag-dismiss-size) now
46
+ settle at or above this same floor instead of exceeding it. */
47
+ --tag-min-h: 1.0625rem; /* 17px @ d=1 — md */
48
+ --tag-min-h-sm: 0.875rem; /* 14px @ d=1 */
49
+ --tag-min-h-lg: 1.375rem; /* 22px @ d=1 */
33
50
  /* One rung down the --a-ui-* scale (gh#965): tags read as metadata
34
51
  chrome, not body copy. Whole tier ladder shifts with it (sm=2xs,
35
52
  lg=sm) so the tiers stay one rung apart. */
@@ -46,12 +63,34 @@ tag-ui[removable]:not([disabled]):hover {
46
63
  same rung spacing the space/type scales already use. */
47
64
  --tag-px-sm: var(--a-space-1);
48
65
  --tag-px-lg: var(--a-space-2);
66
+ /* gh#1084: py flattened toward 1px/tier now that --tag-min-h floors
67
+ the box — was --a-space-px/--a-space-1 (1px/4px), a 4x spread;
68
+ the height job moved to the floor, so the ladder can compress. */
49
69
  --tag-py-sm: var(--a-space-px);
50
- --tag-py-lg: var(--a-space-1);
70
+ --tag-py-lg: var(--a-space-0-5);
51
71
  --tag-font-sm: var(--a-ui-2xs);
52
72
  --tag-font-lg: var(--a-ui-sm);
53
73
  --tag-font-family: var(--a-font-family-ui);
54
- --tag-dismiss-size: 1rem;
74
+ /* gh#1084: was 1rem (16px). NOTE: --button-height (button.css) is a
75
+ MIN-height on the dismiss <button-ui>, but an icon-only button
76
+ also gets an explicit fixed `width: var(--button-height)`
77
+ ([data-icon-only], button.css) — so shrinking this token alone
78
+ (leaving the glyph at its old 14px) would be a no-op on height
79
+ (border(2px, top+bottom) + icon(14px) = 16px content already
80
+ exceeded the old 16px floor exactly, so content — not the
81
+ floor — was driving height) and would CLIP the width (a smaller
82
+ fixed width vs. the still-16px icon+border content). The real
83
+ fix is shrinking the glyph itself — see tag.class.js's dismiss
84
+ template, which now gives the icon its own `size="0.75rem"`
85
+ (12px) instead of leaning on a `--a-icon-size` CSS override here
86
+ (that override is dead — see the [slot="dismiss"] rule below) —
87
+ and re-deriving this token to match: new icon (12px) + the
88
+ button's own 1px top/bottom border (2px) = 14px, so width and
89
+ the min-height floor both land exactly on the new content size —
90
+ no floor/content mismatch, no clipping. Net: the dismiss button
91
+ (and its glyph) really is ~2px smaller in both dimensions, at
92
+ every [size] tier (this token isn't tiered). */
93
+ --tag-dismiss-size: 0.875rem;
55
94
  --tag-bg-hover: var(--a-bg-hover);
56
95
  --tag-bg-disabled: var(--a-ui-bg-disabled);
57
96
  --tag-fg-disabled: var(--a-ui-text-disabled);
@@ -101,6 +140,11 @@ tag-ui[removable]:not([disabled]):hover {
101
140
  label still centers instead of reading left-aligned in an oversized
102
141
  pill. */
103
142
  width: fit-content;
143
+ /* gh#1084: floor, not a fixed size — mirrors tags-input-ui's own
144
+ min-height (tags-input.css). Decouples the chip's vertical size
145
+ from --tag-py so padding can stay minimal; content taller than
146
+ the floor (e.g. a slotted leading icon) still grows the box. */
147
+ min-height: var(--tag-min-h);
104
148
  justify-content: center;
105
149
  gap: var(--tag-gap);
106
150
  padding: var(--tag-py) var(--tag-px);
@@ -291,16 +335,19 @@ tag-ui[removable]:not([disabled]):hover {
291
335
  --tag-px: var(--tag-px-sm);
292
336
  --tag-py: var(--tag-py-sm);
293
337
  --tag-font: var(--tag-font-sm);
338
+ --tag-min-h: var(--tag-min-h-sm);
294
339
  }
295
340
  :scope[size="md"] {
296
341
  --tag-px: var(--a-space-1-5);
297
- --tag-py: var(--a-space-0-5);
342
+ --tag-py: var(--a-space-px);
298
343
  --tag-font: var(--a-ui-xs);
344
+ --tag-min-h: 1.0625rem; /* 17px @ d=1 — matches the :where(:scope) default */
299
345
  }
300
346
  :scope[size="lg"] {
301
347
  --tag-px: var(--tag-px-lg);
302
348
  --tag-py: var(--tag-py-lg);
303
349
  --tag-font: var(--tag-font-lg);
350
+ --tag-min-h: var(--tag-min-h-lg);
304
351
  }
305
352
 
306
353
  /* hover rule moved outside @scope — see Safari 17.x bug note at top. */
@@ -345,7 +392,18 @@ tag-ui[removable]:not([disabled]):hover {
345
392
  --button-fg: var(--tag-dismiss-fg);
346
393
  --button-bg-ghost-hover: var(--tag-dismiss-bg-hover);
347
394
  --button-fg-ghost-hover: var(--tag-dismiss-fg);
348
- --a-icon-size: 0.875rem;
395
+ /* gh#1084: NO `--a-icon-size` override here — it doesn't work. This
396
+ button also carries `size="sm"` (tag.class.js), and the universal
397
+ `[size="sm"] { --a-icon-size: 0.875rem; }` rule (styles/api/
398
+ sizing.css) lives in the `utilities` cascade layer, which
399
+ `@layer reset, tokens, elements, components, utilities, …`
400
+ (styles/index.css) ranks ABOVE `components` — so it always wins
401
+ over anything this file sets on the same element, regardless of
402
+ selector specificity. The icon's rendered size is set directly
403
+ on the icon itself instead (tag.class.js's dismiss template gives
404
+ `<icon-ui size="0.75rem">` — a free-form size, which icon.class.js
405
+ applies as an inline style, outranking every layer). See
406
+ --tag-dismiss-size above — it's kept in sync with that 0.75rem. */
349
407
 
350
408
  opacity: var(--tag-dismiss-opacity);
351
409
  order: 1; /* push dismiss to end so layout reads [text] [×] */