@adia-ai/web-components 0.8.3 → 0.8.5
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.
- package/CHANGELOG.md +33 -0
- package/USAGE.md +60 -25
- package/components/accordion/accordion.css +5 -0
- package/components/adia-mark/adia-mark.a2ui.json +123 -0
- package/components/adia-mark/adia-mark.class.js +64 -0
- package/components/adia-mark/adia-mark.css +48 -0
- package/components/adia-mark/adia-mark.d.ts +20 -0
- package/components/adia-mark/adia-mark.examples.md +26 -0
- package/components/adia-mark/adia-mark.js +17 -0
- package/components/adia-mark/adia-mark.test.js +69 -0
- package/components/adia-mark/adia-mark.yaml +121 -0
- package/components/agent-artifact/agent-artifact.css +1 -0
- package/components/agent-questions/agent-questions.a2ui.json +2 -1
- package/components/agent-questions/agent-questions.class.js +96 -53
- package/components/agent-questions/agent-questions.css +27 -106
- package/components/agent-questions/agent-questions.yaml +1 -0
- package/components/agent-reasoning/agent-reasoning.css +9 -5
- package/components/button/button.class.js +9 -2
- package/components/button/button.css +1 -1
- package/components/calendar-grid/calendar-grid.a2ui.json +3 -1
- package/components/calendar-grid/calendar-grid.class.js +11 -6
- package/components/calendar-grid/calendar-grid.css +42 -20
- package/components/calendar-grid/calendar-grid.yaml +5 -0
- package/components/calendar-picker/calendar-picker.a2ui.json +3 -1
- package/components/calendar-picker/calendar-picker.class.js +11 -6
- package/components/calendar-picker/calendar-picker.css +41 -19
- package/components/calendar-picker/calendar-picker.yaml +5 -0
- package/components/chart/chart.class.js +17 -14
- package/components/chat-thread/chat-input.a2ui.json +3 -1
- package/components/chat-thread/chat-input.css +12 -13
- package/components/chat-thread/chat-input.js +6 -2
- package/components/chat-thread/chat-input.yaml +3 -0
- package/components/check/check.css +1 -1
- package/components/color-input/color-input.class.js +8 -5
- package/components/color-picker/color-picker.class.js +6 -3
- package/components/combobox/combobox.a2ui.json +3 -1
- package/components/combobox/combobox.class.js +1 -3
- package/components/combobox/combobox.css +9 -12
- package/components/combobox/combobox.yaml +2 -0
- package/components/date-range-picker/date-range-picker.css +1 -0
- package/components/field/field.class.js +10 -7
- package/components/fields/fields.class.js +15 -12
- package/components/icon/icon.class.js +9 -5
- package/components/index.js +1 -0
- package/components/input/input.css +6 -2
- package/components/nav/nav.class.js +2 -1
- package/components/nav-group/nav-group.css +5 -6
- package/components/noodles/noodles.class.js +14 -10
- package/components/option-card/option-card.css +1 -0
- package/components/page/page.class.js +3 -2
- package/components/radio/radio.css +1 -1
- package/components/range/range.css +2 -0
- package/components/select/select.a2ui.json +2 -1
- package/components/select/select.class.js +12 -11
- package/components/select/select.css +29 -40
- package/components/select/select.test.js +6 -2
- package/components/select/select.yaml +1 -0
- package/components/swatch/swatch.a2ui.json +3 -1
- package/components/swatch/swatch.class.js +41 -32
- package/components/swatch/swatch.css +13 -18
- package/components/swatch/swatch.yaml +2 -0
- package/components/swiper/swiper.class.js +4 -1
- package/components/swiper/swiper.css +13 -4
- package/components/switch/switch.css +1 -1
- package/components/tag/tag.a2ui.json +4 -1
- package/components/tag/tag.class.js +3 -1
- package/components/tag/tag.css +13 -15
- package/components/tag/tag.yaml +3 -0
- package/components/tags-input/tags-input.css +1 -0
- package/components/textarea/textarea.css +2 -0
- package/components/time-picker/time-picker.css +2 -0
- package/components/timeline/timeline.a2ui.json +2 -1
- package/components/timeline/timeline.class.js +7 -8
- package/components/timeline/timeline.css +14 -26
- package/components/timeline/timeline.yaml +1 -0
- package/components/toggle-group/toggle-group.css +1 -0
- package/components/toolbar/toolbar.class.js +11 -6
- package/components/tree/tree.class.js +5 -2
- package/components/tree/tree.css +5 -2
- package/components/upload/upload.css +1 -0
- package/core/data-stream.js +21 -15
- package/core/element.js +36 -1
- package/core/element.test.js +67 -0
- package/core/icons-phosphor.js +86 -22
- package/core/icons.js +22 -8
- package/dist/theme-provider.min.js +2 -2
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +109 -108
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -5
- package/styles/colors/semantics/features.css +6 -1
- package/styles/components.css +1 -0
- package/traits/error-shake/error-shake.js +23 -17
- package/traits/fade-presence/fade-presence.js +19 -15
- package/traits/success-checkmark/success-checkmark.js +21 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
|
+
|
|
3
|
+
## [0.8.5] — 2026-07-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`<adia-mark-ui>`** (`components/adia-mark/adia-mark.{class.js,css,yaml}`; gh#283): the Adia brand mark as a token-driven primitive — background always reads `--a-canvas-surface-inverse`, the wordmark always reads `--a-fg-inverse`, so one component replaces the four hand-maintained static `agent-ui-{light,dark,light-transparent,dark-transparent}.svg` exports and adapts to scheme changes with zero asset swapping. Delivery shape (inlined custom element, not a bare SVG file or `icon-ui` registry entry) was constrained by the ticket's own finding: an `<img src="...svg">` or CSS `background-image: url(...)` reference cannot resolve the host page's CSS custom properties — only inlined markup can. `size` prop (xs/sm/md/lg/xl) matches `avatar-ui`'s scale. Verified live in both color schemes (Playwright) — background/foreground correctly invert together with no code change. Existing static-asset consumers (`public/icons/*.svg`, the a2ui corpus chunks, `apps/embedded-app`'s CSS-background brand mark) were deliberately left untouched — the static assets aren't deleted, so nothing breaks; migrating those ~35+ call sites is a separate, larger follow-up, out of scope for this ticket's `size:small` label.
|
|
7
|
+
- **`scripts/verify/exports-wildcard-resolution.mjs`** (gh#296, shared with `@adia-ai/web-modules`): live-resolution regression gate asserting every documented short-form CSS/JS subpath specifier resolves to a real on-disk file. Removed the dead `./components/*/*.css` exports-map key — confirmed via live `import.meta.resolve()` testing that it never actually resolved (Node's pattern matching always picked the single-wildcard `./components/*.css` sibling instead). Documented the underlying trap (a redundant nested specifier like `components/button/button.css` silently resolves to a garbage, nonexistent quadrupled path instead of throwing) in `USAGE.md` — not fixable in the exports map itself, since Node's `*` matches across `/` with no way to constrain it to one path segment.
|
|
8
|
+
- **`scripts/dev/audit-template-child-conflict.mjs`** (gh#284): static audit flagging a component whose `static template` is non-null AND whose yaml declares a `slots.default` entry (the container shape — nested CE children silently wiped by `stamp()`'s first-mount `replaceChildren`) or whose usage example shows body-text content (the projected-text shape, advisory). Verified against the current 168 components: zero findings — every component accepting light-DOM children currently uses `static template = () => null`. Catches a FUTURE regression rather than patching `stamp()`/`connectedCallback` itself; wired into `adia-author`'s structural-gate sequence (`npm run audit:template-child-conflict[:strict]`).
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **`button-ui` role-forwarding sweep (gh issue 276 wave 4, operator ruling)**: `button-ui` now defaults `role=button` only when the author set nothing, so composites can stamp `role=tab`/`gridcell` semantics onto the primitive. `components/calendar-grid/calendar-grid.class.js` + `components/calendar-picker/calendar-picker.class.js` stamp `<button-ui variant="ghost">` for all five shapes (prev/next nav, leading/main/trailing day cells), fixing two latent bugs at the root during migration — a `:disabled` pseudo-class selector that only matches native controls (now `[disabled]`), and a today-indicator dot that collided with `button-ui`'s own `::after`-rendered label (relocated to `::before`). `components/agent-questions/agent-questions.class.js` migrates its stamped native `<button role=radio|checkbox>` cards to `option-card-ui`; multi-select mode intercepts click/keydown at the container's capture phase to preserve toggle-off (the primitive is architecturally radio-only). `components/button/button.class.js` + `components/swiper/swiper.class.js`: swiper's pagination dots migrate from a native `<button role=tab>` to `<button-ui variant=ghost role=tab>`. Live-probed in all four components; `scripts/dev/audit-stamped-native-buttons.mjs` SANCTIONED entries removed for swiper and agent-questions.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **`icons-phosphor.js`'s auto-discovery glob breaks under pnpm workspace hoisting (gh#287)**: the baked `import.meta.glob('/node_modules/@phosphor-icons/…')` resolved as a literal path join against Vite's configured project root — under pnpm's default (non-hoisted, symlinked) linking, `@phosphor-icons/core` (a transitive dependency of this package, not necessarily the consumer's) is linked only inside this package's own resolution scope, so the glob silently matched zero files and every icon rendered blank. Switched to the entry-file-relative form (`../node_modules/…`, resolved against `icons-phosphor.js`'s own directory, one level inside the package) — confirmed empirically against a real hoisting-gap install (root cause + fix verified with a from-scratch pnpm workspace reproduction, not assumed). Also documented a second, independent failure mode named in the same issue — Vite's dev-server `optimizeDeps` pre-bundling this package empties the glob via a different mechanism (esbuild's scan transforms the glob into a literal empty object, no exception) — with the `optimizeDeps.exclude` fix and a scoped explicit-import fallback, in `icons-phosphor.js`'s JSDoc, `icons.js`'s JSDoc, `icon.class.js`'s fail-loud diagnostic, and `USAGE.md` § Icons.
|
|
15
|
+
- **Every browser-only API call site guarded for SSR (gh#285)**: `UIElement`'s constructor called `attachInternals()` unconditionally — the highest-leverage crash site, throwing before any subclass code runs — now feature-detected with a no-op `ElementInternals` shim server-side. `adoptStyles()`'s write to `document.adoptedStyleSheets` is now guarded on `'adoptedStyleSheets' in document`. Every `new ResizeObserver/IntersectionObserver/MutationObserver/PerformanceObserver` construction site across `core/element.js`, `core/data-stream.js`, and the shipped `components/{chart,color-input,color-picker,field,fields,nav,noodles,page,select,swatch,toolbar,tree}` + `traits/{error-shake,fade-presence,success-checkmark}` sources is now feature-detected (22 sites needed the guard; 10 more were already correctly guarded). Real-browser behavior unchanged — confirmed via Playwright and the full test suite; 4 new `core/element.test.js` cases simulate the actual API absence (deleting the constructors, not mocking).
|
|
16
|
+
|
|
17
|
+
### Maintenance
|
|
18
|
+
- **`styles/components.css` barrel** gained the `<adia-mark-ui>` `@import` entry (see Added, above) — without it the component would have shipped with zero CSS. **`dist/` bundles rebuilt** (`web-components.min.{css,js}`, `.sheet.js`, `theme-provider.min.js`) to pick up the `<adia-mark-ui>`, exports-map, and SSR-guard source changes above.
|
|
19
|
+
|
|
20
|
+
## [0.8.4] — 2026-07-16
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Focus rings pair with transparent borders** (operator ruling 2026-07-16): a control showing its `--a-focus-ring` box-shadow sets `border-color: transparent` in the same rule — border + gap + ring double-drew the edge (visible on the chat composer). Applied to the 21 ring-bearing controls with visible borders (input, textarea, select, combobox, check, radio, switch, range, tag[tone=outline], tags-input's missing invalid-state half, time/calendar/date-range pickers, upload dropzone, option-card, accordion[contained], agent-artifact header, button, toggle-group, chat-input); the pairing law is stated in `styles/colors/semantics/features.css` and the component-token contract.
|
|
24
|
+
- **Sidebar carets read the universal `--a-caret-size` (14px)**: tree carets were an 8px outlier (`--a-space-2`), accordion carets rode the 1rem icon default, select's trigger caret rode the ambient icon size (admin-shell's 18px bump), timeline's outcomes toggle likewise, and nav-group's collapsed block carried two `var()`-less declarations whose value was the literal token name (silently invalid — removed).
|
|
25
|
+
- **`agent-reasoning-ui` connector spine ~2px right of the dot centers**: the spine read `--timeline-marker-w` from outside timeline-ui's scope (unreachable — always the 1.25rem fallback) while the composed `size="sm"` dots center in a 1rem column; now keyed to the component's own `--agent-reasoning-marker-w`. Probe: 0px delta.
|
|
26
|
+
- **`theme-provider.min.js` inlined a stale foundation sheet**: the bundle embeds the GENERATED `dist/web-components.sheet.js`, and building JS before CSS baked the previous iteration in. `npm run build:bundles` now encodes the css→js order.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **No native `<button>` in stamped component DOM (gh#276)**: `select-ui` (clear-all / select-all / +N overflow), `tag-ui` (dismiss), `swatch-ui` (copy), `timeline-item-ui` (outcomes toggle), `chat-input-ui` (attachment remove), and `combobox-ui` (clear x) stamp `<button-ui variant="ghost" size="xs">` with token re-points — shape, semantic intent, theme tokens, and focus-ring discipline come from the primitive. Data-hooks, aria-labels, and event wiring unchanged. Parked with reasons (in `scripts/dev/audit-stamped-native-buttons.mjs`, the new CI gate): table, agent-questions option cards, swiper dots, calendar day grids.
|
|
30
|
+
- **`button-ui` respects a consumer-authored `tabindex`** instead of clobbering it with `0` on connect — enables mouse-only affordances (combobox's clear keeps `tabindex="-1"`) and roving-tabindex composition.
|
|
31
|
+
|
|
32
|
+
### Maintenance
|
|
33
|
+
- **`dist/` bundles rebuilt** (focus-ring/caret/button-ui payloads across `web-components.min.{css,js}`, `web-components.sheet.js`, `theme-provider.min.js`).
|
|
34
|
+
|
|
2
35
|
## [0.8.3] — 2026-07-16
|
|
3
36
|
|
|
4
37
|
### Maintenance
|
package/USAGE.md
CHANGED
|
@@ -50,11 +50,20 @@ import '@adia-ai/web-components/components/button';
|
|
|
50
50
|
import '@adia-ai/web-components/components/button.css';
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
> **
|
|
54
|
-
> `…/components/button`, never
|
|
55
|
-
>
|
|
56
|
-
>
|
|
57
|
-
>
|
|
53
|
+
> **Don't repeat the directory name — for `.js` OR `.css` subpaths.**
|
|
54
|
+
> Import the short form — `…/components/button`, never
|
|
55
|
+
> `…/components/button/button.js`; `…/components/button.css`, never
|
|
56
|
+
> `…/components/button/button.css`. Every component lives at
|
|
57
|
+
> `components/<name>/<name>.<ext>` on disk, so the nested form looks like
|
|
58
|
+
> the natural specifier — it isn't. The `exports` wildcard's single `*`
|
|
59
|
+
> captures the WHOLE nested subpath (it matches across `/`, with no way to
|
|
60
|
+
> constrain it to one segment) and pastes it into both `*`s of the target,
|
|
61
|
+
> quadrupling the path. Under Vite 8 (rolldown) the `.js` case fails at
|
|
62
|
+
> bundle time when it tries to read the resulting nonexistent file; the
|
|
63
|
+
> `.css` case resolves to a URL that also doesn't exist, with no clear
|
|
64
|
+
> "wrong specifier" error either way — the on-disk filename repetition is
|
|
65
|
+
> irrelevant to the subpath specifier; use the short form. Tracked as
|
|
66
|
+
> [gh#296](https://github.com/adiahealth/gen-ui-kit/issues/296).
|
|
58
67
|
|
|
59
68
|
Subpath exports also available:
|
|
60
69
|
|
|
@@ -100,34 +109,60 @@ For the live demo of every component: <https://ui-kit.exe.xyz/site/components/>.
|
|
|
100
109
|
## Icons — wiring the registry
|
|
101
110
|
|
|
102
111
|
`<icon-ui>` (and every component that renders an icon — `<button-ui icon>`,
|
|
103
|
-
`<badge-ui>`, menus, etc.) draws from an **icon registry
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
`<badge-ui>`, menus, etc.) draws from an **icon registry**. Zero-config
|
|
113
|
+
consumers get every Phosphor name for free: the main barrel
|
|
114
|
+
(`import '@adia-ai/web-components'`) side-effect-imports the package's own
|
|
115
|
+
`icons-phosphor.js`, which globs the full Phosphor set at build time. Most
|
|
116
|
+
apps need no wiring at all.
|
|
117
|
+
|
|
118
|
+
**Two Vite-specific gaps can leave the registry empty (gh#287) — both silent,
|
|
119
|
+
both fixed without touching your component code:**
|
|
120
|
+
|
|
121
|
+
1. **Vite's dev-server `optimizeDeps` pre-bundles this package.** Vite
|
|
122
|
+
pre-bundles bare-specifier dependencies with esbuild for faster cold
|
|
123
|
+
starts, before its own glob transform ever runs — that turns the
|
|
124
|
+
auto-discovery glob into a literal empty object, no exception, no
|
|
125
|
+
per-icon warning. Fix in your Vite config:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
// vite.config.js
|
|
129
|
+
export default defineConfig({
|
|
130
|
+
optimizeDeps: { exclude: ['@adia-ai/web-components', '@adia-ai/web-modules'] },
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
2. **A packaging step changes where this file lands, or you're on a version
|
|
135
|
+
older than this fix.** The auto-glob resolves `@phosphor-icons/core`
|
|
136
|
+
relative to its own file location, one level inside
|
|
137
|
+
`@adia-ai/web-components` — hoisting-safe under pnpm's default (non-hoisted)
|
|
138
|
+
linking, but it assumes the package's own internal layout is preserved. A
|
|
139
|
+
bundler step that re-nests `core/icons-phosphor.js` elsewhere, a Yarn Berry
|
|
140
|
+
PnP install, or `@phosphor-icons/core` missing entirely can still leave it
|
|
141
|
+
empty.
|
|
142
|
+
|
|
143
|
+
If neither fix applies to your setup — or you want a smaller bundle than the
|
|
144
|
+
full ~9000-chunk auto-discovery set — wire icons explicitly from your own
|
|
145
|
+
entry module instead, with plain bare-specifier imports (never affected by
|
|
146
|
+
`optimizeDeps`, since it's your own first-party source, not a dependency):
|
|
116
147
|
|
|
117
148
|
```js
|
|
118
149
|
import { installIconLoaders } from '@adia-ai/web-components/core/icons';
|
|
150
|
+
import caretRight from '@phosphor-icons/core/assets/regular/caret-right.svg?raw';
|
|
151
|
+
import x from '@phosphor-icons/core/assets/regular/x.svg?raw';
|
|
152
|
+
// ...one import per icon you use
|
|
119
153
|
|
|
120
154
|
installIconLoaders({
|
|
121
|
-
regular:
|
|
122
|
-
'/node_modules/@phosphor-icons/core/assets/regular
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// add other weights only if you use them:
|
|
126
|
-
// bold: import.meta.glob('/node_modules/@phosphor-icons/core/assets/bold/*.svg', { query: '?raw', import: 'default', eager: true }),
|
|
127
|
-
// fill: import.meta.glob('/node_modules/@phosphor-icons/core/assets/fill/*.svg', { query: '?raw', import: 'default', eager: true }),
|
|
155
|
+
regular: {
|
|
156
|
+
'/node_modules/@phosphor-icons/core/assets/regular/caret-right.svg': caretRight,
|
|
157
|
+
'/node_modules/@phosphor-icons/core/assets/regular/x.svg': x,
|
|
158
|
+
},
|
|
128
159
|
});
|
|
129
160
|
```
|
|
130
161
|
|
|
162
|
+
The map's keys only need to end with `/<weight>/<filename>.svg` — lookup
|
|
163
|
+
matches by suffix — so the `/node_modules/...` prefix above is just for
|
|
164
|
+
consistency with the glob form, not a real path.
|
|
165
|
+
|
|
131
166
|
`installIconLoaders` is idempotent, may be called before or after the
|
|
132
167
|
elements connect (icons already on the page repaint when it runs), and
|
|
133
168
|
accepts either the per-weight map above or a flat `path → svg-string` map
|
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
:scope[variant="contained"]:has(> :focus-visible),
|
|
114
114
|
:where(accordion-ui[variant="contained"]) > :scope:not([variant="flat"]):has(> :focus-visible) {
|
|
115
115
|
box-shadow: var(--accordion-item-focus-ring);
|
|
116
|
+
border-color: transparent;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
/* ── Header ── */
|
|
@@ -172,6 +173,10 @@
|
|
|
172
173
|
/* ── Caret icon ── */
|
|
173
174
|
:scope [slot="caret"] {
|
|
174
175
|
flex-shrink: 0;
|
|
176
|
+
/* Universal caret size (14px default) — one caret size across
|
|
177
|
+
nav-group / tree / accordion (ruling 2026-07-16); unset, the icon
|
|
178
|
+
rendered at the 1rem icon default and read oversized in sidebars. */
|
|
179
|
+
--a-icon-size: var(--a-caret-size);
|
|
175
180
|
color: var(--accordion-item-caret-fg);
|
|
176
181
|
transition:
|
|
177
182
|
transform var(--accordion-item-duration) var(--accordion-item-easing),
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/AdiaMark.json",
|
|
4
|
+
"title": "AdiaMark",
|
|
5
|
+
"description": "The Adia brand mark — a rounded square with the \"Adia\" wordmark cut out in a contrasting color. Token-driven: the background always reads as the current scheme's inverse surface, and the wordmark always reads as the current scheme's inverse foreground, so a single asset replaces separate light/dark/transparent static exports (gh#283). Not an `icon-ui` registry entry — icon-ui is the Phosphor-glyph registry only; this is a fixed, one-off brand image, a separate delivery shape.",
|
|
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": "AdiaMark"
|
|
18
|
+
},
|
|
19
|
+
"inline": {
|
|
20
|
+
"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.",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": false
|
|
23
|
+
},
|
|
24
|
+
"size": {
|
|
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
|
+
"type": "string",
|
|
27
|
+
"enum": [
|
|
28
|
+
"xs",
|
|
29
|
+
"sm",
|
|
30
|
+
"md",
|
|
31
|
+
"lg",
|
|
32
|
+
"xl"
|
|
33
|
+
],
|
|
34
|
+
"default": "md"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"required": [
|
|
38
|
+
"component"
|
|
39
|
+
],
|
|
40
|
+
"unevaluatedProperties": false,
|
|
41
|
+
"x-adiaui": {
|
|
42
|
+
"anti_patterns": [
|
|
43
|
+
{
|
|
44
|
+
"fix": "<adia-mark-ui size=\"sm\"></adia-mark-ui>\n",
|
|
45
|
+
"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 brand mark.\n",
|
|
46
|
+
"wrong": "<icon-ui name=\"adia\"></icon-ui>\n"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"fix": "<adia-mark-ui size=\"sm\"></adia-mark-ui>\n",
|
|
50
|
+
"why": "Hardcoding the fill colors defeats the entire point of gh#283 —\nthe mark is supposed to track the inverse-surface/inverse-foreground\ntokens automatically across scheme changes, not be pinned to one\nscheme's static values.\n",
|
|
51
|
+
"wrong": "<adia-mark-ui style=\"--adia-mark-bg: #171818; --adia-mark-fg: #E2E3E4;\"></adia-mark-ui>\n"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"category": "display",
|
|
55
|
+
"composes": [],
|
|
56
|
+
"events": {},
|
|
57
|
+
"examples": [
|
|
58
|
+
{
|
|
59
|
+
"description": "Small brand mark in an admin-shell topbar, beside the product name.",
|
|
60
|
+
"a2ui": "[\n {\n \"id\": \"brand-row\",\n \"component\": \"Row\",\n \"gap\": \"2\",\n \"align\": \"center\",\n \"children\": [\"mark\", \"name\"]\n },\n {\n \"id\": \"mark\",\n \"component\": \"AdiaMark\",\n \"size\": \"sm\"\n },\n {\n \"id\": \"name\",\n \"component\": \"Text\",\n \"variant\": \"heading\",\n \"text\": \"Adia\"\n }\n]",
|
|
61
|
+
"name": "topbar-brand-mark"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"keywords": [
|
|
65
|
+
"adia",
|
|
66
|
+
"brand",
|
|
67
|
+
"logo",
|
|
68
|
+
"wordmark",
|
|
69
|
+
"mark",
|
|
70
|
+
"identity",
|
|
71
|
+
"favicon"
|
|
72
|
+
],
|
|
73
|
+
"name": "UIAdiaMark",
|
|
74
|
+
"related": [
|
|
75
|
+
"Icon",
|
|
76
|
+
"Avatar",
|
|
77
|
+
"Image"
|
|
78
|
+
],
|
|
79
|
+
"slots": {},
|
|
80
|
+
"states": [
|
|
81
|
+
{
|
|
82
|
+
"description": "The only state — a static, non-interactive brand mark.",
|
|
83
|
+
"name": "default"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"status": "stable",
|
|
87
|
+
"synonyms": {
|
|
88
|
+
"brand": [
|
|
89
|
+
"adia",
|
|
90
|
+
"logo",
|
|
91
|
+
"mark",
|
|
92
|
+
"identity"
|
|
93
|
+
],
|
|
94
|
+
"logo": [
|
|
95
|
+
"adia",
|
|
96
|
+
"brand",
|
|
97
|
+
"mark",
|
|
98
|
+
"wordmark"
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"tag": "adia-mark-ui",
|
|
102
|
+
"tokens": {
|
|
103
|
+
"--adia-mark-bg": {
|
|
104
|
+
"description": "Background fill — always the inverse surface token, regardless of variant or scheme.",
|
|
105
|
+
"default": "var(--a-canvas-surface-inverse)"
|
|
106
|
+
},
|
|
107
|
+
"--adia-mark-fg": {
|
|
108
|
+
"description": "Wordmark fill — always the inverse foreground token, regardless of variant or scheme.",
|
|
109
|
+
"default": "var(--a-fg-inverse)"
|
|
110
|
+
},
|
|
111
|
+
"--adia-mark-radius": {
|
|
112
|
+
"description": "Corner radius of the background square.",
|
|
113
|
+
"default": "var(--a-radius-lg)"
|
|
114
|
+
},
|
|
115
|
+
"--adia-mark-size": {
|
|
116
|
+
"description": "Width and height of the square mark.",
|
|
117
|
+
"default": "2.5rem"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"traits": [],
|
|
121
|
+
"version": 1
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<adia-mark-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-mark`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* <adia-mark-ui></adia-mark-ui>
|
|
16
|
+
* <adia-mark-ui size="sm"></adia-mark-ui>
|
|
17
|
+
* <adia-mark-ui size="xl"></adia-mark-ui>
|
|
18
|
+
*
|
|
19
|
+
* The Adia brand mark — a rounded square with the "Adia" wordmark cut
|
|
20
|
+
* out in a contrasting color. Background and wordmark are ALWAYS the
|
|
21
|
+
* inverse-surface/inverse-foreground token pair (gh#283); this replaces
|
|
22
|
+
* the four separate static light/dark/transparent SVG exports
|
|
23
|
+
* (`public/icons/agent-ui-*.svg`) that had to be hand-maintained per
|
|
24
|
+
* scheme. Not themeable — see adia-mark.yaml's anti_patterns entry for
|
|
25
|
+
* why a color/tone override defeats the point.
|
|
26
|
+
*
|
|
27
|
+
* Geometry (`viewBox="0 0 100 100"`) is unchanged from the original
|
|
28
|
+
* static asset — same five wordmark paths, same background square —
|
|
29
|
+
* only the `fill` values became token references. `[data-mark-glyph]`
|
|
30
|
+
* marks the five wordmark paths so the CSS token (`--adia-mark-fg`) can
|
|
31
|
+
* target them without repeating the selector five times.
|
|
32
|
+
*
|
|
33
|
+
* Static content, zero props affect the template output — `static
|
|
34
|
+
* template` returns the same markup every render. Safe under SSR:
|
|
35
|
+
* `stamp()`'s first-mount `replaceChildren` only ever regenerates
|
|
36
|
+
* identical, template-owned content (see gh#284's survey — this
|
|
37
|
+
* component follows the same "template content never depends on
|
|
38
|
+
* light-DOM children" shape as check-ui/switch-ui, just with zero
|
|
39
|
+
* interpolated values instead of property-derived ones).
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { UIElement, html } from '../../core/element.js';
|
|
43
|
+
|
|
44
|
+
export class UIAdiaMark extends UIElement {
|
|
45
|
+
static properties = {
|
|
46
|
+
size: { type: String, default: 'md', reflect: true },
|
|
47
|
+
inline: { type: Boolean, default: false, reflect: true },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
static template = () => html`
|
|
51
|
+
<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
52
|
+
<path data-mark-glyph d="M61.3692 41.2755C61.9898 41.8703 62.7139 42.1677 63.5414 42.1677C64.3948 42.1677 65.1189 41.8703 65.7137 41.2755C66.3343 40.6548 66.6446 39.9307 66.6446 39.1032C66.6446 38.2757 66.3343 37.5516 65.7137 36.931C65.1189 36.3103 64.3948 36 63.5414 36C62.7139 36 61.9898 36.3103 61.3692 36.931C60.7744 37.5516 60.477 38.2757 60.477 39.1032C60.477 39.9307 60.7744 40.6548 61.3692 41.2755Z"></path>
|
|
53
|
+
<path data-mark-glyph d="M61.0588 44.4951V63.8902H66.0628V44.4951H61.0588Z"></path>
|
|
54
|
+
<path data-mark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M90 44.4929H84.9961V46.7815C83.4962 44.8937 81.3886 43.9498 78.6732 43.9498C76.0872 43.9498 73.8633 44.9454 72.0013 46.9366C70.1652 48.902 69.2472 51.3199 69.2472 54.1904C69.2472 57.0609 70.1652 59.4918 72.0013 61.483C73.8633 63.4484 76.0872 64.431 78.6732 64.431C81.3886 64.431 83.4962 63.4872 84.9961 61.5994V63.888H90V44.4929ZM79.6042 59.6598C78.0526 59.6598 76.7725 59.1556 75.764 58.147C74.7554 57.1126 74.2512 55.7938 74.2512 54.1904C74.2512 52.5871 74.7554 51.2812 75.764 50.2726C76.7725 49.2382 78.0526 48.721 79.6042 48.721C81.1558 48.721 82.4359 49.2382 83.4445 50.2726C84.4789 51.2812 84.9961 52.5871 84.9961 54.1904C84.9961 55.7938 84.4789 57.1126 83.4445 58.147C82.4359 59.1556 81.1558 59.6598 79.6042 59.6598Z"></path>
|
|
55
|
+
<path data-mark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M51.8676 36.7388H56.8715V63.892H51.8676V61.6034C50.3936 63.4912 48.2989 64.4351 45.5836 64.4351C42.9717 64.4351 40.7348 63.4524 38.8729 61.487C37.0368 59.4958 36.1188 57.0649 36.1188 54.1944C36.1188 51.324 37.0368 48.906 38.8729 46.9407C40.7348 44.9494 42.9717 43.9538 45.5836 43.9538C48.2989 43.9538 50.3936 44.8977 51.8676 46.7855V36.7388ZM42.6355 58.151C43.6699 59.1596 44.9629 59.6639 46.5145 59.6639C48.0662 59.6639 49.3462 59.1596 50.3548 58.151C51.3633 57.1166 51.8676 55.7978 51.8676 54.1944C51.8676 52.5911 51.3633 51.2852 50.3548 50.2766C49.3462 49.2422 48.0662 48.725 46.5145 48.725C44.9629 48.725 43.6699 49.2422 42.6355 50.2766C41.627 51.2852 41.1227 52.5911 41.1227 54.1944C41.1227 55.7978 41.627 57.1166 42.6355 58.151Z"></path>
|
|
56
|
+
<path data-mark-glyph fill-rule="evenodd" clip-rule="evenodd" d="M28.2314 59.0044L29.8606 63.892H35.6792L26.1368 36.7388H19.5036L10 63.892H15.7797L17.4089 59.0044H28.2314ZM22.8396 42.9065L26.5634 54.0005H19.1157L22.8396 42.9065Z"></path>
|
|
57
|
+
</svg>
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
connected() {
|
|
61
|
+
this.setAttribute('role', 'img');
|
|
62
|
+
if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', 'Adia');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@scope (adia-mark-ui) {
|
|
2
|
+
:where(:scope) {
|
|
3
|
+
/* ── Tokens ── */
|
|
4
|
+
--adia-mark-size: var(--a-size, 2.5rem);
|
|
5
|
+
--adia-mark-radius: var(--a-radius-lg);
|
|
6
|
+
/* Fixed by design — always the inverse pairing, never a themeable
|
|
7
|
+
variant. See adia-mark.yaml's anti_patterns entry. */
|
|
8
|
+
--adia-mark-bg: var(--a-canvas-surface-inverse);
|
|
9
|
+
--adia-mark-fg: var(--a-fg-inverse);
|
|
10
|
+
|
|
11
|
+
--adia-mark-size-xs: 1.25rem;
|
|
12
|
+
--adia-mark-size-sm: 1.75rem;
|
|
13
|
+
--adia-mark-size-md: 2.5rem;
|
|
14
|
+
--adia-mark-size-lg: 3.5rem;
|
|
15
|
+
--adia-mark-size-xl: 4.5rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:scope {
|
|
19
|
+
/* ADR-0037 — block-level by default; [inline] opts into inline-block
|
|
20
|
+
for the common "beside a text run" case (a topbar brand slot). */
|
|
21
|
+
display: block;
|
|
22
|
+
width: var(--adia-mark-size);
|
|
23
|
+
height: var(--adia-mark-size);
|
|
24
|
+
border-radius: var(--adia-mark-radius);
|
|
25
|
+
background: var(--adia-mark-bg);
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:scope[inline] { display: inline-block; }
|
|
31
|
+
|
|
32
|
+
:scope > svg {
|
|
33
|
+
display: block;
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:scope > svg [data-mark-glyph] {
|
|
39
|
+
fill: var(--adia-mark-fg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ── Size ladder ── */
|
|
43
|
+
:scope[size="xs"] { --adia-mark-size: var(--adia-mark-size-xs); }
|
|
44
|
+
:scope[size="sm"] { --adia-mark-size: var(--adia-mark-size-sm); }
|
|
45
|
+
:scope[size="md"] { --adia-mark-size: var(--adia-mark-size-md); }
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<adia-mark-ui>` — The Adia brand mark — a rounded square with the "Adia" wordmark cut out in a contrasting color. Token-driven: the background always reads as the current scheme's inverse surface, and the wordmark always reads as the current scheme's inverse foreground, so a single asset replaces separate light/dark/transparent static exports (gh#283). Not an `icon-ui` registry entry — icon-ui is the Phosphor-glyph registry only; this is a fixed, one-off brand image, a separate delivery shape.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/adia-mark
|
|
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 UIAdiaMark extends UIElement {
|
|
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
|
+
inline: boolean;
|
|
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';
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# adia-mark — Examples
|
|
2
|
+
|
|
3
|
+
## Default
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<adia-mark-ui></adia-mark-ui>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Sizes
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<adia-mark-ui size="xs"></adia-mark-ui>
|
|
13
|
+
<adia-mark-ui size="sm"></adia-mark-ui>
|
|
14
|
+
<adia-mark-ui size="md"></adia-mark-ui>
|
|
15
|
+
<adia-mark-ui size="lg"></adia-mark-ui>
|
|
16
|
+
<adia-mark-ui size="xl"></adia-mark-ui>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Topbar brand slot
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<row-ui gap="2" align="center">
|
|
23
|
+
<adia-mark-ui size="sm"></adia-mark-ui>
|
|
24
|
+
<text-ui variant="heading">Adia</text-ui>
|
|
25
|
+
</row-ui>
|
|
26
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<adia-mark-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 { UIAdiaMark } from '@adia-ai/web-components/components/adia-mark/class';
|
|
8
|
+
*
|
|
9
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineIfFree } from '../../core/register.js';
|
|
13
|
+
import { UIAdiaMark } from './adia-mark.class.js';
|
|
14
|
+
|
|
15
|
+
defineIfFree('adia-mark-ui', UIAdiaMark);
|
|
16
|
+
|
|
17
|
+
export { UIAdiaMark };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import '../../core/element.js';
|
|
3
|
+
import './adia-mark.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-mark-ui', () => {
|
|
15
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
16
|
+
|
|
17
|
+
it('registers adia-mark-ui as a custom element', () => {
|
|
18
|
+
expect(customElements.get('adia-mark-ui')).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('defaults to size="md"', async () => {
|
|
22
|
+
const el = mount('<adia-mark-ui></adia-mark-ui>');
|
|
23
|
+
await tick();
|
|
24
|
+
expect(el.size).toBe('md');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('reflects an explicit [size]', async () => {
|
|
28
|
+
const el = mount('<adia-mark-ui size="xl"></adia-mark-ui>');
|
|
29
|
+
await tick();
|
|
30
|
+
expect(el.size).toBe('xl');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('defaults to inline=false and reflects an explicit [inline]', async () => {
|
|
34
|
+
const a = mount('<adia-mark-ui></adia-mark-ui>');
|
|
35
|
+
const b = mount('<adia-mark-ui inline></adia-mark-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-mark-ui></adia-mark-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-mark-ui aria-label="Adia — home"></adia-mark-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-mark-ui></adia-mark-ui>');
|
|
57
|
+
await tick();
|
|
58
|
+
const svg = el.querySelector('svg');
|
|
59
|
+
expect(svg).toBeTruthy();
|
|
60
|
+
expect(el.querySelectorAll('[data-mark-glyph]').length).toBe(5);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('renders identically regardless of size — static template, no interpolation', async () => {
|
|
64
|
+
const a = mount('<adia-mark-ui size="sm"></adia-mark-ui>');
|
|
65
|
+
const b = mount('<adia-mark-ui size="xl"></adia-mark-ui>');
|
|
66
|
+
await tick();
|
|
67
|
+
expect(a.innerHTML).toBe(b.innerHTML);
|
|
68
|
+
});
|
|
69
|
+
});
|