@adia-ai/web-components 0.8.39 → 0.8.40

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 +12 -0
  2. package/MIGRATION.md +240 -0
  3. package/components/agent-artifact/agent-artifact.css +1 -1
  4. package/components/agent-reasoning/agent-reasoning.css +2 -2
  5. package/components/agent-trace/agent-trace.css +3 -3
  6. package/components/alert/alert.css +1 -1
  7. package/components/avatar/avatar.css +1 -1
  8. package/components/badge/badge.css +1 -1
  9. package/components/button/button.css +6 -5
  10. package/components/calendar-picker/calendar-picker.css +1 -1
  11. package/components/card/card.css +3 -3
  12. package/components/chart/chart.a2ui.json +5 -5
  13. package/components/chart/chart.class.js +4 -4
  14. package/components/chart/chart.css +6 -6
  15. package/components/chart/chart.d.ts +2 -2
  16. package/components/chart/chart.yaml +3 -2
  17. package/components/chat-thread/chat-thread.css +1 -1
  18. package/components/code/code.css +6 -6
  19. package/components/combobox/combobox.css +2 -2
  20. package/components/command/command.css +3 -3
  21. package/components/date-range-picker/date-range-picker.css +1 -1
  22. package/components/datetime-picker/datetime-picker.css +1 -1
  23. package/components/description-list/description-list.css +2 -2
  24. package/components/display-field/display-field.css +3 -3
  25. package/components/divider/divider.css +1 -1
  26. package/components/drawer/drawer.css +2 -2
  27. package/components/drilldown/drilldown.css +2 -2
  28. package/components/empty-state/empty-state.css +2 -2
  29. package/components/field/field.css +1 -1
  30. package/components/heatmap/heatmap.css +2 -2
  31. package/components/inline-message/inline-message.css +1 -1
  32. package/components/input/input.css +5 -4
  33. package/components/integration-card/integration-card.css +4 -4
  34. package/components/kbd/kbd.css +1 -1
  35. package/components/list/list.css +2 -2
  36. package/components/nav/nav.css +2 -2
  37. package/components/nav-group/nav-group.css +3 -3
  38. package/components/nav-item/nav-item.css +2 -2
  39. package/components/option-card/option-card.css +1 -1
  40. package/components/otp-input/otp-input.css +1 -1
  41. package/components/page/page.css +2 -2
  42. package/components/pagination/pagination.css +1 -1
  43. package/components/pane/pane.css +1 -1
  44. package/components/progress-row/progress-row.css +2 -2
  45. package/components/qr-code/qr-code.a2ui.json +10 -10
  46. package/components/qr-code/qr-code.class.js +3 -3
  47. package/components/qr-code/qr-code.d.ts +10 -7
  48. package/components/qr-code/qr-code.yaml +13 -9
  49. package/components/range/range.css +1 -1
  50. package/components/rating/rating.css +1 -1
  51. package/components/richtext/richtext.css +3 -3
  52. package/components/segment/segment.css +5 -4
  53. package/components/select/select.css +3 -3
  54. package/components/spinner/spinner.css +1 -1
  55. package/components/stat/stat.css +3 -3
  56. package/components/step-progress/step-progress.css +1 -1
  57. package/components/stepper/stepper.css +2 -2
  58. package/components/swatch/swatch.css +2 -2
  59. package/components/table/table.a2ui.json +0 -10
  60. package/components/table/table.class.js +4 -4
  61. package/components/table/table.css +6 -9
  62. package/components/table/table.d.ts +0 -2
  63. package/components/table/table.yaml +0 -9
  64. package/components/table-toolbar/table-toolbar.css +1 -1
  65. package/components/tabs/tabs.css +1 -1
  66. package/components/tag/tag.css +2 -2
  67. package/components/tags-input/tags-input.css +1 -1
  68. package/components/textarea/textarea.css +1 -1
  69. package/components/time-picker/time-picker.css +1 -1
  70. package/components/timeline/timeline.css +4 -4
  71. package/components/toc/toc.css +1 -1
  72. package/components/toggle-group/toggle-group.class.js +3 -11
  73. package/components/toggle-group/toggle-group.css +1 -3
  74. package/components/toggle-group/toggle-group.d.ts +1 -2
  75. package/components/toggle-group/toggle-group.yaml +1 -4
  76. package/components/toggle-group/toggle-option.a2ui.json +1 -1
  77. package/components/toggle-group/toggle-option.yaml +2 -6
  78. package/components/tooltip/tooltip.css +1 -1
  79. package/components/tour/tour.css +2 -2
  80. package/components/tree/tree.css +1 -1
  81. package/custom-elements.json +7 -15
  82. package/dist/host.min.css +1 -1
  83. package/dist/host.sheet.js +1 -1
  84. package/dist/theme-provider.min.js +1 -1
  85. package/dist/web-components.min.css +1 -1
  86. package/dist/web-components.min.js +89 -89
  87. package/dist/web-components.sheet.js +1 -1
  88. package/package.json +1 -1
  89. package/styles/type/scale.css +6 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.8.40] — 2026-08-15
4
+
5
+ ### Changed
6
+ - **BREAKING (ships in 0.8.40, no deprecation alias): `toggle-group-ui` drops the legacy `data-selected` parallel stamp on `toggle-option-ui` children, per [ADR-0056](../../docs/adr/adr-0056-selection-item-selected-convention.md) (gh#1303).** The additive half of ADR-0056 (`0.8.39`) shipped `toggle-option-ui`'s declared, reflected `selected` prop alongside the pre-existing `data-selected` stamp, scoped that way pending operator sign-off on when the breaking removal would ride. That removal now lands: `toggle-group.class.js`'s render pass no longer sets/clears `data-selected`; `selected` (already the only prop `toggle-group.css` styled) is the sole current-state surface, matching `segment-ui`'s convention. The `DEPRECATED` markers in `toggle-option.yaml`/`toggle-group.yaml` are removed along with the attribute they described. `toggle-group.test.js`'s named deprecation case (`'DEPRECATED — still stamps data-selected in parallel'`) flips to assert the stamp is gone. See the migration guide's [§ Upcoming — `toggle-option-ui` drops the legacy `data-selected` stamp](../../.claude/docs/MIGRATION%20GUIDE.md#upcoming--toggle-option-ui-drops-the-legacy-data-selected-stamp-0840-adr-0056).
7
+ - **`table-ui[density]` converges onto the global attribute grammar (ADR-0054, gh#1300).** The component-local `compact | standard | comfortable` enum is removed (`table.yaml`, the reflected `density` prop + `getState()`/`setState()` persistence in `table.class.js`, the `:scope[density="…"]` CSS overrides in `table.css`) — `[density]` on `table-ui` is now the ordinary ambient global attribute (`compact | spacious`, unset = the middle), which `--table-py`/`--table-px` already read via `--a-space-*` (no CSS change needed beyond removing the now-redundant local override). **Breaking, no deprecation alias:** `density="compact"` is unchanged, `density="standard"` becomes the absent attribute, `density="comfortable"` becomes `density="spacious"`. Rides the ratified **0.8.40** breaking wave.
8
+ - **`chart-ui[radius]` renamed to `chart-ui[corner-radius]` (ADR-0054, gh#1300).** Same `Number` type, same `default: null` (falls back to `--a-radius`), same behavior — the collision was with the global preset-enum `[radius]` (`sharp`/`rounded`/`round`), which a numeric px value can't share a name with. **Breaking, no deprecation alias**, rides the ratified **0.8.40** breaking wave.
9
+ - **BREAKING (ships in 0.8.40): `qr-code-ui`'s reflected `[margin]` attribute is renamed `[quiet-zone]` (property `quietZone`), per [ADR-0053](../../docs/adr/adr-0053-no-shadowing-global-attributes.md) (gh#1299).** `qr-code-ui[margin]` silently collided with the framework's global `[margin]` Scale attribute — both accepted the identical `margin="4"` syntax but resolved to unrelated meanings (QR quiet zone in grid cells vs. global box-model spacing) with no error surfaced. Same type/default/reflection; only the name changes. No deprecation alias — `margin` on `qr-code-ui` now means the global Scale attribute. See the migration guide's [§ Upcoming — qr-code-ui quiet-zone rename](../../.claude/docs/MIGRATION%20GUIDE.md#upcoming--qr-code-ui-quiet-zone-rename-adr-0053).
10
+ - **BREAKING (ships in 0.8.40): the legacy `--a-leading-*` line-height token family is retired; `--a-font-leading-*` is the single canonical leading scale, per [ADR-0052](../../docs/adr/adr-0052-leading-scale-consolidation.md) (gh#1298).** Two leading families coexisted in `styles/type/scale.css` with the same step names resolving to different values (`--a-leading-snug` = `1.3` vs. `--a-font-leading-snug` = `1.2`) — no obviously-correct token choice, which is why 55 component files had reached for a bare numeric literal instead. `--a-font-leading-*` gains a `none: 1` step to close its only gap; the 4 in-repo `--a-leading-*` consumers (`button`, `chat-thread`, `inline-message`, `segment`) are migrated by value-nearest judgment (no name-preserving aliases: `snug`→`normal`, `normal`→`relaxed`). The same sweep tokenized every other hardcoded typography literal in `components/*/*.css`: bare numeric `line-height` (56 files; `1.3`/`1.4` → `--a-font-leading-normal`, escape-valve `--a-font-leading-relaxed` for multi-line body copy), `em`-literal `letter-spacing` (12 files; `0.05em` → `--a-font-tracking-wide`), numeric `font-weight` (7 files, on-scale). New `npm run check:typography-tokens` gate (mirrors the brand-chrome/foundation-layer audit gates) fails on any of the three literal classes reappearing. See the migration guide's [§ Upcoming — leading-scale consolidation](../../.claude/docs/MIGRATION%20GUIDE.md#upcoming--leading-scale-consolidation-adr-0052).
11
+
12
+ ### Maintenance
13
+ - **`dist/` bundles rebuilt** in this cut's window (6 file(s)) — regenerated from the source changes described above, not independent edits.
14
+
3
15
  ## [0.8.39] — 2026-08-15
4
16
 
5
17
  ### Changed
package/MIGRATION.md CHANGED
@@ -54,6 +54,14 @@ Version cuts and migration scope:
54
54
 
55
55
  - **`0.8.37`** — PATCH lockstep cut. **BREAKING: the a2ui / gen-ui estate split (ADR-0048).** Six published package names (`a2ui-runtime`, `a2ui-compose`, `a2ui-corpus`, `a2ui-retrieval`, `a2ui-validator`, `a2ui-mcp`) are replaced by four (`a2ui`, `gen-ui`, `gen-ui-mcp`, `a2ui-protocol-mcp`). **A patch-shaped version number does not make this non-breaking** — every old name is renamed. One-cycle re-export shims bridge the gap; the cut AFTER `0.8.37` retires them. See [§ 0.8.37 — the a2ui / gen-ui estate split](#0837--the-a2ui--gen-ui-estate-split-adr-0048).
56
56
 
57
+ - **Upcoming (`0.8.40` — attribute-grammar reconciliation, ADR-0054)** — **BREAKING, no deprecation alias:** `table-ui[density]` converges onto the ambient global `[density]` attribute — the component-local `compact`/`standard`/`comfortable` enum is removed. Migration: `density="compact"` stays `density="compact"`; `density="standard"` → drop the attribute (unset is the default); `density="comfortable"` → `density="spacious"`. `chart-ui[radius]` renames to `chart-ui[corner-radius]` (same `Number`/`null` semantics, same `--a-radius` fallback) — a mechanical attribute-name rename, no value-shape change. `swatch-ui[color]` and `noodles-ui[color]` are ratified exceptions and are unaffected. See [§ Upcoming — 0.8.40 attribute-grammar reconciliation](#upcoming-0840-attribute-grammar-reconciliation-adr-0054).
58
+
59
+ - **Upcoming (`qr-code-ui` quiet-zone rename, `0.8.40`)** — **BREAKING:** `<qr-code-ui>`'s reflected `[margin]` attribute is renamed `[quiet-zone]` (property `quietZone`) — same type, default (`4`), and reflection; only the name changes, per [ADR-0053](../../docs/adr/adr-0053-no-shadowing-global-attributes.md). `margin` on `qr-code-ui` now means the framework's **global** `[margin]` Scale attribute (box-model spacing), not the QR quiet zone — the two grammars silently collided on the same syntax with unrelated meanings. **No deprecation alias** — keeping `margin` as an alias would preserve the collision the rename exists to remove. Audit existing usages: `<qr-code-ui margin="4">` intending a quiet zone must become `<qr-code-ui quiet-zone="4">`. Ships in `0.8.40`. See [§ Upcoming — qr-code-ui quiet-zone rename](#upcoming--qr-code-ui-quiet-zone-rename-adr-0053).
60
+
61
+ - **Upcoming (leading-scale consolidation, `0.8.40`)** — **BREAKING, no aliases:** the legacy `--a-leading-*` token family is removed per [ADR-0052](../../docs/adr/adr-0052-leading-scale-consolidation.md) — `--a-font-leading-*` (+ new `--a-font-leading-none: 1` step) is now the single canonical leading scale. Value-nearest replacement table: `none→--a-font-leading-none` (1), `tight→--a-font-leading-snug` (1.2), `snug→--a-font-leading-normal` (1.35), `normal→--a-font-leading-relaxed` (1.5). The same gh#1298 sweep also tokenized every hardcoded `line-height`/`letter-spacing`/`font-weight` literal across `packages/web-components/components/*/*.css` (1.3 and 1.4 both → `--a-font-leading-normal`; 0.05em → `--a-font-tracking-wide`) and added a mechanized `npm run check:typography-tokens` gate. See [§ Upcoming — leading-scale consolidation](#upcoming--leading-scale-consolidation-adr-0052).
62
+
63
+ - **Upcoming (`toggle-option-ui` drops `data-selected`, `0.8.40`)** — **BREAKING, no deprecation alias:** `<toggle-group-ui>`'s legacy `data-selected` parallel stamp on its `<toggle-option-ui>` children is removed per [ADR-0056](../../docs/adr/adr-0056-selection-item-selected-convention.md) — the declared, reflected `selected` prop (shipped additively in `0.8.39`) is now the only current-state surface, matching `<segment-ui>`'s convention. Migration: any `[data-selected]` CSS or script targeting `toggle-option-ui` moves to `[selected]`. See [§ Upcoming — `toggle-option-ui` drops the legacy `data-selected` stamp](#upcoming--toggle-option-ui-drops-the-legacy-data-selected-stamp-0840-adr-0056).
64
+
57
65
  Live demos and per-component docs at [ui-kit.exe.xyz/site/](https://ui-kit.exe.xyz/site/). The `adia-ui-migration` skill (in `.agents/skills/`) automates most of the mechanical sweeps in this guide.
58
66
 
59
67
  ---
@@ -384,6 +392,238 @@ npm run check # in-repo consumers; downstream: your app's build + a visual pas
384
392
 
385
393
  ---
386
394
 
395
+ ## Upcoming — `0.8.40` attribute-grammar reconciliation (ADR-0054)
396
+
397
+ **BREAKING, no deprecation alias.** [ADR-0054](../../docs/adr/adr-0054-attribute-grammar-reconciliation.md)
398
+ resolves two component-local attributes that shadowed the [global attribute
399
+ grammar](../../.claude/docs/specs/attribute-api-system.md) with divergent vocabularies —
400
+ `table-ui[density]` **converges** onto the global mechanism (it already did
401
+ the job), `chart-ui[radius]` **renames** (a numeric px value is a genuinely
402
+ different shape than the global preset enum). Per [ADR-0053](../../docs/adr/adr-0053-no-shadowing-global-attributes.md),
403
+ neither collision is exempted — `swatch-ui[color]` / `noodles-ui[color]` are
404
+ the only two ratified exceptions (documented in
405
+ [§11 of attribute-api-system.md](../../.claude/docs/specs/attribute-api-system.md#11-global-attribute-exemption-list)),
406
+ and neither is touched by this cut.
407
+
408
+ ### `table-ui[density]` converges onto the global grammar
409
+
410
+ The component-local `compact | standard | comfortable` enum is removed —
411
+ `table-ui` no longer reflects a `density` prop or JS property at all.
412
+ `[density]` on `table-ui` is now the ordinary ambient global attribute
413
+ (`compact | spacious`, unset = the middle) — `--table-py`/`--table-px`
414
+ already derive from `--a-space-*` steps, which the global `[density]`
415
+ attribute already scales via `--a-density`.
416
+
417
+ **Value migration** (apply wherever `<table-ui density="…">` appears):
418
+
419
+ | Old value | New value |
420
+ |---|---|
421
+ | `density="compact"` | `density="compact"` (unchanged) |
422
+ | `density="standard"` | drop the attribute entirely (unset is the default) |
423
+ | `density="comfortable"` | `density="spacious"` |
424
+
425
+ ```bash
426
+ # find every table-ui density usage in your tree
427
+ git grep -nE '<table-ui[^>]*\bdensity='
428
+
429
+ # comfortable → spacious (mechanical)
430
+ git grep -lz '<table-ui[^>]*density="comfortable"' | while IFS= read -r -d '' f; do
431
+ perl -i -pe 's/(<table-ui[^>]*density=")comfortable(")/${1}spacious${2}/g' "$f"
432
+ done
433
+
434
+ # standard → drop the attribute (review each hit; not always a safe blind sed
435
+ # across multi-line tags)
436
+ git grep -nE '<table-ui[^>]*density="standard"'
437
+ ```
438
+
439
+ If you read/write a table's `density` as a JS property (`el.density`) or
440
+ persisted it via `table-ui`'s `getState()`/`setState()`, switch to
441
+ `getAttribute('density')`/`setAttribute('density', …)` — the reflected
442
+ property and the `getState()`/`setState()` `density` key are both gone.
443
+ Composites that forward density to an embedded `table-ui` (own component,
444
+ own vocabulary) must translate through the same table above before setting
445
+ the child's attribute — see `invoice-history.class.js`'s `mapDensity()` for
446
+ the pattern.
447
+
448
+ ### `chart-ui[radius]` renames to `chart-ui[corner-radius]`
449
+
450
+ Same `Number` type, same `default: null` (falls back to `--a-radius`), same
451
+ behavior — only the attribute name changes.
452
+
453
+ ```bash
454
+ git grep -nE '<chart-ui[^>]*\bradius='
455
+ git grep -lz '<chart-ui[^>]*radius="' | while IFS= read -r -d '' f; do
456
+ perl -i -pe 's/(<chart-ui[^>]*)\bradius=/${1}corner-radius=/g' "$f"
457
+ done
458
+ ```
459
+
460
+ If you set it via JS, `chart.radius = 8` → `chart.cornerRadius = 8`.
461
+
462
+ ### Verify
463
+
464
+ ```bash
465
+ git grep -nE '<table-ui[^>]*\bdensity="(standard|comfortable)"' # must be 0
466
+ git grep -nE '<chart-ui[^>]*\bradius=' # must be 0
467
+ npm run check
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Upcoming — qr-code-ui quiet-zone rename (ADR-0053)
473
+
474
+ **BREAKING, ships in `0.8.40`.** `<qr-code-ui>`'s reflected `[margin]` attribute is
475
+ renamed `[quiet-zone]` (property `quietZone`) — same `number` type, same default (`4`), same
476
+ reflection; only the name changes.
477
+
478
+ **Why:** the framework's global attribute grammar (`styles/api/*.css`) is a pitch — one
479
+ grammar, inferable: `margin="4"` means the same thing on every element, resolving through one
480
+ Scale mechanism (`--a-margin`). `qr-code-ui` declared its own reflected `margin` meaning the QR
481
+ "quiet zone" in grid cells. Both grammars accepted the identical syntax with no error surfaced,
482
+ so an author reaching for `<qr-code-ui margin="4">` expecting global spacing silently got a
483
+ quiet-zone resize instead, and vice versa. [ADR-0053](../../docs/adr/adr-0053-no-shadowing-global-attributes.md)
484
+ rules that no component-local attribute may shadow a global one; this is the first instance
485
+ resolved under that policy (gh#1299).
486
+
487
+ **No deprecation alias.** Keeping `margin` as an alias on `qr-code-ui` would preserve the exact
488
+ collision the rename exists to remove — `margin` on `qr-code-ui` reverts to meaning what it
489
+ means everywhere: the global Scale attribute.
490
+
491
+ ### Mechanical sweep
492
+
493
+ ```bash
494
+ git grep -nE '<qr-code-ui[^>]*\smargin='
495
+ ```
496
+
497
+ Every hit intending the QR quiet zone becomes `quiet-zone`:
498
+
499
+ ```html
500
+ <!-- before --> <qr-code-ui value="…" margin="4"></qr-code-ui>
501
+ <!-- after --> <qr-code-ui value="…" quiet-zone="4"></qr-code-ui>
502
+ ```
503
+
504
+ **Programmatic setters** (JS render paths — the grep above misses these):
505
+
506
+ ```bash
507
+ git grep -rnE "setAttribute\('margin', |\.margin\s*="
508
+ ```
509
+
510
+ Filter to `qr-code-ui` hosts only — `margin` is a legitimate global attribute on every other
511
+ element, so only rewrite a hit that is actually a `<qr-code-ui>` reference:
512
+ `setAttribute('margin', …)` → `setAttribute('quiet-zone', …)`; `el.margin = …` → `el.quietZone
513
+ = …`.
514
+
515
+ ### Verify
516
+
517
+ ```bash
518
+ git grep -nE '<qr-code-ui[^>]*\smargin=' # must be 0
519
+ npm run check # in-repo consumers; downstream: your app's build
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Upcoming — leading-scale consolidation (ADR-0052)
525
+
526
+ **BREAKING, no aliases, ships in `0.8.40`.** [ADR-0052](../../docs/adr/adr-0052-leading-scale-consolidation.md)
527
+ retires the legacy `--a-leading-*` family. `--a-font-leading-*` is now the
528
+ single canonical line-height scale.
529
+
530
+ **Why:** two leading-token families coexisted in `scale.css` with the same
531
+ step names resolving to *different* values (`--a-leading-snug` = `1.3` vs.
532
+ `--a-font-leading-snug` = `1.2`). A component author reaching for a leading
533
+ token had no obviously-correct choice — which is why a corpus-wide sweep
534
+ (gh#1298) found 55 component files reaching for a bare numeric literal
535
+ instead of either family. `--a-font-leading-*` is what the L2 role layer
536
+ already consumes and its shape (tightening as size grows) is the design
537
+ intent on record. One step closes its only gap: `--a-font-leading-none: 1`.
538
+
539
+ **No value-preserving aliases.** `snug`/`normal` name different values
540
+ across the two families — aliasing one name to the other would silently
541
+ restyle every consumer of the alias. Replacement table (value-nearest, not
542
+ name-preserving):
543
+
544
+ | Removed | Value | Replacement | Value |
545
+ |---|---|---|---|
546
+ | `--a-leading-none` | `1` | `--a-font-leading-none` | `1` |
547
+ | `--a-leading-tight` | `1.2` | `--a-font-leading-snug` | `1.2` |
548
+ | `--a-leading-snug` | `1.3` | `--a-font-leading-normal` | `1.35` |
549
+ | `--a-leading-normal` | `1.5` | `--a-font-leading-relaxed` | `1.5` |
550
+
551
+ A surviving `var(--a-leading-*)` reference goes invalid-at-computed-value-time
552
+ → inherit (same failure shape as the `--a-font` alias removal in `0.8.23`).
553
+
554
+ ### The same sweep also tokenized every other hardcoded typography literal
555
+
556
+ Beyond the legacy family, gh#1298's corpus sweep found — and tokenized —
557
+ every bare numeric `line-height`, `em`-literal `letter-spacing`, and numeric
558
+ `font-weight` in `packages/web-components/components/*/*.css`:
559
+
560
+ - **`line-height`** — mapped to the nearest `--a-font-leading-*` step. Both
561
+ `1.3` and `1.4` map to `--a-font-leading-normal` (`1.35`) — no new step was
562
+ minted (at the 12–14px sizes where these values appeared, ±0.05
563
+ line-height is below visual significance). A handful of `1.4` hits on
564
+ genuinely multi-line body/description copy were mapped up to
565
+ `--a-font-leading-relaxed` (`1.5`) instead, case-by-case — see the sweep
566
+ PR description for the enumerated list. `line-height: 0` (the
567
+ icon/line-box-collapse idiom used by `icon.css`, `swatch.css`,
568
+ `qr-code.css`, etc.) is **not** a typographic leading value and was left
569
+ untouched.
570
+ - **`letter-spacing`** — mapped to the nearest `--a-font-tracking-*` step;
571
+ `0.05em` (off-scale) maps to `--a-font-tracking-wide` (`0.04em`), same
572
+ nearest-step logic as the leading family.
573
+ - **`font-weight`** — all hits were already on-scale and map mechanically to
574
+ `--a-weight-*` (`500`→medium, `600`→semibold, `700`→bold).
575
+
576
+ ### A mechanized gate follows the sweep
577
+
578
+ `npm run check:typography-tokens` (wired into the `npm run check` aggregate)
579
+ fails on any bare numeric `line-height`, `em`-literal `letter-spacing`, or
580
+ numeric `font-weight` reappearing in `packages/web-components/components/*/*.css`
581
+ — mirroring the existing brand-chrome/foundation-layer audit gates — so the
582
+ parallel scale (and the literal-reaching it caused) cannot silently
583
+ reaccumulate.
584
+
585
+ ### Mechanical sweep
586
+
587
+ ```bash
588
+ git grep -nE -- '--a-leading-(none|tight|snug|normal)\b'
589
+ ```
590
+
591
+ Apply the replacement table above at each hit — do not blind-`sed`, since
592
+ `snug`/`normal` map to *different* replacement names than their own name
593
+ would suggest.
594
+
595
+ ### Verify
596
+
597
+ ```bash
598
+ git grep -nE -- '--a-leading-(none|tight|snug|normal)\b' # must be 0
599
+ npm run check:typography-tokens # must exit 0
600
+ npm run check
601
+ ```
602
+
603
+ ---
604
+
605
+ ## Upcoming — `toggle-option-ui` drops the legacy `data-selected` stamp (`0.8.40`, ADR-0056)
606
+
607
+ **BREAKING, no deprecation alias.** [ADR-0056](../../docs/adr/adr-0056-selection-item-selected-convention.md)'s
608
+ breaking half removes `<toggle-group-ui>`'s legacy `data-selected` attribute
609
+ stamp on its `<toggle-option-ui>` children. The declared, reflected `selected`
610
+ prop (shipped additively in `0.8.39`) is now the *only* current-state surface
611
+ — matching `<segment-ui>`'s existing convention.
612
+
613
+ **Migration:** any external CSS or script targeting `[data-selected]` on
614
+ `toggle-option-ui` must move to `[selected]` — same semantics, same timing,
615
+ just the attribute name. `toggle-group.css` itself already styled
616
+ `[selected]`; the removed stamp was a parallel, undocumented hook with no
617
+ declared API surface (invisible to the yaml SoT, the catalog, and the A2UI
618
+ grammar).
619
+
620
+ ```bash
621
+ git grep -n "data-selected" -- '*.css' '*.js' '*.html' | grep -i toggle # must be 0
622
+ npm run check
623
+ ```
624
+
625
+ ---
626
+
387
627
  ## Migrating to `@adia-ai/*@v0.8.23` (2026-07-30)
388
628
 
389
629
  - **Removed: the `--a-font` alias** (pre-ADR-0035 name, DEPRECATED since the 0.8.x line). Any surviving `font-family: var(--a-font)` goes invalid-at-computed-value-time → `inherit` → UA **serif** — the visible symptom is Times-faced labels on an otherwise-correct page.
@@ -80,7 +80,7 @@
80
80
  uppercase kind reads as a tag, not a label. */
81
81
  [data-artifact-kind] {
82
82
  flex-shrink: 0;
83
- letter-spacing: 0.04em;
83
+ letter-spacing: var(--a-font-tracking-wide);
84
84
  }
85
85
 
86
86
  [data-artifact-actions] {
@@ -171,7 +171,7 @@
171
171
  display: grid;
172
172
  grid-template-columns: subgrid;
173
173
  align-items: center;
174
- line-height: 1.5;
174
+ line-height: var(--a-font-leading-relaxed);
175
175
  color: var(--agent-reasoning-fg-muted);
176
176
  animation: _ar-fade var(--agent-reasoning-duration) var(--agent-reasoning-easing);
177
177
  }
@@ -199,7 +199,7 @@
199
199
  font-size: var(--agent-reasoning-font);
200
200
  color: var(--agent-reasoning-fg-subtle);
201
201
  text-transform: uppercase;
202
- letter-spacing: 0.04em;
202
+ letter-spacing: var(--a-font-tracking-wide);
203
203
  font-weight: var(--agent-reasoning-weight-heading);
204
204
  }
205
205
 
@@ -167,7 +167,7 @@
167
167
  [data-trace-header] {
168
168
  font-size: calc(var(--agent-trace-font) * 0.85);
169
169
  text-transform: uppercase;
170
- letter-spacing: 0.06em;
170
+ letter-spacing: var(--a-font-tracking-wider);
171
171
  font-weight: var(--a-weight-medium);
172
172
  color: var(--agent-trace-fg-subtle);
173
173
  }
@@ -289,7 +289,7 @@
289
289
  font-size: calc(var(--agent-trace-label-font) * 0.85);
290
290
  font-weight: var(--a-weight-medium);
291
291
  text-transform: uppercase;
292
- letter-spacing: 0.06em;
292
+ letter-spacing: var(--a-font-tracking-wider);
293
293
  padding-block-end: var(--a-space-1);
294
294
  }
295
295
 
@@ -301,7 +301,7 @@
301
301
  grid-template-columns: 1em 1fr;
302
302
  column-gap: var(--agent-trace-inline-gap);
303
303
  align-items: baseline;
304
- line-height: 1.5;
304
+ line-height: var(--a-font-leading-relaxed);
305
305
  font-size: var(--agent-trace-font);
306
306
  color: var(--agent-trace-fg);
307
307
  }
@@ -207,7 +207,7 @@
207
207
  :scope[pattern="dunning"] [data-dunning-amount] {
208
208
  font-family: var(--alert-amount-font);
209
209
  font-variant-numeric: tabular-nums;
210
- font-weight: 600;
210
+ font-weight: var(--a-weight-semibold);
211
211
  /* Inherit color from the variant; tabular-nums + monospace make
212
212
  the amount the scannable focal datum. */
213
213
  }
@@ -46,7 +46,7 @@
46
46
  [slot="initials"] {
47
47
  user-select: none;
48
48
  text-transform: uppercase;
49
- line-height: 1;
49
+ line-height: var(--a-font-leading-none);
50
50
  }
51
51
 
52
52
  /* ── Icon (non-person avatar) ──
@@ -89,7 +89,7 @@
89
89
  font-family: var(--badge-font-family);
90
90
  font-size: var(--badge-font-size);
91
91
  font-weight: var(--badge-weight);
92
- line-height: 1;
92
+ line-height: var(--a-font-leading-none);
93
93
  /* ── Register (issue 845) — uppercase + tracking + tabular numerals is the
94
94
  badge/tag differentiator now that geometry is shared (see the
95
95
  --badge-radius note above). Tag stays proportional sentence-case at
@@ -89,10 +89,11 @@ button-ui[variant="ghost"][color="warning"]:not([disabled]):hover { --button-f
89
89
  blocks), but the label's ::after clips to its own line box, so
90
90
  inheriting 1 makes half-leading negative and shears descenders
91
91
  (g/j/p/q/y, parentheses) by ~0.93px at the default size — gh#906.
92
- Snug (1.3) contains the font's content box at every [size]; the
92
+ Normal (1.35) contains the font's content box at every [size]; the
93
93
  precedent is stat.css:174,198 setting leading on the truncating
94
- box rather than on the component root. */
95
- --button-label-line-height: var(--a-leading-snug);
94
+ box rather than on the component root. Value-nearest migration from
95
+ the retired --a-leading-snug (1.3) — ADR-0052. */
96
+ --button-label-line-height: var(--a-font-leading-normal);
96
97
  --button-gap: var(--a-space-1);
97
98
  --button-bg-hover: var(--a-bg-strong); /* neutral fill → neutral -strong surface on hover */
98
99
  --button-fg-hover: var(--a-ui-text-hover);
@@ -160,7 +161,7 @@ button-ui[variant="ghost"][color="warning"]:not([disabled]):hover { --button-f
160
161
  font-family: var(--button-font-family);
161
162
  font-size: var(--button-font-size);
162
163
  font-weight: var(--button-font-weight);
163
- line-height: 1;
164
+ line-height: var(--a-font-leading-none);
164
165
  cursor: pointer;
165
166
  user-select: none;
166
167
  transition:
@@ -211,7 +212,7 @@ button-ui[variant="ghost"][color="warning"]:not([disabled]):hover { --button-f
211
212
  border: 1px solid var(--button-trailing-border);
212
213
  border-radius: var(--button-trailing-radius);
213
214
  padding: 0 var(--button-trailing-px);
214
- line-height: 1;
215
+ line-height: var(--a-font-leading-none);
215
216
  /* Recolor a <kbd-ui slot="trailing"> pill via kbd's OWN tokens so it tracks
216
217
  the button label color on every variant (primary white, ghost subtle,
217
218
  colored fills). kbd-ui's `:scope { color: var(--kbd-fg) }` wins over the
@@ -141,7 +141,7 @@
141
141
  border-radius: var(--calendar-picker-trigger-radius);
142
142
  background: var(--calendar-picker-trigger-bg);
143
143
  color: var(--calendar-picker-trigger-fg);
144
- line-height: 1;
144
+ line-height: var(--a-font-leading-none);
145
145
  cursor: pointer;
146
146
  transition:
147
147
  border-color var(--calendar-picker-duration) var(--calendar-picker-easing),
@@ -271,7 +271,7 @@
271
271
  & > header > :is(text-ui[variant="display"], text-ui[variant="title"], text-ui[variant="heading"], text-ui[variant="subsection"]):not([slot]),
272
272
  & > header > [slot="heading"] :is(h1, h2, h3, h4, h5, h6) {
273
273
  grid-row: 1;
274
- line-height: 1.3;
274
+ line-height: var(--a-font-leading-normal);
275
275
  margin: 0;
276
276
  }
277
277
  /* Heading slot is a flex container — can hold title + inline badges/metadata.
@@ -304,7 +304,7 @@
304
304
  & > header > :is(text-ui[variant="body"], text-ui[variant="caption"]):not([slot]) {
305
305
  grid-row: 2;
306
306
  grid-column: 1 / -1;
307
- line-height: 1.4;
307
+ line-height: var(--a-font-leading-relaxed);
308
308
  margin: 0;
309
309
  }
310
310
  & > header:has(> [slot="icon"]) > :is([slot="description"], p, small) { grid-column: 2 / -1; }
@@ -489,7 +489,7 @@
489
489
  font-size: var(--card-font-size);
490
490
  font-weight: var(--a-weight-medium);
491
491
  color: var(--card-heading-fg);
492
- line-height: 1.3;
492
+ line-height: var(--a-font-leading-normal);
493
493
  margin: 0;
494
494
  flex: 0 0 auto;
495
495
  }
@@ -53,6 +53,11 @@
53
53
  "component": {
54
54
  "const": "Chart"
55
55
  },
56
+ "cornerRadius": {
57
+ "description": "Bar corner radius in px (null = let CSS tokens decide)",
58
+ "type": "number",
59
+ "default": null
60
+ },
56
61
  "data": {
57
62
  "description": "JS property (set programmatically — `el.data = [...]`). An array of plain objects; each object's keys are named by the `x` and `y` attributes — e.g. `<chart-ui x=\"month\" y=\"revenue\">` consumes `[{month:'Jan', revenue:3200}, {month:'Feb', revenue:4100}]`. The Chart.js `{labels, datasets}` envelope is NOT chart-ui's API — passing it (or any non-array value) renders an empty chart. May also be supplied declaratively as a JSON-array `data=\"[…]\"` attribute, hydrated once at connect. Custom accessor on the element class, not a reflected attribute.",
58
63
  "$ref": "common_types.json#/$defs/DynamicObjectList"
@@ -99,11 +104,6 @@
99
104
  "type": "boolean",
100
105
  "default": false
101
106
  },
102
- "radius": {
103
- "description": "Bar corner radius (null = let CSS tokens decide)",
104
- "type": "number",
105
- "default": null
106
- },
107
107
  "seriesEmphasis": {
108
108
  "description": "Multi-line only. Names one series key (matching a `y` key) to render at full strength — area fill + full-opacity line. Every other series drops its area fill and renders line-only at --chart-deemphasized-opacity. Empty (default) applies full treatment to every series, unchanged from pre-gh#561 behavior.",
109
109
  "type": "string",
@@ -25,7 +25,7 @@
25
25
  * color — accent/success/warning/danger/info
26
26
  * hide-grid — hide gridlines
27
27
  * hide-values — hide value labels
28
- * radius — bar corner radius (default: 4)
28
+ * corner-radius — bar corner radius in px (default: null, falls back to --a-radius)
29
29
  * dots — line/multi-line marker visibility: all|none|hover|last (default: all)
30
30
  * series-emphasis — multi-line: series key that keeps full area+line treatment; others render line-only, dimmed
31
31
  *
@@ -219,7 +219,7 @@ export class UIChart extends UIElement {
219
219
  color: { type: String, default: '', reflect: true },
220
220
  hideGrid: { type: Boolean, default: false, reflect: true, attribute: 'hide-grid' },
221
221
  hideValues: { type: Boolean, default: false, reflect: true, attribute: 'hide-values' },
222
- radius: { type: Number, default: null, reflect: true },
222
+ cornerRadius: { type: Number, default: null, reflect: true, attribute: 'corner-radius' },
223
223
  smooth: { type: Number, default: 0.4, reflect: true },
224
224
  size: { type: String, default: '', reflect: true },
225
225
  format: { type: String, default: 'abbr', reflect: true },
@@ -255,11 +255,11 @@ export class UIChart extends UIElement {
255
255
  re-render without a full DOM query. */
256
256
  #activeDotEl = null;
257
257
 
258
- /** Resolves the corner radius: the `radius` prop if explicitly set,
258
+ /** Resolves the corner radius: the `cornerRadius` prop if explicitly set,
259
259
  * otherwise `--a-radius` from the host's computed style. Falls back
260
260
  * to 6 if the token is unset (should not happen in practice). */
261
261
  #resolveRadius() {
262
- if (this.radius != null) return this.radius;
262
+ if (this.cornerRadius != null) return this.cornerRadius;
263
263
  const val = getComputedStyle(this).getPropertyValue('--a-radius').trim();
264
264
  const parsed = parseFloat(val);
265
265
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 6;
@@ -176,7 +176,7 @@
176
176
 
177
177
  [data-value] {
178
178
  font-size: var(--chart-font-size);
179
- font-weight: 600;
179
+ font-weight: var(--a-weight-semibold);
180
180
  fill: var(--chart-value);
181
181
  font-family: var(--chart-font);
182
182
  }
@@ -265,7 +265,7 @@
265
265
  [data-avg-label] {
266
266
  fill: var(--chart-avg);
267
267
  font-family: var(--chart-font);
268
- font-weight: 600;
268
+ font-weight: var(--a-weight-semibold);
269
269
  }
270
270
 
271
271
  /* ── Radar ── */
@@ -388,7 +388,7 @@
388
388
  /* ── Donut center text ── */
389
389
 
390
390
  [data-donut-total] {
391
- font-weight: 700;
391
+ font-weight: var(--a-weight-bold);
392
392
  fill: var(--chart-fg);
393
393
  font-family: var(--chart-font);
394
394
  /* font-size set by JS on the element — scales with donut radius */
@@ -719,7 +719,7 @@
719
719
  color: var(--chart-tooltip-fg);
720
720
  font-family: var(--a-font-family);
721
721
  font-size: var(--a-ui-sm);
722
- line-height: 1.35;
722
+ line-height: var(--a-font-leading-normal);
723
723
  border: none;
724
724
  border-radius: var(--chart-tooltip-radius);
725
725
  box-shadow: var(--chart-tooltip-shadow);
@@ -741,11 +741,11 @@
741
741
  font-size: var(--a-ui-tiny);
742
742
  opacity: 0.75;
743
743
  text-transform: uppercase;
744
- letter-spacing: 0.04em;
744
+ letter-spacing: var(--a-font-tracking-wide);
745
745
  }
746
746
 
747
747
  .chart-tooltip-popup [data-tip-role="label"] {
748
- font-weight: 500;
748
+ font-weight: var(--a-weight-medium);
749
749
  }
750
750
 
751
751
  .chart-tooltip-popup [data-tip-role="value"] {
@@ -22,6 +22,8 @@ export class UIChart extends UIElement {
22
22
  type: 'bar' | 'line' | 'pie' | 'donut' | 'radar' | 'sparkline' | 'segments' | 'area' | 'scatter' | 'radial-bar' | 'gauge' | 'stacked-bar' | 'grouped-bar' | 'multi-line' | 'funnel' | 'treemap' | 'sankey' | 'composed';
23
23
  /** Color scheme */
24
24
  color: 'accent' | 'success' | 'warning' | 'danger' | 'info';
25
+ /** Bar corner radius in px (null = let CSS tokens decide) */
26
+ cornerRadius: number;
25
27
  /** JS property (set programmatically — `el.data = [...]`). An array of plain objects; each object's keys are named by the `x` and `y` attributes — e.g. `<chart-ui x="month" y="revenue">` consumes `[{month:'Jan', revenue:3200}, {month:'Feb', revenue:4100}]`. The Chart.js `{labels, datasets}` envelope is NOT chart-ui's API — passing it (or any non-array value) renders an empty chart. May also be supplied declaratively as a JSON-array `data="[…]"` attribute, hydrated once at connect. Custom accessor on the element class, not a reflected attribute. */
26
28
  data: string;
27
29
  /** Marker (dot) visibility for line and multi-line series. `all` draws a circle[data-dot] on every datum (default — matches pre-gh#561 behavior). `none` hides all dots so a dense series reads as a clean line. `hover` shows only the currently-hovered datum's dot. `last` shows only the terminal point per series (sparkline convention). Tooltip hit targets ([data-hit]) are a separate always-rendered layer — every mode keeps hover/click working along the whole line. */
@@ -36,8 +38,6 @@ export class UIChart extends UIElement {
36
38
  hideValues: boolean;
37
39
  /** Show a skeleton placeholder instead of the chart body. Set to true while data is being fetched; clear once data arrives. Preserves the element's aspect-ratio dimensions so the skeleton occupies the same space the chart will fill. Parity with stat-ui[loading] and table-ui[loading] (§FB-12, v0.6.27). */
38
40
  loading: boolean;
39
- /** Bar corner radius (null = let CSS tokens decide) */
40
- radius: number;
41
41
  /** Multi-line only. Names one series key (matching a `y` key) to render at full strength — area fill + full-opacity line. Every other series drops its area fill and renders line-only at --chart-deemphasized-opacity. Empty (default) applies full treatment to every series, unchanged from pre-gh#561 behavior. */
42
42
  seriesEmphasis: string;
43
43
  /** Chart size */
@@ -76,10 +76,11 @@ props:
76
76
  type: boolean
77
77
  default: false
78
78
  attribute: hide-values
79
- radius:
80
- description: Bar corner radius (null = let CSS tokens decide)
79
+ cornerRadius:
80
+ description: Bar corner radius in px (null = let CSS tokens decide)
81
81
  type: number
82
82
  default: null
83
+ attribute: corner-radius
83
84
  size:
84
85
  description: Chart size
85
86
  type: string
@@ -50,7 +50,7 @@
50
50
  --chat-msg-avatar-size-font: var(--a-ui-sm);
51
51
  --chat-msg-avatar-weight: var(--a-weight-semibold);
52
52
  --chat-bubble-font-size: var(--a-body-size);
53
- --chat-bubble-line-height: var(--a-leading-normal);
53
+ --chat-bubble-line-height: var(--a-font-leading-relaxed);
54
54
  }
55
55
 
56
56
  :scope {
@@ -107,9 +107,9 @@
107
107
  }
108
108
 
109
109
  & > header [slot="label"] {
110
- font-weight: 500;
110
+ font-weight: var(--a-weight-medium);
111
111
  text-transform: uppercase;
112
- letter-spacing: 0.05em;
112
+ letter-spacing: var(--a-font-tracking-wide);
113
113
  }
114
114
 
115
115
  /* Copy button — ghost style */
@@ -141,7 +141,7 @@
141
141
  overflow-x: auto;
142
142
  font-family: var(--code-font);
143
143
  font-size: var(--code-font-size);
144
- line-height: 1.5;
144
+ line-height: var(--a-font-leading-relaxed);
145
145
  scrollbar-width: none;
146
146
  }
147
147
  & > pre::-webkit-scrollbar {
@@ -312,7 +312,7 @@
312
312
  color: var(--code-fg);
313
313
  font-family: var(--code-font);
314
314
  font-size: var(--code-font-size);
315
- line-height: 1.5;
315
+ line-height: var(--a-font-leading-relaxed);
316
316
  }
317
317
 
318
318
  :scope .cm-scroller {
@@ -377,7 +377,7 @@
377
377
 
378
378
  /* Syntax highlight — one rule per token role (see core/_cm-theme.js) */
379
379
  :scope .tok-comment { color: var(--code-tok-comment); font-style: italic; }
380
- :scope .tok-keyword { color: var(--code-tok-keyword); font-weight: 500; }
380
+ :scope .tok-keyword { color: var(--code-tok-keyword); font-weight: var(--a-weight-medium); }
381
381
  :scope .tok-string { color: var(--code-tok-string); }
382
382
  :scope .tok-number { color: var(--code-tok-number); }
383
383
  :scope .tok-boolean { color: var(--code-tok-boolean); }
@@ -385,7 +385,7 @@
385
385
  :scope .tok-punctuation { color: var(--code-tok-punctuation); }
386
386
  :scope .tok-function { color: var(--code-tok-function); }
387
387
  :scope .tok-variable { color: var(--code-tok-variable); }
388
- :scope .tok-type { color: var(--code-tok-type); font-weight: 500; }
388
+ :scope .tok-type { color: var(--code-tok-type); font-weight: var(--a-weight-medium); }
389
389
  :scope .tok-property { color: var(--code-tok-property); }
390
390
  :scope .tok-tag { color: var(--code-tok-tag); }
391
391
  :scope .tok-attribute { color: var(--code-tok-attribute); }