@adia-ai/web-components 0.8.10 → 0.8.11

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 (80) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/MIGRATION.md +58 -0
  3. package/USAGE.md +5 -7
  4. package/components/adia-mark/adia-mark.a2ui.json +6 -6
  5. package/components/adia-mark/adia-mark.class.js +1 -1
  6. package/components/adia-mark/adia-mark.css +3 -4
  7. package/components/adia-mark/adia-mark.d.ts +1 -1
  8. package/components/adia-mark/adia-mark.examples.md +0 -2
  9. package/components/adia-mark/adia-mark.test.js +3 -3
  10. package/components/adia-mark/adia-mark.yaml +5 -4
  11. package/components/adia-wordmark/adia-wordmark.a2ui.json +108 -0
  12. package/components/adia-wordmark/adia-wordmark.class.js +60 -0
  13. package/components/adia-wordmark/adia-wordmark.css +45 -0
  14. package/components/adia-wordmark/adia-wordmark.d.ts +20 -0
  15. package/components/adia-wordmark/adia-wordmark.examples.md +23 -0
  16. package/components/adia-wordmark/adia-wordmark.js +17 -0
  17. package/components/adia-wordmark/adia-wordmark.test.js +69 -0
  18. package/components/adia-wordmark/adia-wordmark.yaml +102 -0
  19. package/components/avatar/avatar-group.a2ui.json +1 -1
  20. package/components/avatar/avatar-group.yaml +1 -1
  21. package/components/avatar/avatar.a2ui.json +2 -4
  22. package/components/avatar/avatar.css +16 -12
  23. package/components/avatar/avatar.d.ts +3 -3
  24. package/components/avatar/avatar.examples.md +1 -1
  25. package/components/avatar/avatar.yaml +1 -3
  26. package/components/badge/badge.a2ui.json +2 -4
  27. package/components/badge/badge.d.ts +2 -2
  28. package/components/badge/badge.yaml +1 -3
  29. package/components/button/button.a2ui.json +2 -4
  30. package/components/button/button.d.ts +2 -2
  31. package/components/button/button.yaml +1 -3
  32. package/components/calendar-grid/calendar-grid.class.js +2 -2
  33. package/components/calendar-grid/calendar-grid.css +1 -1
  34. package/components/calendar-picker/calendar-picker.class.js +2 -2
  35. package/components/calendar-picker/calendar-picker.css +1 -1
  36. package/components/chat-thread/chat-input.css +1 -1
  37. package/components/combobox/combobox.class.js +1 -1
  38. package/components/combobox/combobox.css +1 -1
  39. package/components/icon/icon.a2ui.json +1 -1
  40. package/components/icon/icon.class.js +5 -5
  41. package/components/icon/icon.css +7 -15
  42. package/components/icon/icon.d.ts +1 -1
  43. package/components/icon/icon.examples.md +1 -1
  44. package/components/icon/icon.yaml +10 -10
  45. package/components/index.js +1 -0
  46. package/components/input/input.class.js +2 -2
  47. package/components/modal/modal.a2ui.json +2 -3
  48. package/components/modal/modal.d.ts +2 -2
  49. package/components/modal/modal.yaml +1 -2
  50. package/components/select/select.class.js +4 -4
  51. package/components/select/select.css +12 -3
  52. package/components/swatch/swatch.css +1 -1
  53. package/components/switch/switch.a2ui.json +2 -4
  54. package/components/switch/switch.yaml +1 -3
  55. package/components/tag/tag.class.js +1 -1
  56. package/components/tag/tag.css +1 -1
  57. package/components/theme-provider/theme-provider.a2ui.json +11 -1
  58. package/components/theme-provider/theme-provider.class.js +27 -7
  59. package/components/theme-provider/theme-provider.d.ts +2 -2
  60. package/components/theme-provider/theme-provider.yaml +10 -1
  61. package/components/timeline/timeline.css +1 -1
  62. package/components/toggle-scheme/toggle-scheme.a2ui.json +2 -4
  63. package/components/toggle-scheme/toggle-scheme.d.ts +2 -2
  64. package/components/toggle-scheme/toggle-scheme.yaml +1 -3
  65. package/dist/scale.min.css +1 -0
  66. package/dist/scale.sheet.js +11 -0
  67. package/dist/theme-provider.min.js +8 -8
  68. package/dist/web-components.min.css +1 -1
  69. package/dist/web-components.min.js +93 -85
  70. package/dist/web-components.sheet.js +1 -1
  71. package/package.json +1 -1
  72. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +4 -4
  73. package/patterns/kanban-board/kanban-board.examples.html +8 -8
  74. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +2 -2
  75. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +1 -1
  76. package/patterns/search-discovery/search-discovery.examples.html +4 -4
  77. package/styles/components.css +1 -0
  78. package/styles/prose.css +4 -0
  79. package/styles/scale.css +683 -0
  80. package/styles/verse.css +4 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.8.11] — 2026-07-23
4
+
5
+ ### Added
6
+ - **`<adia-wordmark-ui>` primitive** (`components/adia-wordmark/`; gh#390, PR #393) — the Adia wordmark as a token-driven, scheme-adaptive brand primitive: inline SVG lockup reading `--a-fg-strong`, height-scaled ladder (`--adia-wordmark-height-*`) with fixed aspect ratio, `inline` mode for text-flow placement. Sits beside `<adia-mark-ui>` (the square mark) with a matching size vocabulary. Dist bundles regenerated with it.
7
+ - **Ancestor `[scale]` sizing context** (`styles/scale.css`, new opt-in layer; `dist/scale.{min.css,sheet.js}`) — six explicit hand-tabled tiers across two bands: `ui-sm` (≡ `[verse]`, byte-identical), `ui-md` (≡ base — a real reset tier), `ui-lg`, `content-sm`, `content-md` (≡ `[prose]`), `content-lg`. Each tier re-declares the full `--a-size/-inset/-gap/-radius/-icon/-caret/-toggle` sm/md/lg families plus all 13 type roles, so the universal `[size]` attribute keeps working inside a scaled subtree: **scale picks the table, size picks the row.** Tiers also re-table the identity-component ladders (`--avatar-size-*`, `--avatar-group-counter-size-*`/`-overlap-*`, `--adia-mark-size-*`, `--adia-wordmark-height-*`), so the removed `xs`/`xl` diameters live on as grid cells — `(ui-sm, sm)` and `(ui-lg, lg)`. `--a-density` is orthogonal and composes unchanged. Tier selectors are written `[scale][scale="…"]` (0-2-0) so they deterministically beat the legacy register attributes; the deduced tiers (`ui-lg`, `content-sm`, `content-lg`) are one-row shifts of their neighbors, tunable.
8
+ - **`icon-ui` free-form `%` values** — `size="100%"` fills the parent box (replaces the removed named `fill` tier); `#isFreeFormSize` accepts `px|rem|em|%`.
9
+
10
+ ### BREAKING
11
+ - **Every component `size` enum is now exactly `sm | md | lg`.** Trimmed: `button-ui`, `badge-ui`, `switch-ui`, `toggle-scheme-ui` (xs/xl — these were **enum-only promises with no CSS backing; they always rendered `md`**), `modal-ui` (xl, same), `avatar-ui`/`avatar-group-ui`/`adia-mark-ui`/`adia-wordmark-ui` (xs/xl — real tiers, now `[scale]` grid cells), and `icon-ui` (named xs/xl/2xl/3xl/4xl/fill → free-form `"12"/"32"/"48"/"64"/"96"/"100%"`; its named sm/md/lg now ride the universal `[size]` → `--a-icon-size` system, same 14/16/20px values). Migration table + mechanical sweep: MIGRATION GUIDE § v0.8.11.
12
+ - **`[verse]` / `[prose]` deprecated** — superseded by `scale="ui-sm"` / `scale="content-md"` (byte-identical tables); the legacy attributes still ship and work until v1.0. `theme-provider`'s `scale` prop takes the six tier names; `scale="verse|prose"` remain as console-warned aliases.
13
+
14
+ ### Changed
15
+ - **Internally stamped ghost/icon affordances tighten `md → sm`** (`select-ui` clear-all + leading marks, `input-ui` clear/reveal, `combobox-ui` clear, `tag-ui` dismiss, `calendar-picker-ui`/`calendar-grid-ui` prev/next) — they declared the fake `size="xs"` tier, so they actually rendered `md`; they now declare `size="sm"` and render one step tighter. `select-ui`'s stamped `adia-mark-ui` leadings keep their former 1.25rem diameter via a scoped parity rule (`select.css`).
16
+ - **`avatar-group-ui` counter/overlap are token families** (`--avatar-group-counter-size-{sm,md,lg}`, `--avatar-group-overlap-{sm,md,lg}`) so `[scale]` tiers can re-table them like the avatar ladder. Same default values.
17
+
18
+ ### Maintenance
19
+ - **`dist/` bundles rebuilt** (`web-components.{min.css,min.js,sheet.js}`, `host.*`, `themes.*`, new `scale.*`) — regenerated from the source changes above.
20
+ - **`patterns/` touched in this release window** (5 file(s), e.g. `comments-and-collaboration/comments-and-collaboration.examples.html`) — carried by the entries above.
21
+ - **`traits/` touched in this release window** (5 file(s), e.g. `count-up/count-up.examples.html`) — carried by the entries above.
22
+
3
23
  ## [0.8.10] — 2026-07-20
4
24
 
5
25
  ### Added
package/MIGRATION.md CHANGED
@@ -46,10 +46,68 @@ Version cuts and migration scope:
46
46
 
47
47
  - **`0.8.0`** — MINOR lockstep cut (2026-07-15). Internal ranges `^0.7.0` → `^0.8.0`. **BREAKING (3 items):** the Material color-token adoption — `--md-sys-color-*` (a static export) is now the color system's source of truth and public API. Removed: the 19 `--a-accent-*` back-compat aliases (→ `--a-primary-*`), the 189 raw per-family `--a-{family}-{0..6}-{tint-|shade-}scrim` primitives (→ the static `--md-sys-color-{family}-scrim-*` ladder), and the `primitives-accent.css` deep-import path (→ `primitives-primary.css`). **Behavioral:** the 8 named `[theme]` presets no longer re-color components (only brand hue + radius/density/shadow knobs still respond). See [§ v0.8.0 section below](#migrating-to-adia-aiv080-2026-07-15).
48
48
 
49
+ - **`0.8.11`** — PATCH lockstep cut (2026-07-23). Internal ranges stay at `^0.8.0`. **BREAKING: the size model.** Every component `size` enum is now exactly `sm | md | lg`; the removed tiers (`xs`, `xl`, icon's `2xl/3xl/4xl/fill`) are served by the new ancestor **`[scale]`** context — six hand-tabled tiers (`ui-sm · ui-md · ui-lg · content-sm · content-md · content-lg`): scale picks the table, `size` picks the row. `[verse]`/`[prose]` are deprecated aliases of `ui-sm`/`content-md` (removed at v1.0). Also new: `<adia-wordmark-ui>`. See [§ v0.8.11 section below](#migrating-to-adia-aiv0811-2026-07-23).
50
+
49
51
  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.
50
52
 
51
53
  ---
52
54
 
55
+ ## Migrating to `@adia-ai/*@v0.8.11` (2026-07-23)
56
+
57
+ **PATCH lockstep cut.** Internal ranges stay at `^0.8.0`. One breaking theme: **the size model contracts to `sm | md | lg` everywhere, and an ancestor `[scale]` attribute takes over everything the extra tiers did.** `styles/scale.css` (new, opt-in like verse/prose; `dist/scale.min.css` + `dist/scale.sheet.js`) defines six explicit tiers — `ui-sm` (≡ old `[verse]`), `ui-md` (≡ base), `ui-lg`, `content-sm`, `content-md` (≡ old `[prose]`), `content-lg` — each a full hand-tabled re-declaration of the `--a-size/-inset/-gap/-radius/-icon/-caret/-toggle` families and all 13 type roles. Scale picks the table, `[size]` picks the row; an unscaled page renders byte-identically to v0.8.10.
58
+
59
+ ### Component `size` enums trimmed to `sm | md | lg`
60
+
61
+ Two very different classes were removed:
62
+
63
+ - **Fake tiers** (`button-ui`, `badge-ui`, `switch-ui`, `toggle-scheme-ui` `xs`/`xl`; `modal-ui` `xl`) — these had **no CSS behind them and always rendered as `md`**. Dropping the attribute is exact visual parity; remapping to `sm`/`lg` applies the size the author probably intended (a visible change).
64
+ - **Real tiers** (`avatar-ui`, `avatar-group-ui`, `adia-mark-ui`, `adia-wordmark-ui` `xs`/`xl`; `icon-ui` `xs/xl/2xl/3xl/4xl/fill`) — the old pixel values live on exactly as `[scale]` grid cells or free-form icon values (table below).
65
+
66
+ | old call site | exact new equivalent | escape hatch |
67
+ |---|---|---|
68
+ | `avatar-ui size="xs"` (20px) | `size="sm"` under `scale="ui-sm"` | `style="--avatar-size:1.25rem"` |
69
+ | `avatar-ui size="xl"` (72px) | `size="lg"` under `scale="ui-lg"` (or `content-md`) | `style="--avatar-size:4.5rem"` |
70
+ | `adia-mark-ui` / `adia-wordmark-ui` `xs`/`xl` | same pattern | `--adia-mark-size` / `--adia-wordmark-height` |
71
+ | `icon-ui size="xs"` | `size="12"` | — |
72
+ | `icon-ui size="xl"` / `2xl` / `3xl` / `4xl` | `size="32"` / `"48"` / `"64"` / `"96"` | — |
73
+ | `icon-ui size="fill"` | `size="100%"` (new: `%` accepted as free-form) | `style="--icon-size:100%"` |
74
+ | `button/badge/switch/toggle-scheme size="xs|xl"` | drop the attr (was fake → rendered `md`), or `sm`/`lg` if the intent was real | — |
75
+ | `modal-ui size="xl"` | `size="lg"` (was fake) | `style="--modal-width:…"` |
76
+ | `[verse]` / `[prose]` | `scale="ui-sm"` / `scale="content-md"` (byte-identical tables) | deprecated attrs still ship until v1.0 |
77
+
78
+ `icon-ui` also went grid-native: its named `sm/md/lg` now ride the universal `[size]` → `--a-icon-size` system (same 14/16/20px values), so icons track an ancestor `[scale]` register automatically.
79
+
80
+ **Audit:**
81
+ ```bash
82
+ git grep -nE 'size="(xs|xl|2xl|3xl|4xl|fill)"'
83
+ git grep -nE '"size": *"(xs|xl|2xl|3xl|4xl|fill)"' # A2UI/JSON form
84
+ ```
85
+
86
+ **Sweep (mechanical, non-icon tags):**
87
+ ```bash
88
+ git grep -lE 'size="(xs|xl)"' | xargs perl -i -pe 's/size="xs"/size="sm"/g; s/size="xl"/size="lg"/g'
89
+ ```
90
+ Icon named tiers are a table-driven remap (`xs→"12"`, `xl→"32"`, `2xl→"48"`, `3xl→"64"`, `4xl→"96"`, `fill→"100%"`) — sweep them BEFORE the generic pass, and never touch already-free-form values (`size="48"`).
91
+
92
+ **Manual review:** call sites that used `xs`/`xl` to express "this whole region is denser/larger" should adopt an ancestor `scale="…"` instead of the per-element remap — that is the new model's point.
93
+
94
+ ### `theme-provider` `scale` prop revalued
95
+
96
+ `<theme-provider scale="…">` now takes the six tier names (the reflected attribute IS the `[scale]` CSS hook and auto-adopts `dist/scale.sheet.js`). `scale="verse"`/`"prose"` still work as deprecated aliases (console warning; removed at v1.0).
97
+
98
+ ### Internal stamped affordances tightened `md → sm`
99
+
100
+ The ghost/icon affordances several components stamp internally (`select-ui` clear-all + leading marks, `input-ui` clear/reveal, `combobox-ui` clear, `tag-ui` dismiss, calendar prev/next) declared `size="xs"` — which the fake-tier collapse rendered as `md`. They now declare `size="sm"` and genuinely render one step tighter. No consumer action; noted because it is a visible (intentional) change on dense forms.
101
+
102
+ ### Verify the sweep landed
103
+
104
+ ```bash
105
+ git grep -cE 'size="(xs|xl|2xl|3xl|4xl|fill)"' || echo clean # expect clean (icon free-form px values remain)
106
+ ```
107
+ Then render one dense screen and one long-form screen and confirm register changes come only from `[scale]`/`[size]`.
108
+
109
+ ---
110
+
53
111
  ## Migrating to `@adia-ai/*@v0.8.0` (2026-07-15)
54
112
 
55
113
  **MINOR cut.** All 9 packages bumped `0.7.x → 0.8.0`; internal `@adia-ai/*` ranges **bump to `^0.8.0`**. The headline: **AdiaUI adopted the Material Design token system as its color source of truth** (`.claude/docs/specs/material-color-bridge-contract.md`, Phases 1–5). `--md-sys-color-*` roles (59 per palette, 8 palettes, static OKLCH literals in `colors/material-static.css`) are the public color API; the `--a-*` semantic layer remains fully functional as a thin alias layer reading FROM it. 3 breaking items + 1 behavioral change.
package/USAGE.md CHANGED
@@ -1103,17 +1103,15 @@ This section collects 11 docs items (§221a–§221k) that emerged from rounds R
1103
1103
 
1104
1104
  ### §221a — Universal `[size]` prop family
1105
1105
 
1106
- Every interactive primitive accepts `[size]` with `xs | sm | md | lg | xl` (some additionally accept `2xs` / `2xl`). The token wiring is in `themes.css`:
1106
+ Every interactive primitive accepts `[size]` with `sm | md | lg` (since v0.8.11 this is the whole vocabulary — the former `xs`/`xl` tiers are served by an ancestor `[scale]` register instead). The token wiring is the universal `[size]` system in `styles/api/sizing.css`:
1107
1107
 
1108
1108
  ```css
1109
- [size="xs"] { --a-size: 24px; } /* compact toolbar / inline densities */
1110
- [size="sm"] { --a-size: 28px; }
1111
- [size="md"] { --a-size: 32px; } /* canonical default */
1112
- [size="lg"] { --a-size: 40px; }
1113
- [size="xl"] { --a-size: 48px; } /* hero CTAs, top-level brand */
1109
+ [size="sm"] { --a-size: calc(var(--a-density) * 1.5rem); } /* 24px dense data UIs */
1110
+ /* [size="md"] is the default — 30px */
1111
+ [size="lg"] { --a-size: calc(var(--a-density) * 2.25rem); } /* 36px hero CTAs */
1114
1112
  ```
1115
1113
 
1116
- Authoring: `<button-ui size="lg" text="Continue">`. The element inherits `--a-size` from the closest `[size]` ancestor when the prop is unset, so a `<row-ui size="sm">` containing 5 controls makes them all small without per-element repeat.
1114
+ Authoring: `<button-ui size="lg" text="Continue">`. The element inherits `--a-size` from the closest `[size]` ancestor when the prop is unset, so a `<row-ui size="sm">` containing 5 controls makes them all small without per-element repeat. For a whole region one register denser or larger, set `scale="ui-sm" … "content-lg"` on an ancestor (`styles/scale.css`): scale picks the table, size picks the row.
1117
1115
 
1118
1116
  When in doubt about whether a primitive supports `[size]`: every form-bearing primitive does (input / textarea / select / switch / check / radio / button / range / slider / search). Display primitives that participate in size cascades also do (avatar / badge / tag / icon / chip / card).
1119
1117
 
@@ -25,11 +25,9 @@
25
25
  "description": "Diameter of the square mark. Matches avatar-ui's size ladder for visual consistency when the two sit side by side (e.g. an app switcher row).",
26
26
  "type": "string",
27
27
  "enum": [
28
- "xs",
29
28
  "sm",
30
29
  "md",
31
- "lg",
32
- "xl"
30
+ "lg"
33
31
  ],
34
32
  "default": "md"
35
33
  }
@@ -65,13 +63,15 @@
65
63
  "adia",
66
64
  "brand",
67
65
  "logo",
68
- "wordmark",
69
66
  "mark",
70
67
  "identity",
71
- "favicon"
68
+ "favicon",
69
+ "tile",
70
+ "icon"
72
71
  ],
73
72
  "name": "UIAdiaMark",
74
73
  "related": [
74
+ "AdiaWordmark",
75
75
  "Icon",
76
76
  "Avatar",
77
77
  "Image"
@@ -95,7 +95,7 @@
95
95
  "adia",
96
96
  "brand",
97
97
  "mark",
98
- "wordmark"
98
+ "tile"
99
99
  ]
100
100
  },
101
101
  "tag": "adia-mark-ui",
@@ -14,7 +14,7 @@
14
14
  /**
15
15
  * <adia-mark-ui></adia-mark-ui>
16
16
  * <adia-mark-ui size="sm"></adia-mark-ui>
17
- * <adia-mark-ui size="xl"></adia-mark-ui>
17
+ * <adia-mark-ui size="lg"></adia-mark-ui>
18
18
  *
19
19
  * The Adia brand mark — a rounded square with the "Adia" wordmark cut
20
20
  * out in a contrasting color. Background and wordmark are ALWAYS the
@@ -8,11 +8,12 @@
8
8
  --adia-mark-bg: var(--a-canvas-surface-inverse);
9
9
  --adia-mark-fg: var(--a-fg-inverse);
10
10
 
11
- --adia-mark-size-xs: 1.25rem;
11
+ /* sm/md/lg only — removed xs/xl live on as the ancestor-[scale]
12
+ grid cells (ui-sm, sm) / (ui-lg, lg); styles/scale.css re-tables
13
+ this family per tier. */
12
14
  --adia-mark-size-sm: 1.75rem;
13
15
  --adia-mark-size-md: 2.5rem;
14
16
  --adia-mark-size-lg: 3.5rem;
15
- --adia-mark-size-xl: 4.5rem;
16
17
  }
17
18
 
18
19
  :scope {
@@ -40,9 +41,7 @@
40
41
  }
41
42
 
42
43
  /* ── Size ladder ── */
43
- :scope[size="xs"] { --adia-mark-size: var(--adia-mark-size-xs); }
44
44
  :scope[size="sm"] { --adia-mark-size: var(--adia-mark-size-sm); }
45
45
  :scope[size="md"] { --adia-mark-size: var(--adia-mark-size-md); }
46
46
  :scope[size="lg"] { --adia-mark-size: var(--adia-mark-size-lg); }
47
- :scope[size="xl"] { --adia-mark-size: var(--adia-mark-size-xl); }
48
47
  }
@@ -16,5 +16,5 @@ export class UIAdiaMark extends UIElement {
16
16
  /** Renders as `display:inline-block` instead of the block-level default (ADR-0037) — set this when the mark sits beside a text run (a topbar brand slot) rather than standing alone. */
17
17
  inline: boolean;
18
18
  /** Diameter of the square mark. Matches avatar-ui's size ladder for visual consistency when the two sit side by side (e.g. an app switcher row). */
19
- size: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
19
+ size: 'sm' | 'md' | 'lg';
20
20
  }
@@ -9,11 +9,9 @@
9
9
  ## Sizes
10
10
 
11
11
  ```html
12
- <adia-mark-ui size="xs"></adia-mark-ui>
13
12
  <adia-mark-ui size="sm"></adia-mark-ui>
14
13
  <adia-mark-ui size="md"></adia-mark-ui>
15
14
  <adia-mark-ui size="lg"></adia-mark-ui>
16
- <adia-mark-ui size="xl"></adia-mark-ui>
17
15
  ```
18
16
 
19
17
  ## Topbar brand slot
@@ -25,9 +25,9 @@ describe('adia-mark-ui', () => {
25
25
  });
26
26
 
27
27
  it('reflects an explicit [size]', async () => {
28
- const el = mount('<adia-mark-ui size="xl"></adia-mark-ui>');
28
+ const el = mount('<adia-mark-ui size="lg"></adia-mark-ui>');
29
29
  await tick();
30
- expect(el.size).toBe('xl');
30
+ expect(el.size).toBe('lg');
31
31
  });
32
32
 
33
33
  it('defaults to inline=false and reflects an explicit [inline]', async () => {
@@ -62,7 +62,7 @@ describe('adia-mark-ui', () => {
62
62
 
63
63
  it('renders identically regardless of size — static template, no interpolation', async () => {
64
64
  const a = mount('<adia-mark-ui size="sm"></adia-mark-ui>');
65
- const b = mount('<adia-mark-ui size="xl"></adia-mark-ui>');
65
+ const b = mount('<adia-mark-ui size="lg"></adia-mark-ui>');
66
66
  await tick();
67
67
  expect(a.innerHTML).toBe(b.innerHTML);
68
68
  });
@@ -19,11 +19,9 @@ props:
19
19
  type: string
20
20
  default: md
21
21
  enum:
22
- - xs
23
22
  - sm
24
23
  - md
25
24
  - lg
26
- - xl
27
25
  reflect: true
28
26
  inline:
29
27
  description: Renders as `display:inline-block` instead of the block-level default (ADR-0037) — set this when the mark sits beside a text run (a topbar brand slot) rather than standing alone.
@@ -54,6 +52,7 @@ a2ui:
54
52
  - Use <AdiaMark> for the Adia brand/app-identity mark — a favicon-style app-switcher tile, a splash/loading screen, an about-panel header. Do not use it as a generic icon; it has no [name] prop and always renders the same wordmark.
55
53
  - Do not wrap <AdiaMark> in <Icon> or pass it a color/tone override — the background/foreground pairing is fixed by design (always inverse-on-inverse for contrast), not a themeable variant.
56
54
  - Prefer <AdiaMark size="sm"> in a nav/topbar brand slot (a Row alongside the product name); reserve larger sizes (lg, xl) for splash screens or empty-state headers. Set [inline] only when the mark sits directly inside a run of prose text, not inside a Row/flex layout (block-level children compose fine as flex items already).
55
+ - Use <AdiaMark> for the square icon/tile use (app-switcher slot, favicon-style avatar) — always the fixed inverse-on-inverse pairing. Use <AdiaWordmark> instead for the standalone "Adia" text logotype meant to sit directly on a surface with no background chip.
57
56
  anti_patterns:
58
57
  - wrong: |
59
58
  <icon-ui name="adia"></icon-ui>
@@ -100,22 +99,24 @@ keywords:
100
99
  - adia
101
100
  - brand
102
101
  - logo
103
- - wordmark
104
102
  - mark
105
103
  - identity
106
104
  - favicon
105
+ - tile
106
+ - icon
107
107
  synonyms:
108
108
  logo:
109
109
  - adia
110
110
  - brand
111
111
  - mark
112
- - wordmark
112
+ - tile
113
113
  brand:
114
114
  - adia
115
115
  - logo
116
116
  - mark
117
117
  - identity
118
118
  related:
119
+ - AdiaWordmark
119
120
  - Icon
120
121
  - Avatar
121
122
  - Image
@@ -0,0 +1,108 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/AdiaWordmark.json",
4
+ "title": "AdiaWordmark",
5
+ "description": "The Adia wordmark — the \"Adia\" text logotype rendered as inline SVG. Unlike `adia-mark-ui` (gh#283's fixed inverse-on-inverse square tile), this is a standalone mark meant to sit directly on a surface: its fill defaults to the current scheme's on-surface foreground token (`--a-fg-strong`) rather than a fixed inverse pairing, so it reads correctly wherever it's placed — a login header, a footer, a splash screen — without needing a background chip (gh#390).",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "component": {
17
+ "const": "AdiaWordmark"
18
+ },
19
+ "inline": {
20
+ "description": "Renders as `display:inline-block` instead of the block-level default (ADR-0037) — set this when the wordmark sits beside a text run rather than standing alone.",
21
+ "type": "boolean",
22
+ "default": false
23
+ },
24
+ "size": {
25
+ "description": "Height of the wordmark; width follows via the fixed aspect ratio. Matches adia-mark-ui's size ladder for visual consistency when the two sit side by side.",
26
+ "type": "string",
27
+ "enum": [
28
+ "sm",
29
+ "md",
30
+ "lg"
31
+ ],
32
+ "default": "md"
33
+ }
34
+ },
35
+ "required": [
36
+ "component"
37
+ ],
38
+ "unevaluatedProperties": false,
39
+ "x-adiaui": {
40
+ "anti_patterns": [
41
+ {
42
+ "fix": "<adia-wordmark-ui size=\"sm\"></adia-wordmark-ui>\n",
43
+ "why": "icon-ui only resolves Phosphor glyph names from the registry; there\nis no \"adia\" Phosphor icon, and registering one would misuse a\nthird-party glyph set for a proprietary wordmark.\n",
44
+ "wrong": "<icon-ui name=\"adia\"></icon-ui>\n"
45
+ },
46
+ {
47
+ "fix": "<adia-wordmark-ui size=\"sm\"></adia-wordmark-ui>\n",
48
+ "why": "adia-mark-ui is the fixed inverse-on-inverse SQUARE TILE (gh#283) —\nalways a background chip with the wordmark cut out, sized for an\napp-switcher/favicon slot. It is not interchangeable with the\nstandalone text logotype meant to sit directly on a surface.\n",
49
+ "wrong": "<adia-mark-ui size=\"sm\"></adia-mark-ui>\n"
50
+ }
51
+ ],
52
+ "category": "display",
53
+ "composes": [],
54
+ "events": {},
55
+ "examples": [
56
+ {
57
+ "description": "Standalone wordmark in a login/marketing header, no background chip needed.",
58
+ "a2ui": "[\n {\n \"id\": \"brand-row\",\n \"component\": \"Row\",\n \"gap\": \"2\",\n \"align\": \"center\",\n \"children\": [\"wordmark\"]\n },\n {\n \"id\": \"wordmark\",\n \"component\": \"AdiaWordmark\",\n \"size\": \"sm\"\n }\n]",
59
+ "name": "login-header-wordmark"
60
+ }
61
+ ],
62
+ "keywords": [
63
+ "adia",
64
+ "wordmark",
65
+ "logotype",
66
+ "text logo",
67
+ "identity"
68
+ ],
69
+ "name": "UIAdiaWordmark",
70
+ "related": [
71
+ "AdiaMark",
72
+ "Icon",
73
+ "Image"
74
+ ],
75
+ "slots": {},
76
+ "states": [
77
+ {
78
+ "description": "The only state — a static, non-interactive wordmark.",
79
+ "name": "default"
80
+ }
81
+ ],
82
+ "status": "stable",
83
+ "synonyms": {
84
+ "logotype": [
85
+ "adia",
86
+ "wordmark"
87
+ ],
88
+ "wordmark": [
89
+ "adia",
90
+ "logotype",
91
+ "text logo"
92
+ ]
93
+ },
94
+ "tag": "adia-wordmark-ui",
95
+ "tokens": {
96
+ "--adia-wordmark-fg": {
97
+ "description": "Wordmark fill — defaults to the on-surface foreground token. Themeable (unlike adia-mark-ui's fixed inverse pairing), since this mark already sits directly on a surface rather than owning its own background chip.",
98
+ "default": "var(--a-fg-strong)"
99
+ },
100
+ "--adia-wordmark-height": {
101
+ "description": "Height of the wordmark; width follows via aspect-ratio.",
102
+ "default": "1.5rem"
103
+ }
104
+ },
105
+ "traits": [],
106
+ "version": 1
107
+ }
108
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Non-side-effect class export for `<adia-wordmark-ui>`.
3
+ *
4
+ * Importing this file gives you the class without auto-registering the
5
+ * tag. Useful for test isolation, subclassing with tag-name override, or
6
+ * selective composition.
7
+ *
8
+ * The auto-register path stays at `@adia-ai/web-components/components/adia-wordmark`
9
+ * (which imports this file + calls `defineIfFree()`).
10
+ *
11
+ * @see ../../USAGE.md#registration--auto-vs-explicit
12
+ */
13
+
14
+ /**
15
+ * <adia-wordmark-ui></adia-wordmark-ui>
16
+ * <adia-wordmark-ui size="sm"></adia-wordmark-ui>
17
+ * <adia-wordmark-ui size="lg"></adia-wordmark-ui>
18
+ *
19
+ * The Adia wordmark — the "Adia" text logotype rendered as inline SVG.
20
+ * Unlike `adia-mark-ui` (gh#283's fixed inverse-on-inverse square tile),
21
+ * this is a standalone mark meant to sit directly on a surface, so its
22
+ * fill defaults to the current scheme's on-surface foreground token
23
+ * (`--a-fg-strong`) rather than a fixed inverse pairing (gh#390).
24
+ *
25
+ * `[data-wordmark-glyph]` marks the five logotype paths so the CSS token
26
+ * (`--adia-wordmark-fg`) can target them without repeating the selector
27
+ * five times — same pattern as `adia-mark-ui`'s `[data-mark-glyph]`.
28
+ *
29
+ * Static content, zero props affect the template output — `static
30
+ * template` returns the same markup every render. Safe under SSR:
31
+ * `stamp()`'s first-mount `replaceChildren` only ever regenerates
32
+ * identical, template-owned content (see gh#284's survey — this
33
+ * component follows the same "template content never depends on
34
+ * light-DOM children" shape as check-ui/switch-ui, just with zero
35
+ * interpolated values instead of property-derived ones).
36
+ */
37
+
38
+ import { UIElement, html } from '../../core/element.js';
39
+
40
+ export class UIAdiaWordmark extends UIElement {
41
+ static properties = {
42
+ size: { type: String, default: 'md', reflect: true },
43
+ inline: { type: Boolean, default: false, reflect: true },
44
+ };
45
+
46
+ static template = () => html`
47
+ <svg viewBox="0 0 512 182" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
48
+ <path data-wordmark-glyph d="M328.763 33.7659C332.735 37.5729 337.369 39.4764 342.665 39.4764C348.127 39.4764 352.761 37.5729 356.568 33.7659C360.54 29.7935 362.526 25.1589 362.526 19.8623C362.526 14.5657 360.54 9.93116 356.568 5.95869C352.761 1.98623 348.127 0 342.665 0C337.369 0 332.735 1.98623 328.763 5.95869C324.956 9.93116 323.053 14.5657 323.053 19.8623C323.053 25.1589 324.956 29.7935 328.763 33.7659Z"></path>
49
+ <path data-wordmark-glyph d="M326.777 54.3731V178.513H358.802V54.3731H326.777Z"></path>
50
+ <path data-wordmark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M512 54.3589H479.975V69.0074C470.375 56.9245 456.887 50.883 439.509 50.883C422.958 50.883 408.725 57.2555 396.808 70.0005C385.058 82.58 379.182 98.056 379.182 116.429C379.182 134.801 385.058 150.36 396.808 163.105C408.725 175.685 422.958 181.974 439.509 181.974C456.887 181.974 470.375 175.933 479.975 163.85V178.498H512V54.3589ZM445.467 151.436C435.537 151.436 427.344 148.208 420.889 141.753C414.435 135.132 411.207 126.691 411.207 116.429C411.207 106.166 414.435 97.8077 420.889 91.3525C427.344 84.7317 435.537 81.4213 445.467 81.4213C455.397 81.4213 463.59 84.7317 470.044 91.3525C476.665 97.8077 479.975 106.166 479.975 116.429C479.975 126.691 476.665 135.132 470.044 141.753C463.59 148.208 455.397 151.436 445.467 151.436Z"></path>
51
+ <path data-wordmark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M267.953 4.72882H299.978V178.524H267.953V163.876C258.519 175.959 245.113 182 227.735 182C211.019 182 196.703 175.71 184.786 163.131C173.035 150.386 167.16 134.827 167.16 116.454C167.16 98.0817 173.035 82.6057 184.786 70.0262C196.703 57.2812 211.019 50.9087 227.735 50.9087C245.113 50.9087 258.519 56.9502 267.953 69.0331V4.72882ZM208.867 141.779C215.488 148.234 223.763 151.462 233.693 151.462C243.623 151.462 251.816 148.234 258.271 141.779C264.725 135.158 267.953 126.717 267.953 116.454C267.953 106.192 264.725 97.8334 258.271 91.3782C251.816 84.7574 243.623 81.447 233.693 81.447C223.763 81.447 215.488 84.7574 208.867 91.3782C202.413 97.8334 199.185 106.192 199.185 116.454C199.185 126.717 202.413 135.158 208.867 141.779Z"></path>
52
+ <path data-wordmark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M116.681 147.241L127.108 178.524H164.347L103.275 4.72882H60.8231L0 178.524H36.9904L47.4172 147.241H116.681ZM82.1733 44.2052L106.006 115.213H58.3406L82.1733 44.2052Z"></path>
53
+ </svg>
54
+ `;
55
+
56
+ connected() {
57
+ this.setAttribute('role', 'img');
58
+ if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', 'Adia');
59
+ }
60
+ }
@@ -0,0 +1,45 @@
1
+ @scope (adia-wordmark-ui) {
2
+ :where(:scope) {
3
+ /* ── Tokens ── */
4
+ --adia-wordmark-height: var(--a-size, 1.5rem);
5
+ /* Meant to sit directly on a surface (gh#390) — defaults to the
6
+ on-surface foreground token, unlike adia-mark-ui's fixed
7
+ inverse-on-inverse pairing. Themeable: override this if the
8
+ wordmark sits on a non-default surface. */
9
+ --adia-wordmark-fg: var(--a-fg-strong);
10
+
11
+ /* sm/md/lg only — removed xs/xl live on as the ancestor-[scale]
12
+ grid cells (ui-sm, sm) / (ui-lg, lg); styles/scale.css re-tables
13
+ this family per tier. */
14
+ --adia-wordmark-height-sm: 1.125rem;
15
+ --adia-wordmark-height-md: 1.5rem;
16
+ --adia-wordmark-height-lg: 2rem;
17
+ }
18
+
19
+ :scope {
20
+ /* ADR-0037 — block-level by default; [inline] opts into inline-block
21
+ for the common "beside a text run" case (a topbar brand slot). */
22
+ display: block;
23
+ height: var(--adia-wordmark-height);
24
+ width: auto;
25
+ aspect-ratio: 512 / 182;
26
+ flex-shrink: 0;
27
+ }
28
+
29
+ :scope[inline] { display: inline-block; }
30
+
31
+ :scope > svg {
32
+ display: block;
33
+ width: 100%;
34
+ height: 100%;
35
+ }
36
+
37
+ :scope > svg [data-wordmark-glyph] {
38
+ fill: var(--adia-wordmark-fg);
39
+ }
40
+
41
+ /* ── Size ladder ── */
42
+ :scope[size="sm"] { --adia-wordmark-height: var(--adia-wordmark-height-sm); }
43
+ :scope[size="md"] { --adia-wordmark-height: var(--adia-wordmark-height-md); }
44
+ :scope[size="lg"] { --adia-wordmark-height: var(--adia-wordmark-height-lg); }
45
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `<adia-wordmark-ui>` — The Adia wordmark — the "Adia" text logotype rendered as inline SVG. Unlike `adia-mark-ui` (gh#283's fixed inverse-on-inverse square tile), this is a standalone mark meant to sit directly on a surface: its fill defaults to the current scheme's on-surface foreground token (`--a-fg-strong`) rather than a fixed inverse pairing, so it reads correctly wherever it's placed — a login header, a footer, a splash screen — without needing a background chip (gh#390).
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/adia-wordmark
5
+ *
6
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
11
+ */
12
+
13
+ import { UIElement } from '../../core/element.js';
14
+
15
+ export class UIAdiaWordmark extends UIElement {
16
+ /** Renders as `display:inline-block` instead of the block-level default (ADR-0037) — set this when the wordmark sits beside a text run rather than standing alone. */
17
+ inline: boolean;
18
+ /** Height of the wordmark; width follows via the fixed aspect ratio. Matches adia-mark-ui's size ladder for visual consistency when the two sit side by side. */
19
+ size: 'sm' | 'md' | 'lg';
20
+ }
@@ -0,0 +1,23 @@
1
+ # adia-wordmark — Examples
2
+
3
+ ## Default
4
+
5
+ ```html
6
+ <adia-wordmark-ui></adia-wordmark-ui>
7
+ ```
8
+
9
+ ## Sizes
10
+
11
+ ```html
12
+ <adia-wordmark-ui size="sm"></adia-wordmark-ui>
13
+ <adia-wordmark-ui size="md"></adia-wordmark-ui>
14
+ <adia-wordmark-ui size="lg"></adia-wordmark-ui>
15
+ ```
16
+
17
+ ## Login header
18
+
19
+ ```html
20
+ <row-ui gap="2" align="center">
21
+ <adia-wordmark-ui size="lg"></adia-wordmark-ui>
22
+ </row-ui>
23
+ ```
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `<adia-wordmark-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIAdiaWordmark } from '@adia-ai/web-components/components/adia-wordmark/class';
8
+ *
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
10
+ */
11
+
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIAdiaWordmark } from './adia-wordmark.class.js';
14
+
15
+ defineIfFree('adia-wordmark-ui', UIAdiaWordmark);
16
+
17
+ export { UIAdiaWordmark };
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import '../../core/element.js';
3
+ import './adia-wordmark.js';
4
+
5
+ const tick = () => new Promise((r) => queueMicrotask(r));
6
+
7
+ function mount(html) {
8
+ const wrap = document.createElement('div');
9
+ wrap.innerHTML = html;
10
+ document.body.appendChild(wrap);
11
+ return wrap.firstElementChild;
12
+ }
13
+
14
+ describe('adia-wordmark-ui', () => {
15
+ beforeEach(() => { document.body.innerHTML = ''; });
16
+
17
+ it('registers adia-wordmark-ui as a custom element', () => {
18
+ expect(customElements.get('adia-wordmark-ui')).toBeDefined();
19
+ });
20
+
21
+ it('defaults to size="md"', async () => {
22
+ const el = mount('<adia-wordmark-ui></adia-wordmark-ui>');
23
+ await tick();
24
+ expect(el.size).toBe('md');
25
+ });
26
+
27
+ it('reflects an explicit [size]', async () => {
28
+ const el = mount('<adia-wordmark-ui size="lg"></adia-wordmark-ui>');
29
+ await tick();
30
+ expect(el.size).toBe('lg');
31
+ });
32
+
33
+ it('defaults to inline=false and reflects an explicit [inline]', async () => {
34
+ const a = mount('<adia-wordmark-ui></adia-wordmark-ui>');
35
+ const b = mount('<adia-wordmark-ui inline></adia-wordmark-ui>');
36
+ await tick();
37
+ expect(a.inline).toBe(false);
38
+ expect(b.inline).toBe(true);
39
+ expect(b.getAttribute('inline')).toBe('');
40
+ });
41
+
42
+ it('sets role="img" and a default aria-label on connect', async () => {
43
+ const el = mount('<adia-wordmark-ui></adia-wordmark-ui>');
44
+ await tick();
45
+ expect(el.getAttribute('role')).toBe('img');
46
+ expect(el.getAttribute('aria-label')).toBe('Adia');
47
+ });
48
+
49
+ it('does not overwrite a consumer-authored aria-label', async () => {
50
+ const el = mount('<adia-wordmark-ui aria-label="Adia — home"></adia-wordmark-ui>');
51
+ await tick();
52
+ expect(el.getAttribute('aria-label')).toBe('Adia — home');
53
+ });
54
+
55
+ it('stamps the wordmark SVG with five glyph paths, no consumer-content conflict', async () => {
56
+ const el = mount('<adia-wordmark-ui></adia-wordmark-ui>');
57
+ await tick();
58
+ const svg = el.querySelector('svg');
59
+ expect(svg).toBeTruthy();
60
+ expect(el.querySelectorAll('[data-wordmark-glyph]').length).toBe(5);
61
+ });
62
+
63
+ it('renders identically regardless of size — static template, no interpolation', async () => {
64
+ const a = mount('<adia-wordmark-ui size="sm"></adia-wordmark-ui>');
65
+ const b = mount('<adia-wordmark-ui size="lg"></adia-wordmark-ui>');
66
+ await tick();
67
+ expect(a.innerHTML).toBe(b.innerHTML);
68
+ });
69
+ });