@adia-ai/web-components 0.5.14 → 0.5.15
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 +187 -0
- package/components/heatmap/heatmap.css +1 -1
- package/components/list/list.css +6 -6
- package/components/nav-group/nav-group.css +2 -2
- package/components/page/page.css +1 -1
- package/components/select/select.css +2 -2
- package/components/stepper/stepper.css +24 -24
- package/components/swatch/class.js +56 -1
- package/components/swatch/swatch.css +2 -2
- package/components/swatch/swatch.test.js +163 -0
- package/components/text/text.a2ui.json +16 -2
- package/components/text/text.d.ts +28 -45
- package/components/text/text.yaml +25 -1
- package/components/toolbar/toolbar.css +1 -1
- package/package.json +1 -1
- package/styles/colors/semantics.css +7 -84
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,193 @@ runtime ships in the sibling `@adia-ai/a2ui-runtime` package
|
|
|
11
11
|
|
|
12
12
|
_No pending changes._
|
|
13
13
|
|
|
14
|
+
## [0.5.15] - 2026-05-16
|
|
15
|
+
|
|
16
|
+
> **Scope note:** §325 + §326 were originally planned as a separate v0.5.16 cycle (see retired `docs/plans/0.5.16-release-plan.md`); absorbed into v0.5.15 since both landed within the same release window (same-day commits prior to bump). The peer's v0.5.16 plan-doc explicitly anticipated this absorption option ("Hermes's call per `/lockstep-release`"). v0.5.16 plan-doc marked SHIPPED-IN-V0.5.15.
|
|
17
|
+
|
|
18
|
+
### v0.5.15 §326 — `<swatch-ui>` `[slot="chrome"]` lost in template contexts (FB-37; P1)
|
|
19
|
+
|
|
20
|
+
Closes FB-37. Pre-§326, `<swatch-ui>` consumed via any `html\`\`` template engine (or any engine that batches child appends separately from element creation) lost its `[slot="chrome"]` children entirely. Root cause: `#stamp()` is gated by a `#stamped` once-only guard + scans `this.children` for chrome slots at `connectedCallback` time. Template engines create the host via `createElement` + connect it FIRST (children empty), then batch-append children AFTER. `#stamp()`'s chrome scan returns `[]`; the `#stamped` guard prevents re-runs; chrome children landed at the END of host's children (after `data-copy`) instead of the canonical sibling-of-tile position. Blocked Tokens Studio's C1.3 dogfood migration (9 failing Playwright tests root-caused to this) — and >95% of AdiaUI consumers using `html\`\`` templates.
|
|
21
|
+
|
|
22
|
+
#### Fixed — `components/swatch/class.js`
|
|
23
|
+
|
|
24
|
+
- NEW `#absorbChromeSlot()` helper. Idempotent. Walks `this.children`; for any child with `slot="chrome"` that's not one of the internal stamped elements + not in the canonical position, calls `this.insertBefore(child, this.#badgeEl)` to move it to the slot between `#tileEl` + `#badgeEl`.
|
|
25
|
+
- `#syncCore()` calls `#absorbChromeSlot()` at the top of every render pass. Late-arriving chrome children (template-render path) get picked up on the next reactive cycle; pre-positioned chrome children (static-HTML path) are no-op-moves.
|
|
26
|
+
- `#stamp()`'s once-only eager scan stays intact for the static-HTML path; `#absorbChromeSlot()` is the safety net for late-append.
|
|
27
|
+
|
|
28
|
+
#### Added — `components/swatch/swatch.test.js`
|
|
29
|
+
|
|
30
|
+
- 2 NEW vitest cases under "FB-37 / §326":
|
|
31
|
+
- `absorbs chrome child appended AFTER connectedCallback (template-render path)` — mimics the bug's exact timing (create + connect with empty children → late append → property mutation triggers render → fix picks up the chrome child).
|
|
32
|
+
- `still handles chrome child appended BEFORE connect (static-HTML path)` — regression check that §314's original behavior still works.
|
|
33
|
+
- Total suite: 6 cases (4 from §325 + 2 from §326).
|
|
34
|
+
|
|
35
|
+
#### Migration
|
|
36
|
+
|
|
37
|
+
Consumers using `<swatch-ui>` via `html\`\`` template engines (lit-html, AdiaUI's own `html\`\``, Preact, Vue, etc.) get the §314 chrome-slot feature working as documented in v0.5.13's `swatch.yaml slots:` block. No consumer-side changes required — the §326 fix is invisible to consumers writing canonical templates.
|
|
38
|
+
|
|
39
|
+
### v0.5.15 §325 — `<swatch-ui>` default-slot wiped by label-attr (FB-36)
|
|
40
|
+
|
|
41
|
+
Closes FB-36 + FB-36-followup. Pre-§325, `<swatch-ui label="500"><span>tip</span></swatch-ui>` written as indented multi-line HTML dropped the default-slot child entirely; only `label="500"` rendered. Root cause: `#stamp()`'s `Array.from(this.childNodes).filter(...)` captured pure-whitespace text nodes from the indentation; those landed in `#labelEl.firstChild`; `#syncCore()`'s "stale text-node" branch (designed for runtime `el.label = 'X'` mutations on attr-only swatches) then matched the whitespace + did `textContent = this.label`, wiping both the whitespace AND the consumer's `<span>`.
|
|
42
|
+
|
|
43
|
+
Pre-§325 the yaml spec ("Use the default slot for richer content" — `swatch.yaml:40`) and the implementation contradicted each other; post-§325 they agree.
|
|
44
|
+
|
|
45
|
+
#### Fixed — `components/swatch/class.js`
|
|
46
|
+
|
|
47
|
+
- `#stamp()`'s `slotted` filter chain gains `&& !(n.nodeType === 3 && !n.textContent.trim())` — strips pure-whitespace text nodes from the capture. `#labelEl.firstChild` now becomes the consumer's element (or non-whitespace text), so `#syncCore`'s stale-text-node branch fires only for genuinely attr-driven cases.
|
|
48
|
+
|
|
49
|
+
#### Added — `components/swatch/swatch.test.js`
|
|
50
|
+
|
|
51
|
+
- NEW 4-case vitest suite covering FB-36's 3 input axes (default-slot present/absent × label-attr present/absent × runtime mutation). Locks the §325 fix structurally — future regressions of any of the 4 paths fail CI.
|
|
52
|
+
|
|
53
|
+
#### Migration
|
|
54
|
+
|
|
55
|
+
`<swatch-ui label="500"><span>tip</span></swatch-ui>` now behaves per yaml spec: default-slot wins, label-attr drops. Consumers using the workaround `.label=` property binding (per FB-36-followup) to bypass the bug can switch back to the canonical `[label]` attribute form post-upgrade.
|
|
56
|
+
|
|
57
|
+
### v0.5.15 §324 — Yaml `enum_descriptions:` codegen extension (v0.6.0 FB-23 §3 retire carry-forward, PATCH-eligible)
|
|
58
|
+
|
|
59
|
+
Closes the v0.6.0 carry-forward "Codegen yaml `enum:` per-member descriptions (FB-23 §3 retire)" from the RESPONSE-29 carry-forwards table. v0.5.15 ships the additive codegen surface on PATCH cadence; v0.6.0 no longer needs to carry this item.
|
|
60
|
+
|
|
61
|
+
**The surface change:** `scripts/schemas/component.yaml.schema.json` `Prop` definition gains an optional `enum_descriptions:` object field — when present alongside `enum:`, dts-codegen emits an exported type alias (e.g. `UITextVariant`) above the class declaration with per-variant JSDoc carrying each description. IDE hover surfaces the disambiguation at authoring time without requiring hand-authored .d.ts.
|
|
62
|
+
|
|
63
|
+
#### Added
|
|
64
|
+
|
|
65
|
+
- `scripts/schemas/component.yaml.schema.json` — NEW optional `enum_descriptions: { type: object, additionalProperties: { type: string } }` field per prop. Backward-compatible: existing yamls without the field continue to emit inline enum unions.
|
|
66
|
+
- `scripts/build/components.mjs` — forwards `enum_descriptions:` through `compileProp()` into the a2ui.json sidecar.
|
|
67
|
+
- `scripts/build/dts-codegen.mjs` — `emitEnumTypeAlias()` helper detects props with both `enum:` and `enum_descriptions:`; emits a typed union alias `${className}${PascalCase(propName)}` with per-variant JSDoc; class property references the alias instead of inlining the union.
|
|
68
|
+
|
|
69
|
+
#### Changed — `components/text/text.yaml`
|
|
70
|
+
|
|
71
|
+
- `variant:` prop gains 12-entry `enum_descriptions:` block (one description per variant). Prop `description:` block also gains the FB-23 §2 ARIA-role disclaimer that previously lived in the hand-authored .d.ts.
|
|
72
|
+
|
|
73
|
+
#### Changed — `scripts/build/dts-codegen.mjs` `HAND_AUTHORED_DTS`
|
|
74
|
+
|
|
75
|
+
- `text` removed from skip-list. `text.d.ts` now codegen-driven; per-variant JSDoc surfaces via the new `enum_descriptions:` field. Net effect: 1 of 3 remaining `HAND_AUTHORED_DTS` entries closed (`toast` + `feed` remain — both carry runtime API the yaml shape can't express).
|
|
76
|
+
|
|
77
|
+
#### Regenerated — `components/text/text.d.ts`
|
|
78
|
+
|
|
79
|
+
- Replaces hand-authored .d.ts with codegen output. `UITextVariant` type alias preserved as exported type; per-variant JSDoc preserved verbatim. Net result: zero consumer-facing diff in the .d.ts surface; codegen takes over maintenance.
|
|
80
|
+
|
|
81
|
+
### v0.5.15 §322 — NEW audit slot 32: `check-dead-constants.mjs`
|
|
82
|
+
|
|
83
|
+
Graduates the FB-35 bug class (declared-but-unread module-level consts) to a CI gate. Walks every `.js` file under `packages/<pkg>/`; extracts SCREAMING_SNAKE_CASE / PascalCase `const NAME = ...;` declarations at module level; flags zero-reference cases. Skips `export const NAME` (consumed externally) + test/spec/examples/config files.
|
|
84
|
+
|
|
85
|
+
First sweep surfaced + removed 2 real dead constants:
|
|
86
|
+
- `packages/a2ui/validator/validator.js:38` — `WIRING_WEIGHTS` (vestigial from planned weighted-scoring system; safe deletion).
|
|
87
|
+
- `packages/llm/adapters/openai.js:10` — `DEFAULT_MAX_TOKENS = 4096` (replaced by explicit `opts.max_tokens` plumbing).
|
|
88
|
+
|
|
89
|
+
### v0.5.15 §323 — NEW audit slot 33: `check-hover-guard-discipline.mjs`
|
|
90
|
+
|
|
91
|
+
Graduates the `analyze-css` bare-`:hover` discipline class to a CI gate. Detects `:hover` rules on host primitives + interactive slots/roles that lack a `:not([disabled])` guard AND set visual properties (background/color/border/box-shadow/etc.). Skips cursor-only / outline-style-only rule bodies.
|
|
92
|
+
|
|
93
|
+
First sweep surfaced 19 findings across 11 components — all baselined in EXPECTED_DRIFT for cycle-by-cycle close:
|
|
94
|
+
- **Real drift** (rely on `pointer-events: none` for click-suppression but hover styles still apply): action-list (4×), menu (1×), nav-item (2×), nav-group (1×).
|
|
95
|
+
- **Slotted-trigger drift** (mitigated via parent guard rules; cosmetic-OK but worth tightening): select (5×), calendar-picker (1×), range (2×), tag (1×).
|
|
96
|
+
- **Decorative-pop hover** (intentional): avatar (1×). Long-term whitelist candidate.
|
|
97
|
+
- **@scope-Safari-17.x-workaround**: action-list (3 of 4) + nav-item (2) + rating (1) — guard can't live inside @scope; lift alongside the hover rule.
|
|
98
|
+
|
|
99
|
+
#### Removed — dead module-level constants
|
|
100
|
+
|
|
101
|
+
- `packages/a2ui/validator/validator.js` — `WIRING_WEIGHTS` constant (zero refs).
|
|
102
|
+
- `packages/llm/adapters/openai.js` — `DEFAULT_MAX_TOKENS` constant (zero refs).
|
|
103
|
+
|
|
104
|
+
### v0.5.15 §319 — NEW audit slot 29: `check-exports-map-resolves.mjs`
|
|
105
|
+
|
|
106
|
+
Graduates the FB-32 bug class to a CI gate. Closes the recurring drift where a package.json `exports` map entry resolves (via wildcard expansion or static path) to a file that doesn't exist on disk — consumer TS imports fail with `TS2882` even though Vite/Node can fall through `node_modules/<pkg>/<path>` directly.
|
|
107
|
+
|
|
108
|
+
#### Added — `scripts/release/check-exports-map-resolves.mjs`
|
|
109
|
+
|
|
110
|
+
- ~270 LOC. Walks every `packages/<pkg>/package.json` `exports` map. For each STATIC entry, verifies the referenced file path exists. For each WILDCARD entry matching the FB-32 shape (`./X/*` key + `./X/*/*.<ext>` value), enumerates filesystem candidates that would match the wildcard + verifies each resolved path exists.
|
|
111
|
+
- Skips wildcard expansions where an explicit non-wildcard entry overrides the resolution (Node exports-map resolution algorithm precedence).
|
|
112
|
+
- Skips CSS-only / no-JS components (header / footer / section / aside / various web-modules shells) — these intentionally ship no `.js`.
|
|
113
|
+
- Result: 9 packages / 52 static + 39 wildcard exports entries / 59 resolutions verified / 0 unresolved.
|
|
114
|
+
|
|
115
|
+
### v0.5.15 §320 — NEW audit slot 30: `check-composes-passthrough.mjs`
|
|
116
|
+
|
|
117
|
+
Graduates the FB-33 bug class to a CI gate. Closes the recurring drift where a form-bearing primitive that programmatically creates ANOTHER form-bearing primitive in its lifecycle (light-DOM composition) doesn't expose / forward the composed primitive's documented prop + event-detail surface.
|
|
118
|
+
|
|
119
|
+
#### Added — `scripts/release/check-composes-passthrough.mjs`
|
|
120
|
+
|
|
121
|
+
- ~270 LOC. Static `COMPOSITION_PAIRS` list declares the required pass-through surface per pair. For each pair: verifies composing primitive's yaml `composes:` block lists the composed primitive; verifies yaml `props:` block exposes every required prop; verifies yaml `events:` block declares each event with required detail keys.
|
|
122
|
+
- Initial pair: `color-input → color-picker` (8 props + 12 event-detail keys across `change` + `input`).
|
|
123
|
+
- Adding a new pair: any new primitive that programmatically wraps a form-bearing primitive declares its contract in `COMPOSITION_PAIRS`; the audit fires at PR time.
|
|
124
|
+
- Companion to slot 23 (`check-form-bearing-dts.mjs`): that audit checks .d.ts type-coverage; this audit checks yaml-declared shape.
|
|
125
|
+
|
|
126
|
+
### v0.5.15 §321 — NEW audit slot 31: `check-token-name-canonical-shape.mjs`
|
|
127
|
+
|
|
128
|
+
Graduates the stepper / list state-first naming drift class to a CI gate. Closes the recurring drift where a component-local CSS token uses state-first (`--stepper-active-bg`) or color-infix-state (`--link-color-hover`) naming instead of the canonical property-first state-suffix shape (`--<comp>(-<sub>)*-<prop>(-<state>)?`).
|
|
129
|
+
|
|
130
|
+
#### Added — `scripts/release/check-token-name-canonical-shape.mjs`
|
|
131
|
+
|
|
132
|
+
- ~210 LOC. Extracts `--<comp>-*` declarations from each component CSS file. Classifies each by segment shape: canonical-prop / canonical-prop-state / canonical-leaf / canonical-domain / drift-state-first / drift-infix-color-state.
|
|
133
|
+
- Vocabularies (PROP_TOKENS + STATE_TOKENS) capture the catalog's recognized property + state terms (bg/fg/border/ring/shadow/color/outline/size + hover/active/selected/disabled/focus/checked/done/...).
|
|
134
|
+
- Result: 92 components / 1809 component-local tokens audited / 0 drift findings.
|
|
135
|
+
- `link.css` `--link-color-hover` not flagged at v0.5.15 ship: `color` is a recognized PROP_TOKEN + `hover` is a recognized STATE_TOKEN — the shape matches canonical. A future, stricter audit (alternative-property-naming) could flag the `color` vs `fg` divergence; deferred to v0.6.0 BREAKING window since link.css's `--link-color-*` are documented public API.
|
|
136
|
+
|
|
137
|
+
### v0.5.15 §316 — Naming-convention drift fix (stepper + list state-first → property-first)
|
|
138
|
+
|
|
139
|
+
`<stepper-ui>` and `<list-ui>` declared internal tokens with state-first naming (`--stepper-active-bg`, `--list-selected-fg`, etc.) — the catalog convention is property-first (`--<comp>-{prop}-{state}`). 18 token names renamed; usage sites updated in-place. `<link-ui>` skipped — its `--link-color-*` tokens are documented public API per `link.yaml tokens:` block; renaming is BREAKING and defers to v0.6.0.
|
|
140
|
+
|
|
141
|
+
#### Changed — `components/stepper/`, `components/list/`
|
|
142
|
+
|
|
143
|
+
- `stepper.css` — 12 tokens renamed: `--stepper-{active,done}-{bg,border,fg}` + `--stepper-item-{active,done}-{bg,border,fg}` → `--stepper-{bg,border,fg}-{active,done}` + `--stepper-item-{bg,border,fg}-{active,done}`.
|
|
144
|
+
- `list.css` — 3 tokens renamed: `--list-selected-{bg,fg,border}` → `--list-{bg,fg,border}-selected`.
|
|
145
|
+
|
|
146
|
+
### v0.5.15 §316 — Hand-rolled transition-duration literals → token references (swatch + heatmap + page)
|
|
147
|
+
|
|
148
|
+
Three components hand-rolled `100ms ease-out` / `120ms ease-out` / `0.1s ease-out` / `150ms ease` instead of routing through the canonical `var(--a-duration-fast)` + `var(--a-easing*)` token chain. Theme designers couldn't swap transition cadence centrally.
|
|
149
|
+
|
|
150
|
+
#### Changed — `components/swatch/`, `components/heatmap/`, `components/page/`
|
|
151
|
+
|
|
152
|
+
- `swatch.css:294,311` — `100ms ease-out` + `120ms ease-out` → `var(--a-duration-fast) var(--a-easing-out)`.
|
|
153
|
+
- `heatmap.css:80` — `0.1s ease-out` → `var(--a-duration-fast) var(--a-easing-out)`.
|
|
154
|
+
- `page.css:61` — `150ms ease` → `var(--a-duration-fast) var(--a-easing)`.
|
|
155
|
+
|
|
156
|
+
### v0.5.15 §317 — NEW audit slot 27: `check-raw-fg-in-component-css.mjs`
|
|
157
|
+
|
|
158
|
+
Graduates the §313/§315/§316 bug class to a CI gate. Closes the recurring drift class where component CSS rules bypass their own `--<comp>-<family>-<state>` semantic-layer token and reference the raw global `--a-<family>-<state>` token directly. The audit slot found 5 additional bypass instances across the codebase (nav-group + select + toolbar) — all routed through the component-local semantic layer in this commit.
|
|
159
|
+
|
|
160
|
+
#### Added — `scripts/release/check-raw-fg-in-component-css.mjs`
|
|
161
|
+
|
|
162
|
+
- ~190 LOC audit script. Parses each `<comp>.css` for the `:where(:scope) { ... }` declaration block + the set of `--<comp>-*` tokens defined therein. Scans the remainder for `<standard-css-property>: var(--a-<family>-<state>?)` usage where the parallel `--<comp>-<family>-<state>?` is defined — emits a finding per bypass.
|
|
163
|
+
- Whitelisted CSS properties: `color`, `background`, `background-color`, `border` (+ all sub-axis variants), `outline`, `fill`, `stroke`, `caret-color`, `accent-color`, `text-decoration-color`, `column-rule-color`.
|
|
164
|
+
- Distinguishes declaration sites (`--<comp>-X: var(--a-X)`) from usage sites mechanically — declaration sites are NOT flagged (those are correct semantic-layer aliasing).
|
|
165
|
+
- Companion to slot 25 (`check-component-css-vs-styles-tokens.mjs`): that audit catches orphan references (`var(--a-X)` where `--a-X` doesn't exist); this audit catches the inverse (`var(--a-X)` where it exists but `--<comp>-X` should be preferred).
|
|
166
|
+
- EXPECTED_FINDINGS baseline: 0 entries at v0.5.15 ship (sweep landed every concrete bypass).
|
|
167
|
+
|
|
168
|
+
#### Fixed — 5 component CSS bypass sites
|
|
169
|
+
|
|
170
|
+
- `nav-group.css:243,252` — `background: var(--a-bg-hover)` → `var(--nav-group-bg-hover)`.
|
|
171
|
+
- `select.css:199` — listbox container `color: var(--a-fg)` → `var(--select-fg)`.
|
|
172
|
+
- `select.css:319` — hint slot fallback `var(--a-fg-muted)` → `var(--select-fg-muted)`.
|
|
173
|
+
- `toolbar.css:107` — overflow popover `color: var(--a-fg)` → `var(--toolbar-fg)`.
|
|
174
|
+
|
|
175
|
+
### v0.5.15 §317 — NEW audit slot 28: `check-form-bearing-events-symmetric.mjs`
|
|
176
|
+
|
|
177
|
+
Graduates the FB-31/§315 bug class to a CI gate. Closes the recurring drift class where UIFormElement-extending primitives emit only `input` (or only `complete`/`search`/`pick`) without the canonical `change` event with `detail.value`. The audit confirms all 17 form-bearing primitives are now symmetric post-v0.5.14 §315.
|
|
178
|
+
|
|
179
|
+
#### Added — `scripts/release/check-form-bearing-events-symmetric.mjs`
|
|
180
|
+
|
|
181
|
+
- ~145 LOC audit script. Walks every `class.js` for classes that `extends UIFormElement`. For each, verifies a `dispatchEvent(new CustomEvent('change', { detail: { value: ... } }))` occurs (statically or via dynamic-name dispatch + nearby `'change'` string literal + `detail = { value: ... }` construction).
|
|
182
|
+
- Heuristic limitations documented: false negatives possible if event name is computed via complex variable assignment; manual review at addition time required.
|
|
183
|
+
- ALLOW_LIST mechanism for primitives where the commit model legitimately omits `change` (none at v0.5.15 ship).
|
|
184
|
+
- Companion to slot 23 (`check-form-bearing-dts.mjs`): that audit verifies typed `<Comp>ChangeEvent` exists in `.d.ts`; this audit verifies the runtime dispatch.
|
|
185
|
+
|
|
186
|
+
### v0.5.15 §318 — ADR-0028: L3 state-matrix orphan retirement (78 unused tokens deleted)
|
|
187
|
+
|
|
188
|
+
ADR-0028 ratifies the deletion of 78 orphaned L3 state-matrix tokens from `semantics.css`. The `maintain-tokens` skill audit during v0.5.14 §315 surfaced that 73 of ~80 L3 `--a-{family}-{prop}-{state}` tokens have zero consumers across the entire codebase. The per-primitive `--<comp>-*` token pattern (validated across v0.5.13/14/15 sweeps) is the actual consumer-facing API.
|
|
189
|
+
|
|
190
|
+
#### Removed — `styles/colors/semantics.css`
|
|
191
|
+
|
|
192
|
+
- 78 `--a-{accent,primary,brand,info,success,warning,danger,fg,border}-{fg,bg,border}-{active,selected,disabled,invalid}` declarations deleted. ~80 LOC reduction.
|
|
193
|
+
- Token count dropped 1301 → 1217 (-6.5%).
|
|
194
|
+
- Kept (have consumers): `--a-bg-{active,selected,disabled,invalid}`, `--a-fg-{selected,disabled}`, `--a-accent-bg-active`, `--a-primary-bg-active` (transitive via accent-bg-active).
|
|
195
|
+
- Kept (consumer-facing hover state): all `--a-*-hover` variants — hover is the only state where the generic-L3 layer is load-bearing.
|
|
196
|
+
|
|
197
|
+
#### Added — `.brain/adrs/0028-l3-state-matrix-orphan-retirement.md`
|
|
198
|
+
|
|
199
|
+
- ADR documents the audit-driven discovery, the retention rationale for the 7 used tokens, the deletion rationale for the 78 orphans, alternatives considered (keep all / delete only family-specific / add audit), and the per-primitive convention as the canonical consumer-facing override surface.
|
|
200
|
+
|
|
14
201
|
## [0.5.14] - 2026-05-15
|
|
15
202
|
|
|
16
203
|
### v0.5.14 — Form-control hover-fg token unification (precursor to §315; commit `178418525`)
|
package/components/list/list.css
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
:where(:scope) {
|
|
3
3
|
/* ── Colors ── */
|
|
4
4
|
--list-divider-color: var(--a-border-subtle);
|
|
5
|
-
--list-selected
|
|
6
|
-
--list-selected
|
|
7
|
-
--list-selected
|
|
5
|
+
--list-bg-selected: var(--a-accent-muted);
|
|
6
|
+
--list-fg-selected: var(--a-accent-strong);
|
|
7
|
+
--list-border-selected: var(--a-accent-strong);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
:scope {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
:scope[selectable] > [role="listitem"][aria-selected="true"] {
|
|
71
|
-
background: var(--list-selected
|
|
72
|
-
color: var(--list-selected
|
|
73
|
-
border-inline-start-color: var(--list-selected
|
|
71
|
+
background: var(--list-bg-selected);
|
|
72
|
+
color: var(--list-fg-selected);
|
|
73
|
+
border-inline-start-color: var(--list-border-selected);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
:scope[selectable] > [role="listitem"]:focus-visible {
|
|
@@ -240,7 +240,7 @@ nav-group-ui [slot="popover"] [role="option"] {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
nav-group-ui [slot="popover"] [role="option"]:hover {
|
|
243
|
-
background: var(--
|
|
243
|
+
background: var(--nav-group-bg-hover);
|
|
244
244
|
color: var(--nav-group-fg-hover);
|
|
245
245
|
}
|
|
246
246
|
|
|
@@ -249,7 +249,7 @@ nav-group-ui [slot="popover"] [role="option"]:hover {
|
|
|
249
249
|
nav-group-ui [slot="popover"] [role="option"][aria-current="page"],
|
|
250
250
|
nav-group-ui [slot="popover"] [role="option"][aria-selected="true"] {
|
|
251
251
|
position: relative;
|
|
252
|
-
background: var(--
|
|
252
|
+
background: var(--nav-group-bg-hover);
|
|
253
253
|
color: var(--nav-group-fg-selected);
|
|
254
254
|
font-weight: var(--a-weight-medium);
|
|
255
255
|
}
|
package/components/page/page.css
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
top: 0;
|
|
59
59
|
z-index: 1;
|
|
60
60
|
background: var(--page-sticky-bg);
|
|
61
|
-
transition: border-color
|
|
61
|
+
transition: border-color var(--a-duration-fast) var(--a-easing), box-shadow var(--a-duration-fast) var(--a-easing);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
:scope[data-header-stuck] > :where(header, header-ui) {
|
|
@@ -196,7 +196,7 @@ select-ui [slot="listbox"] {
|
|
|
196
196
|
overflow-y: auto;
|
|
197
197
|
font-family: inherit;
|
|
198
198
|
font-size: var(--a-ui-size);
|
|
199
|
-
color: var(--
|
|
199
|
+
color: var(--select-fg);
|
|
200
200
|
|
|
201
201
|
/* Positioned by JS (#positionListbox) — fixed to viewport */
|
|
202
202
|
width: max-content;
|
|
@@ -316,6 +316,6 @@ select-ui > [slot="hint"] {
|
|
|
316
316
|
display: block;
|
|
317
317
|
margin-top: var(--select-hint-mt, var(--a-space-1));
|
|
318
318
|
font-size: var(--select-hint-size, var(--a-ui-xs));
|
|
319
|
-
color: var(--select-hint-fg, var(--
|
|
319
|
+
color: var(--select-hint-fg, var(--select-fg-muted));
|
|
320
320
|
line-height: var(--select-hint-lh, 1.4);
|
|
321
321
|
}
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
--stepper-weight: var(--a-weight-medium);
|
|
21
21
|
--stepper-border-size: 2px;
|
|
22
22
|
|
|
23
|
-
--stepper-active
|
|
24
|
-
--stepper-active
|
|
25
|
-
--stepper-active
|
|
23
|
+
--stepper-bg-active: var(--a-bg);
|
|
24
|
+
--stepper-border-active: var(--a-accent);
|
|
25
|
+
--stepper-fg-active: var(--a-accent);
|
|
26
26
|
|
|
27
|
-
--stepper-done
|
|
28
|
-
--stepper-done
|
|
29
|
-
--stepper-done
|
|
27
|
+
--stepper-bg-done: var(--a-accent);
|
|
28
|
+
--stepper-border-done: var(--a-accent);
|
|
29
|
+
--stepper-fg-done: var(--a-accent-fg);
|
|
30
30
|
|
|
31
31
|
--stepper-line: var(--a-border-subtle);
|
|
32
32
|
--stepper-line-done: var(--a-accent);
|
|
@@ -76,13 +76,13 @@
|
|
|
76
76
|
--stepper-item-weight: var(--stepper-weight, var(--a-weight-medium));
|
|
77
77
|
--stepper-item-border-size: var(--stepper-border-size, 2px);
|
|
78
78
|
|
|
79
|
-
--stepper-item-active
|
|
80
|
-
--stepper-item-active
|
|
81
|
-
--stepper-item-active
|
|
79
|
+
--stepper-item-bg-active: var(--stepper-bg-active, var(--a-bg));
|
|
80
|
+
--stepper-item-border-active: var(--stepper-border-active, var(--a-accent));
|
|
81
|
+
--stepper-item-fg-active: var(--stepper-fg-active, var(--a-accent));
|
|
82
82
|
|
|
83
|
-
--stepper-item-done
|
|
84
|
-
--stepper-item-done
|
|
85
|
-
--stepper-item-done
|
|
83
|
+
--stepper-item-bg-done: var(--stepper-bg-done, var(--a-accent));
|
|
84
|
+
--stepper-item-border-done: var(--stepper-border-done, var(--a-accent));
|
|
85
|
+
--stepper-item-fg-done: var(--stepper-fg-done, var(--a-accent-fg));
|
|
86
86
|
|
|
87
87
|
--stepper-item-line: var(--stepper-line, var(--a-border-subtle));
|
|
88
88
|
--stepper-item-line-done: var(--stepper-line-done, var(--a-accent));
|
|
@@ -158,9 +158,9 @@
|
|
|
158
158
|
|
|
159
159
|
/* Active state */
|
|
160
160
|
:scope[status="active"]::after {
|
|
161
|
-
border-color: var(--stepper-item-active
|
|
162
|
-
color: var(--stepper-item-active
|
|
163
|
-
background: var(--stepper-item-active
|
|
161
|
+
border-color: var(--stepper-item-border-active);
|
|
162
|
+
color: var(--stepper-item-fg-active);
|
|
163
|
+
background: var(--stepper-item-bg-active);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
:scope[status="active"] [slot="label"] {
|
|
@@ -171,9 +171,9 @@
|
|
|
171
171
|
/* Completed state — checkmark replaces number */
|
|
172
172
|
:scope[status="completed"]::after {
|
|
173
173
|
content: '\2713';
|
|
174
|
-
background: var(--stepper-item-done
|
|
175
|
-
border-color: var(--stepper-item-done
|
|
176
|
-
color: var(--stepper-item-done
|
|
174
|
+
background: var(--stepper-item-bg-done);
|
|
175
|
+
border-color: var(--stepper-item-border-done);
|
|
176
|
+
color: var(--stepper-item-fg-done);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
:scope[status="completed"]::before {
|
|
@@ -214,15 +214,15 @@
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
:scope[status="active"] [slot="icon"] {
|
|
217
|
-
border-color: var(--stepper-item-active
|
|
218
|
-
color: var(--stepper-item-active
|
|
219
|
-
background: var(--stepper-item-active
|
|
217
|
+
border-color: var(--stepper-item-border-active);
|
|
218
|
+
color: var(--stepper-item-fg-active);
|
|
219
|
+
background: var(--stepper-item-bg-active);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
:scope[status="completed"] [slot="icon"] {
|
|
223
|
-
background: var(--stepper-item-done
|
|
224
|
-
border-color: var(--stepper-item-done
|
|
225
|
-
color: var(--stepper-item-done
|
|
223
|
+
background: var(--stepper-item-bg-done);
|
|
224
|
+
border-color: var(--stepper-item-border-done);
|
|
225
|
+
color: var(--stepper-item-fg-done);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/* ── Content slots ── */
|
|
@@ -226,6 +226,17 @@ export class UISwatch extends UIElement {
|
|
|
226
226
|
// Capture pre-existing default-slot content so consumer-authored
|
|
227
227
|
// children (e.g. <swatch-ui>Forecast</swatch-ui>) survive stamping.
|
|
228
228
|
// `[slot="chrome"]` children are already removed above, so won't appear here.
|
|
229
|
+
//
|
|
230
|
+
// §325 (v0.5.16, FB-36): filter pure-whitespace text nodes from the
|
|
231
|
+
// capture. Pre-§325, indented multi-line HTML (`<swatch-ui
|
|
232
|
+
// label="500">\n <span>tip</span>\n</swatch-ui>`) captured the
|
|
233
|
+
// leading whitespace text node, which then made #labelEl.firstChild
|
|
234
|
+
// a nodeType=3 text node. #syncCore()'s "stale-text-node" branch
|
|
235
|
+
// matched the whitespace + did textContent = this.label, wiping
|
|
236
|
+
// BOTH the whitespace AND the consumer's <span>. Filter here so
|
|
237
|
+
// #labelEl.firstChild is the actual element/non-whitespace text,
|
|
238
|
+
// and #syncCore's stale-text branch only fires for genuinely
|
|
239
|
+
// attr-driven cases.
|
|
229
240
|
const slotted = Array.from(this.childNodes).filter(n =>
|
|
230
241
|
!(n.nodeType === 1 && n.dataset && (
|
|
231
242
|
n.dataset.tile !== undefined ||
|
|
@@ -233,7 +244,7 @@ export class UISwatch extends UIElement {
|
|
|
233
244
|
n.dataset.detail !== undefined ||
|
|
234
245
|
n.dataset.badge !== undefined ||
|
|
235
246
|
n.dataset.copy !== undefined
|
|
236
|
-
))
|
|
247
|
+
)) && !(n.nodeType === 3 && !n.textContent.trim())
|
|
237
248
|
);
|
|
238
249
|
this.innerHTML = '';
|
|
239
250
|
|
|
@@ -282,9 +293,53 @@ export class UISwatch extends UIElement {
|
|
|
282
293
|
|
|
283
294
|
// ── Sync ──────────────────────────────────────────────────────────
|
|
284
295
|
|
|
296
|
+
/**
|
|
297
|
+
* §326 (v0.5.16, FB-37): Late-arriving `[slot="chrome"]` children may
|
|
298
|
+
* land on the host AFTER `#stamp()` runs — happens in any template
|
|
299
|
+
* engine (lit-html, AdiaUI `html\`\``, etc.) that batches child appends
|
|
300
|
+
* separately from element creation. `#stamp()` is gated by
|
|
301
|
+
* `this.#stamped` and won't re-run; this method picks up the late
|
|
302
|
+
* arrivals on every `render()` pass + moves them to the canonical
|
|
303
|
+
* sibling-of-tile position (between `#tileEl` and `#badgeEl`).
|
|
304
|
+
*
|
|
305
|
+
* Idempotent: chrome children already in the right slot are left
|
|
306
|
+
* alone (the `insertBefore` only moves them if they're not the
|
|
307
|
+
* #badgeEl's immediate previousSibling chain).
|
|
308
|
+
*/
|
|
309
|
+
#absorbChromeSlot() {
|
|
310
|
+
if (!this.#tileEl || !this.#badgeEl) return;
|
|
311
|
+
const chromeChildren = [];
|
|
312
|
+
for (const child of this.children) {
|
|
313
|
+
if (child === this.#tileEl) continue;
|
|
314
|
+
if (child === this.#badgeEl) break; // hit the badge — done scanning the head region
|
|
315
|
+
if (child.getAttribute?.('slot') === 'chrome') chromeChildren.push(child);
|
|
316
|
+
}
|
|
317
|
+
// Walk the rest of the children looking for chrome children that landed
|
|
318
|
+
// AFTER the internal structure (the common template-late-append case).
|
|
319
|
+
for (const child of Array.from(this.children)) {
|
|
320
|
+
if (
|
|
321
|
+
child !== this.#tileEl &&
|
|
322
|
+
child !== this.#badgeEl &&
|
|
323
|
+
child !== this.#labelEl &&
|
|
324
|
+
child !== this.#detailEl &&
|
|
325
|
+
child !== this.#copyEl &&
|
|
326
|
+
child.getAttribute?.('slot') === 'chrome'
|
|
327
|
+
) {
|
|
328
|
+
// Move to the canonical position: immediately after #tileEl.
|
|
329
|
+
// insertBefore is idempotent — moving an already-positioned child
|
|
330
|
+
// to the same spot is a no-op.
|
|
331
|
+
this.insertBefore(child, this.#badgeEl);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
285
336
|
#syncCore() {
|
|
286
337
|
if (!this.#tileEl || !this.#labelEl) return;
|
|
287
338
|
|
|
339
|
+
// §326 (v0.5.16, FB-37): pick up `[slot="chrome"]` children that
|
|
340
|
+
// landed after #stamp() ran (template-render path).
|
|
341
|
+
this.#absorbChromeSlot();
|
|
342
|
+
|
|
288
343
|
// Normalize enum values; fall back silently when a consumer sets a typo.
|
|
289
344
|
const shape = SHAPES.has(this.shape) ? this.shape : 'square';
|
|
290
345
|
const size = SIZES.has(this.size) ? this.size : 'md';
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
line-height: 1;
|
|
292
292
|
cursor: pointer;
|
|
293
293
|
border-radius: var(--a-radius-xs);
|
|
294
|
-
transition: color
|
|
294
|
+
transition: color var(--a-duration-fast) var(--a-easing-out), background var(--a-duration-fast) var(--a-easing-out);
|
|
295
295
|
}
|
|
296
296
|
:scope > [data-copy]:hover { color: var(--a-fg); background: var(--a-bg-muted); }
|
|
297
297
|
:scope > [data-copy]:focus-visible {
|
|
@@ -308,7 +308,7 @@
|
|
|
308
308
|
:scope[selectable] {
|
|
309
309
|
cursor: pointer;
|
|
310
310
|
border-radius: var(--a-radius-sm);
|
|
311
|
-
transition: box-shadow
|
|
311
|
+
transition: box-shadow var(--a-duration-fast) var(--a-easing-out);
|
|
312
312
|
}
|
|
313
313
|
:scope[selectable]:focus { outline: none; }
|
|
314
314
|
:scope[selectable]:focus-visible {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <swatch-ui> behavioral tests.
|
|
3
|
+
*
|
|
4
|
+
* Currently scoped to FB-36 / §325 regression coverage. Future tests for
|
|
5
|
+
* #stamp() lifecycle, chrome-slot composition, auto-contrast probe,
|
|
6
|
+
* etc. land here.
|
|
7
|
+
*
|
|
8
|
+
* Test setup note: happy-dom connects custom elements synchronously
|
|
9
|
+
* during innerHTML parsing — BEFORE sibling children inside the same
|
|
10
|
+
* element are appended. Real browsers connect after the full innerHTML
|
|
11
|
+
* is parsed. To exercise the FB-36 path (children present at connect
|
|
12
|
+
* time), tests use `document.createElement` + `appendChild` to build
|
|
13
|
+
* the full subtree FIRST, then attach to the document — guaranteeing
|
|
14
|
+
* the children are in `this.childNodes` when `connectedCallback()` fires.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
await import('./swatch.js');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function buildSwatch({ label, defaultSlot, shape = 'block', labelPosition = 'overlay', color = '#3b82f6' }) {
|
|
24
|
+
const swatch = document.createElement('swatch-ui');
|
|
25
|
+
swatch.setAttribute('shape', shape);
|
|
26
|
+
swatch.setAttribute('label-position', labelPosition);
|
|
27
|
+
swatch.setAttribute('color', color);
|
|
28
|
+
if (label !== undefined) swatch.setAttribute('label', label);
|
|
29
|
+
if (defaultSlot) {
|
|
30
|
+
// Mimic indented HTML's whitespace text nodes — the FB-36 trigger.
|
|
31
|
+
swatch.appendChild(document.createTextNode('\n '));
|
|
32
|
+
const child = document.createElement('span');
|
|
33
|
+
child.id = defaultSlot.id;
|
|
34
|
+
child.textContent = defaultSlot.text;
|
|
35
|
+
swatch.appendChild(child);
|
|
36
|
+
swatch.appendChild(document.createTextNode('\n '));
|
|
37
|
+
}
|
|
38
|
+
return swatch;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('<swatch-ui> default-slot + label-attr precedence (FB-36 / §325)', () => {
|
|
42
|
+
let host;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
host = document.createElement('div');
|
|
46
|
+
document.body.appendChild(host);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('preserves default-slot element when label="X" is also present (FB-36 §325 fix)', async () => {
|
|
50
|
+
const swatch = buildSwatch({
|
|
51
|
+
label: '500',
|
|
52
|
+
defaultSlot: { id: 'probe-default', text: 'richlabel' },
|
|
53
|
+
});
|
|
54
|
+
host.appendChild(swatch);
|
|
55
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
56
|
+
|
|
57
|
+
const label = swatch.querySelector('[data-label]');
|
|
58
|
+
const span = swatch.querySelector('#probe-default');
|
|
59
|
+
|
|
60
|
+
// Pre-§325 the span was wiped + label="500" took precedence.
|
|
61
|
+
// Post-§325 the default-slot wins (yaml spec: "richer content").
|
|
62
|
+
expect(span).not.toBeNull();
|
|
63
|
+
expect(label.textContent.trim()).toBe('richlabel');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('renders label-attr text when no default-slot is present', async () => {
|
|
67
|
+
const swatch = buildSwatch({ label: '500' });
|
|
68
|
+
host.appendChild(swatch);
|
|
69
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
70
|
+
const label = swatch.querySelector('[data-label]');
|
|
71
|
+
expect(label.textContent.trim()).toBe('500');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('renders default-slot text when no label-attr is present', async () => {
|
|
75
|
+
const swatch = buildSwatch({
|
|
76
|
+
defaultSlot: { id: 'probe-only', text: 'defaultonly' },
|
|
77
|
+
});
|
|
78
|
+
host.appendChild(swatch);
|
|
79
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
80
|
+
const label = swatch.querySelector('[data-label]');
|
|
81
|
+
const span = swatch.querySelector('#probe-only');
|
|
82
|
+
expect(span).not.toBeNull();
|
|
83
|
+
expect(label.textContent.trim()).toBe('defaultonly');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('syncs label-attr changes when there is no default-slot (no whitespace regression)', async () => {
|
|
87
|
+
const swatch = buildSwatch({ label: '500' });
|
|
88
|
+
host.appendChild(swatch);
|
|
89
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
90
|
+
swatch.label = '600';
|
|
91
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
92
|
+
const label = swatch.querySelector('[data-label]');
|
|
93
|
+
expect(label.textContent.trim()).toBe('600');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('<swatch-ui> late-arriving [slot="chrome"] children (FB-37 / §326)', () => {
|
|
98
|
+
let host;
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
host = document.createElement('div');
|
|
102
|
+
document.body.appendChild(host);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('absorbs chrome child appended AFTER connectedCallback (template-render path)', async () => {
|
|
106
|
+
// Mimic the template-engine timing: create the swatch + connect it FIRST
|
|
107
|
+
// (children empty), then append the chrome child + trigger a render.
|
|
108
|
+
const swatch = document.createElement('swatch-ui');
|
|
109
|
+
swatch.setAttribute('shape', 'block');
|
|
110
|
+
swatch.setAttribute('label-position', 'overlay');
|
|
111
|
+
swatch.setAttribute('color', '#3b82f6');
|
|
112
|
+
swatch.setAttribute('label', '500');
|
|
113
|
+
host.appendChild(swatch);
|
|
114
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
115
|
+
|
|
116
|
+
// At this point #stamp() has run + chromeSlot was empty.
|
|
117
|
+
// Append a chrome child LATER — this is what template engines do.
|
|
118
|
+
const chrome = document.createElement('span');
|
|
119
|
+
chrome.setAttribute('slot', 'chrome');
|
|
120
|
+
chrome.id = 'late-chrome';
|
|
121
|
+
chrome.textContent = '★';
|
|
122
|
+
swatch.appendChild(chrome);
|
|
123
|
+
|
|
124
|
+
// Trigger a re-render via property mutation (template engines typically
|
|
125
|
+
// set props after appending — this nudge fires render → #syncCore →
|
|
126
|
+
// #absorbChromeSlot picks up the late chrome child). Must use a value
|
|
127
|
+
// DIFFERENT from current to actually fire the signal write.
|
|
128
|
+
swatch.label = '999';
|
|
129
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
130
|
+
|
|
131
|
+
const found = swatch.querySelector('#late-chrome');
|
|
132
|
+
expect(found).not.toBeNull();
|
|
133
|
+
// Chrome child should be a direct host sibling positioned between tile + badge.
|
|
134
|
+
const tile = swatch.querySelector('[data-tile]');
|
|
135
|
+
const badge = swatch.querySelector('[data-badge]');
|
|
136
|
+
expect(found.previousElementSibling).toBe(tile);
|
|
137
|
+
expect(found.nextElementSibling).toBe(badge);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('still handles chrome child appended BEFORE connect (static-HTML path)', async () => {
|
|
141
|
+
// Build swatch with chrome child present at connect time — the canonical
|
|
142
|
+
// FB-34 / §314 path should still work post-§326.
|
|
143
|
+
const swatch = document.createElement('swatch-ui');
|
|
144
|
+
swatch.setAttribute('shape', 'block');
|
|
145
|
+
swatch.setAttribute('label-position', 'overlay');
|
|
146
|
+
swatch.setAttribute('color', '#3b82f6');
|
|
147
|
+
swatch.setAttribute('label', '500');
|
|
148
|
+
const chrome = document.createElement('span');
|
|
149
|
+
chrome.setAttribute('slot', 'chrome');
|
|
150
|
+
chrome.id = 'early-chrome';
|
|
151
|
+
chrome.textContent = '⚑';
|
|
152
|
+
swatch.appendChild(chrome);
|
|
153
|
+
host.appendChild(swatch);
|
|
154
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
155
|
+
|
|
156
|
+
const found = swatch.querySelector('#early-chrome');
|
|
157
|
+
expect(found).not.toBeNull();
|
|
158
|
+
const tile = swatch.querySelector('[data-tile]');
|
|
159
|
+
const badge = swatch.querySelector('[data-badge]');
|
|
160
|
+
expect(found.previousElementSibling).toBe(tile);
|
|
161
|
+
expect(found.nextElementSibling).toBe(badge);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"default": false
|
|
37
37
|
},
|
|
38
38
|
"variant": {
|
|
39
|
-
"description": "Typography variant — sets role tokens (size/weight/
|
|
39
|
+
"description": "Typography variant — sets design-role tokens (size / weight /\ntracking / color / leading) per the L0 typography token family.\n**PRESENTATIONAL-ONLY (§247, FB-23 §2).** `<text-ui variant=\"heading\">`\ndoes NOT set `role=\"heading\"` + `aria-level` on the host. Document-\noutline assistive technology will treat the element as `role=\"generic\"`.\nFor semantic headings, wrap with native `<h1>`-`<h6>` OR add\n`role=\"heading\" aria-level=\"N\"` on the host directly. For visual-only\nlabels (eyebrows, kickers, captions, deck), the presentational default\nis correct. The §221k chooser guide in USAGE.md documents picker heuristics.",
|
|
40
40
|
"type": "string",
|
|
41
41
|
"enum": [
|
|
42
42
|
"body",
|
|
@@ -52,7 +52,21 @@
|
|
|
52
52
|
"metric",
|
|
53
53
|
"code"
|
|
54
54
|
],
|
|
55
|
-
"default": "body"
|
|
55
|
+
"default": "body",
|
|
56
|
+
"enum_descriptions": {
|
|
57
|
+
"title": "Page title (visual rank H1). Largest under display. Use at the top of an authoritative page or dialog.",
|
|
58
|
+
"body": "Default body copy. 14px / regular. Paragraphs, multi-line content, running prose.",
|
|
59
|
+
"caption": "Annotation under a primary line — smaller + muted. Use for image captions, footnotes.",
|
|
60
|
+
"code": "Inline monospace code reference. Use for inline code within prose.",
|
|
61
|
+
"deck": "Sub-title under a `title`. One-line lead, slightly larger than body. Use for the lead sentence after a title.",
|
|
62
|
+
"display": "Top-level hero / brand display. Tallest visual rank. Use for page-level hero one-liners.",
|
|
63
|
+
"heading": "Major page heading (visual rank H2). 16-18px / bold. Use for major sub-section dividers.",
|
|
64
|
+
"kicker": "Eyebrow text above a `title`. UPPERCASE + small + tracking. Use for content eyebrows (NOT form labels — use `label` for those).",
|
|
65
|
+
"label": "Form-control label (above an `<input-ui>` / `<select-ui>` etc). UI-sized + medium-weight. Use for field labels bound to form controls.",
|
|
66
|
+
"metric": "Numeric KPI / big-number stat. Bold + large. Use for dashboard metric numbers.",
|
|
67
|
+
"section": "Inline form-group / navlist heading (visual rank H4). Small-cap. Use for form group labels, nav list headings.",
|
|
68
|
+
"subsection": "Sub-landmark within a section (visual rank H3). 14px / semibold. Use for card titles within a section."
|
|
69
|
+
}
|
|
56
70
|
}
|
|
57
71
|
},
|
|
58
72
|
"required": [
|
|
@@ -1,41 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `<text-ui>` — Typography wrapper that applies role tokens. Supports
|
|
3
|
-
* truncation and line clamping.
|
|
2
|
+
* `<text-ui>` — Typography wrapper that applies role tokens. Supports truncation and line clamping.
|
|
4
3
|
*
|
|
5
4
|
* @see https://ui-kit.exe.xyz/site/components/text
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Also carries the FB-23 §2 ARIA-role disclaimer on the property doc.
|
|
13
|
-
* `dts-codegen.mjs` keeps this file in its `HAND_AUTHORED_DTS` skip list.
|
|
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.
|
|
14
11
|
*/
|
|
15
12
|
|
|
16
13
|
import { UIElement } from '../../core/element.js';
|
|
17
14
|
|
|
18
|
-
/**
|
|
19
|
-
* Typography variants — visual rank from `display` (largest, hero) to
|
|
20
|
-
* `code` (inline monospace). Pick by intent, not by visual size — every
|
|
21
|
-
* variant has a distinct typographic role per §221k chooser guide.
|
|
22
|
-
*
|
|
23
|
-
* See `packages/web-components/USAGE.md` "v0.5.9 — Consumer-feedback
|
|
24
|
-
* discoverability sweep" §221k for the picker heuristics.
|
|
25
|
-
*/
|
|
26
15
|
export type UITextVariant =
|
|
27
16
|
/** Default body copy. 14px / regular. Paragraphs, multi-line content, running prose. */
|
|
28
17
|
| 'body'
|
|
29
|
-
/** Top-level hero / brand display. Tallest visual rank. Use for page-level hero one-liners. */
|
|
30
|
-
| 'display'
|
|
31
|
-
/** Page title (visual rank H1). Largest under display. Use at the top of an authoritative page or dialog. */
|
|
32
|
-
| 'title'
|
|
33
18
|
/** Major page heading (visual rank H2). 16-18px / bold. Use for major sub-section dividers. */
|
|
34
19
|
| 'heading'
|
|
20
|
+
/** Page title (visual rank H1). Largest under display. Use at the top of an authoritative page or dialog. */
|
|
21
|
+
| 'title'
|
|
35
22
|
/** Sub-landmark within a section (visual rank H3). 14px / semibold. Use for card titles within a section. */
|
|
36
23
|
| 'subsection'
|
|
37
|
-
/**
|
|
38
|
-
| '
|
|
24
|
+
/** Top-level hero / brand display. Tallest visual rank. Use for page-level hero one-liners. */
|
|
25
|
+
| 'display'
|
|
39
26
|
/** Annotation under a primary line — smaller + muted. Use for image captions, footnotes. */
|
|
40
27
|
| 'caption'
|
|
41
28
|
/** Form-control label (above an `<input-ui>` / `<select-ui>` etc). UI-sized + medium-weight. Use for field labels bound to form controls. */
|
|
@@ -44,34 +31,30 @@ export type UITextVariant =
|
|
|
44
31
|
| 'kicker'
|
|
45
32
|
/** Sub-title under a `title`. One-line lead, slightly larger than body. Use for the lead sentence after a title. */
|
|
46
33
|
| 'deck'
|
|
34
|
+
/** Inline form-group / navlist heading (visual rank H4). Small-cap. Use for form group labels, nav list headings. */
|
|
35
|
+
| 'section'
|
|
47
36
|
/** Numeric KPI / big-number stat. Bold + large. Use for dashboard metric numbers. */
|
|
48
37
|
| 'metric'
|
|
49
|
-
/** Inline monospace code reference. Use for
|
|
38
|
+
/** Inline monospace code reference. Use for inline code within prose. */
|
|
50
39
|
| 'code';
|
|
51
40
|
|
|
52
41
|
export class UIText extends UIElement {
|
|
53
|
-
/**
|
|
54
|
-
* Typography variant — sets design-role tokens (size / weight / tracking /
|
|
55
|
-
* color / leading) per the L0 typography token family.
|
|
56
|
-
*
|
|
57
|
-
* **PRESENTATIONAL-ONLY (§247, FB-23 §2).** `<text-ui variant="heading">`
|
|
58
|
-
* does NOT set `role="heading"` + `aria-level` on the host. Document-outline
|
|
59
|
-
* assistive technology will treat the element as `role="generic"`. For
|
|
60
|
-
* **semantic** headings (screen-reader heading-navigation, document
|
|
61
|
-
* outline), wrap with native `<h1>`-`<h6>` OR add
|
|
62
|
-
* `role="heading" aria-level="N"` on the host element directly.
|
|
63
|
-
*
|
|
64
|
-
* For visual-only labels (eyebrows, kickers, captions, deck), the
|
|
65
|
-
* presentational default is correct. The §221k chooser guide in USAGE.md
|
|
66
|
-
* documents the picker heuristics.
|
|
67
|
-
*/
|
|
68
|
-
variant: UITextVariant;
|
|
69
|
-
/** When true, applies stronger emphasis (heavier weight + accent color). Styled via `:scope[strong]` in text.css. Use instead of `variant="heading"` when you want a single emphasized word inline in body copy. */
|
|
70
|
-
strong: boolean;
|
|
71
|
-
/** Single-line truncation with ellipsis. Ignored when `lines` is set. */
|
|
72
|
-
truncate: boolean;
|
|
73
|
-
/** Multi-line clamp count (0 = no clamp). Sets `--_text-lines` for `-webkit-line-clamp`. */
|
|
42
|
+
/** Multi-line clamp count (0 = no clamp) */
|
|
74
43
|
lines: number;
|
|
44
|
+
/** When true, applies stronger emphasis (heavier weight + accent color). Styled via :scope[strong] in text.css. Use instead of variant=heading when you want a single emphasized word inline in body copy. */
|
|
45
|
+
strong: boolean;
|
|
75
46
|
/** Display text content. The main payload field for Text components extracted from HTML. */
|
|
76
47
|
textContent: string;
|
|
48
|
+
/** Single-line truncation with ellipsis. Ignored when `lines` is set. */
|
|
49
|
+
truncate: boolean;
|
|
50
|
+
/** Typography variant — sets design-role tokens (size / weight /
|
|
51
|
+
tracking / color / leading) per the L0 typography token family.
|
|
52
|
+
**PRESENTATIONAL-ONLY (§247, FB-23 §2).** `<text-ui variant="heading">`
|
|
53
|
+
does NOT set `role="heading"` + `aria-level` on the host. Document-
|
|
54
|
+
outline assistive technology will treat the element as `role="generic"`.
|
|
55
|
+
For semantic headings, wrap with native `<h1>`-`<h6>` OR add
|
|
56
|
+
`role="heading" aria-level="N"` on the host directly. For visual-only
|
|
57
|
+
labels (eyebrows, kickers, captions, deck), the presentational default
|
|
58
|
+
is correct. The §221k chooser guide in USAGE.md documents picker heuristics. */
|
|
59
|
+
variant: UITextVariant;
|
|
77
60
|
}
|
|
@@ -28,7 +28,16 @@ props:
|
|
|
28
28
|
default: ""
|
|
29
29
|
dynamic: true
|
|
30
30
|
variant:
|
|
31
|
-
description:
|
|
31
|
+
description: |-
|
|
32
|
+
Typography variant — sets design-role tokens (size / weight /
|
|
33
|
+
tracking / color / leading) per the L0 typography token family.
|
|
34
|
+
**PRESENTATIONAL-ONLY (§247, FB-23 §2).** `<text-ui variant="heading">`
|
|
35
|
+
does NOT set `role="heading"` + `aria-level` on the host. Document-
|
|
36
|
+
outline assistive technology will treat the element as `role="generic"`.
|
|
37
|
+
For semantic headings, wrap with native `<h1>`-`<h6>` OR add
|
|
38
|
+
`role="heading" aria-level="N"` on the host directly. For visual-only
|
|
39
|
+
labels (eyebrows, kickers, captions, deck), the presentational default
|
|
40
|
+
is correct. The §221k chooser guide in USAGE.md documents picker heuristics.
|
|
32
41
|
type: string
|
|
33
42
|
default: body
|
|
34
43
|
enum:
|
|
@@ -44,6 +53,21 @@ props:
|
|
|
44
53
|
- section
|
|
45
54
|
- metric
|
|
46
55
|
- code
|
|
56
|
+
# §324 (v0.5.15, FB-23 §3 retire): per-variant JSDoc surfaces at IDE
|
|
57
|
+
# hover via codegen. Retires the HAND_AUTHORED_DTS skip for text.d.ts.
|
|
58
|
+
enum_descriptions:
|
|
59
|
+
body: Default body copy. 14px / regular. Paragraphs, multi-line content, running prose.
|
|
60
|
+
display: Top-level hero / brand display. Tallest visual rank. Use for page-level hero one-liners.
|
|
61
|
+
title: Page title (visual rank H1). Largest under display. Use at the top of an authoritative page or dialog.
|
|
62
|
+
heading: Major page heading (visual rank H2). 16-18px / bold. Use for major sub-section dividers.
|
|
63
|
+
subsection: Sub-landmark within a section (visual rank H3). 14px / semibold. Use for card titles within a section.
|
|
64
|
+
section: Inline form-group / navlist heading (visual rank H4). Small-cap. Use for form group labels, nav list headings.
|
|
65
|
+
caption: Annotation under a primary line — smaller + muted. Use for image captions, footnotes.
|
|
66
|
+
label: Form-control label (above an `<input-ui>` / `<select-ui>` etc). UI-sized + medium-weight. Use for field labels bound to form controls.
|
|
67
|
+
kicker: Eyebrow text above a `title`. UPPERCASE + small + tracking. Use for content eyebrows (NOT form labels — use `label` for those).
|
|
68
|
+
deck: Sub-title under a `title`. One-line lead, slightly larger than body. Use for the lead sentence after a title.
|
|
69
|
+
metric: Numeric KPI / big-number stat. Bold + large. Use for dashboard metric numbers.
|
|
70
|
+
code: Inline monospace code reference. Use for inline code within prose.
|
|
47
71
|
events: {}
|
|
48
72
|
slots: {}
|
|
49
73
|
states:
|
|
@@ -104,7 +104,7 @@ toolbar-ui [data-toolbar-spillover-menu]:popover-open {
|
|
|
104
104
|
min-width: 10rem;
|
|
105
105
|
font-family: inherit;
|
|
106
106
|
font-size: var(--a-ui-size);
|
|
107
|
-
color: var(--
|
|
107
|
+
color: var(--toolbar-fg);
|
|
108
108
|
/* Stack overflow items as rows (each row can be a group with its own gap). */
|
|
109
109
|
display: flex;
|
|
110
110
|
flex-direction: column;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-components",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -116,20 +116,21 @@
|
|
|
116
116
|
--a-fg-muted: var(--a-canvas-text-muted);
|
|
117
117
|
--a-fg-strong: var(--a-canvas-text-strong);
|
|
118
118
|
--a-fg-hover: var(--a-canvas-text-strong);
|
|
119
|
-
--a-fg-active
|
|
119
|
+
/* ADR-0028: --a-fg-{active,invalid} retired (zero consumers). Use
|
|
120
|
+
`--a-ui-text-active` / `--a-ui-text-invalid` for form-control state,
|
|
121
|
+
or define a per-primitive `--<comp>-fg-{active,invalid}` token. */
|
|
120
122
|
--a-fg-selected: var(--a-canvas-text-strong);
|
|
121
123
|
--a-fg-disabled: var(--a-canvas-text-disabled);
|
|
122
|
-
--a-fg-invalid: var(--a-danger-text);
|
|
123
124
|
--a-fg-inverse: var(--a-canvas-text-inverse);
|
|
124
125
|
|
|
125
126
|
--a-border: var(--a-canvas-border);
|
|
126
127
|
--a-border-subtle: var(--a-canvas-border-subtle);
|
|
127
128
|
--a-border-strong: var(--a-canvas-border-strong);
|
|
128
129
|
--a-border-hover: var(--a-canvas-border-strong);
|
|
129
|
-
--a-border-active
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
/* ADR-0028: --a-border-{active,selected,disabled,invalid} retired (zero
|
|
131
|
+
consumers). Per-primitive `--<comp>-border-{state}` is the override
|
|
132
|
+
surface; `--a-focus-ring` carries focus-active borders for form
|
|
133
|
+
controls. */
|
|
133
134
|
|
|
134
135
|
/* ══════════════════════════════════════════════════════════════
|
|
135
136
|
ACCENT — Interactive, links, focus (source for --a-primary-*)
|
|
@@ -172,23 +173,12 @@
|
|
|
172
173
|
--a-primary-bg: var(--a-accent-strong);
|
|
173
174
|
--a-primary-bg-hover: var(--a-accent-hover);
|
|
174
175
|
--a-primary-bg-active: var(--a-accent-active);
|
|
175
|
-
--a-primary-bg-selected: var(--a-accent-selected);
|
|
176
|
-
--a-primary-bg-disabled: var(--a-accent-muted);
|
|
177
|
-
--a-primary-bg-invalid: var(--a-danger-muted);
|
|
178
176
|
|
|
179
177
|
--a-primary-fg: var(--a-accent-05-tint);
|
|
180
178
|
--a-primary-fg-hover: var(--a-accent-text-strong);
|
|
181
|
-
--a-primary-fg-active: var(--a-accent-text-strong);
|
|
182
|
-
--a-primary-fg-selected: var(--a-accent-text-strong);
|
|
183
|
-
--a-primary-fg-disabled: var(--a-accent-text-disabled);
|
|
184
|
-
--a-primary-fg-invalid: var(--a-danger-text);
|
|
185
179
|
|
|
186
180
|
--a-primary-border: var(--a-accent-border);
|
|
187
181
|
--a-primary-border-hover: var(--a-accent-border-strong);
|
|
188
|
-
--a-primary-border-active: var(--a-accent-border-strong);
|
|
189
|
-
--a-primary-border-selected: var(--a-accent-border-strong);
|
|
190
|
-
--a-primary-border-disabled: var(--a-accent-border-subtle);
|
|
191
|
-
--a-primary-border-invalid: var(--a-danger-border);
|
|
192
182
|
|
|
193
183
|
/* Legacy shorthand — keep until all components migrate to --a-primary-* */
|
|
194
184
|
--a-primary: var(--a-primary-bg);
|
|
@@ -200,16 +190,9 @@
|
|
|
200
190
|
--a-accent-bg: var(--a-primary-bg);
|
|
201
191
|
--a-accent-bg-hover: var(--a-primary-bg-hover);
|
|
202
192
|
--a-accent-bg-active: var(--a-primary-bg-active);
|
|
203
|
-
--a-accent-bg-selected: var(--a-primary-bg-selected);
|
|
204
|
-
--a-accent-bg-disabled: var(--a-primary-bg-disabled);
|
|
205
|
-
--a-accent-bg-invalid: var(--a-primary-bg-invalid);
|
|
206
193
|
|
|
207
194
|
--a-accent-fg: var(--a-primary-fg);
|
|
208
195
|
--a-accent-fg-hover: var(--a-primary-fg-hover);
|
|
209
|
-
--a-accent-fg-active: var(--a-primary-fg-active);
|
|
210
|
-
--a-accent-fg-selected: var(--a-primary-fg-selected);
|
|
211
|
-
--a-accent-fg-disabled: var(--a-primary-fg-disabled);
|
|
212
|
-
--a-accent-fg-invalid: var(--a-primary-fg-invalid);
|
|
213
196
|
|
|
214
197
|
/* accent border is already defined at L2 (--a-accent-border*);
|
|
215
198
|
L3 matches L2 since both resolve to the same scheme-aware value. */
|
|
@@ -239,24 +222,12 @@
|
|
|
239
222
|
/* L3 — Brand surface role matrix */
|
|
240
223
|
--a-brand-bg: var(--a-brand-strong);
|
|
241
224
|
--a-brand-bg-hover: var(--a-brand-hover);
|
|
242
|
-
--a-brand-bg-active: var(--a-brand-active);
|
|
243
|
-
--a-brand-bg-selected: var(--a-brand-selected);
|
|
244
|
-
--a-brand-bg-disabled: var(--a-brand-muted);
|
|
245
|
-
--a-brand-bg-invalid: var(--a-danger-muted);
|
|
246
225
|
|
|
247
226
|
--a-brand-fg: var(--a-brand-text-strong);
|
|
248
227
|
--a-brand-fg-hover: var(--a-brand-text-strong);
|
|
249
|
-
--a-brand-fg-active: var(--a-brand-text-strong);
|
|
250
|
-
--a-brand-fg-selected: var(--a-brand-text-strong);
|
|
251
|
-
--a-brand-fg-disabled: var(--a-brand-text-disabled);
|
|
252
|
-
--a-brand-fg-invalid: var(--a-danger-text);
|
|
253
228
|
|
|
254
229
|
/* L3 border state matrix — rest state is --a-brand-border (L2 above). */
|
|
255
230
|
--a-brand-border-hover: var(--a-brand-border-strong);
|
|
256
|
-
--a-brand-border-active: var(--a-brand-border-strong);
|
|
257
|
-
--a-brand-border-selected: var(--a-brand-border-strong);
|
|
258
|
-
--a-brand-border-disabled: var(--a-brand-border-subtle);
|
|
259
|
-
--a-brand-border-invalid: var(--a-danger-border);
|
|
260
231
|
|
|
261
232
|
/* ══════════════════════════════════════════════════════════════
|
|
262
233
|
INFO
|
|
@@ -283,24 +254,12 @@
|
|
|
283
254
|
/* L3 — Info surface role matrix */
|
|
284
255
|
--a-info-bg: var(--a-info-strong);
|
|
285
256
|
--a-info-bg-hover: var(--a-info-hover);
|
|
286
|
-
--a-info-bg-active: var(--a-info-active);
|
|
287
|
-
--a-info-bg-selected: var(--a-info-selected);
|
|
288
|
-
--a-info-bg-disabled: var(--a-info-muted);
|
|
289
|
-
--a-info-bg-invalid: var(--a-danger-muted);
|
|
290
257
|
|
|
291
258
|
--a-info-fg: var(--a-info-text-strong);
|
|
292
259
|
--a-info-fg-hover: var(--a-info-text-strong);
|
|
293
|
-
--a-info-fg-active: var(--a-info-text-strong);
|
|
294
|
-
--a-info-fg-selected: var(--a-info-text-strong);
|
|
295
|
-
--a-info-fg-disabled: var(--a-info-text-disabled);
|
|
296
|
-
--a-info-fg-invalid: var(--a-danger-text);
|
|
297
260
|
|
|
298
261
|
/* L3 border state matrix — rest state is --a-info-border (L2 above). */
|
|
299
262
|
--a-info-border-hover: var(--a-info-border-strong);
|
|
300
|
-
--a-info-border-active: var(--a-info-border-strong);
|
|
301
|
-
--a-info-border-selected: var(--a-info-border-strong);
|
|
302
|
-
--a-info-border-disabled: var(--a-info-border-subtle);
|
|
303
|
-
--a-info-border-invalid: var(--a-danger-border);
|
|
304
263
|
|
|
305
264
|
/* ══════════════════════════════════════════════════════════════
|
|
306
265
|
SUCCESS
|
|
@@ -327,24 +286,12 @@
|
|
|
327
286
|
/* L3 — Success surface role matrix */
|
|
328
287
|
--a-success-bg: var(--a-success-strong);
|
|
329
288
|
--a-success-bg-hover: var(--a-success-hover);
|
|
330
|
-
--a-success-bg-active: var(--a-success-active);
|
|
331
|
-
--a-success-bg-selected: var(--a-success-selected);
|
|
332
|
-
--a-success-bg-disabled: var(--a-success-muted);
|
|
333
|
-
--a-success-bg-invalid: var(--a-danger-muted);
|
|
334
289
|
|
|
335
290
|
--a-success-fg: var(--a-success-text-strong);
|
|
336
291
|
--a-success-fg-hover: var(--a-success-text-strong);
|
|
337
|
-
--a-success-fg-active: var(--a-success-text-strong);
|
|
338
|
-
--a-success-fg-selected: var(--a-success-text-strong);
|
|
339
|
-
--a-success-fg-disabled: var(--a-success-text-disabled);
|
|
340
|
-
--a-success-fg-invalid: var(--a-danger-text);
|
|
341
292
|
|
|
342
293
|
/* L3 border state matrix — rest state is --a-success-border (L2 above). */
|
|
343
294
|
--a-success-border-hover: var(--a-success-border-strong);
|
|
344
|
-
--a-success-border-active: var(--a-success-border-strong);
|
|
345
|
-
--a-success-border-selected: var(--a-success-border-strong);
|
|
346
|
-
--a-success-border-disabled: var(--a-success-border-subtle);
|
|
347
|
-
--a-success-border-invalid: var(--a-danger-border);
|
|
348
295
|
|
|
349
296
|
/* ══════════════════════════════════════════════════════════════
|
|
350
297
|
WARNING
|
|
@@ -372,24 +319,12 @@
|
|
|
372
319
|
/* L3 — Warning surface role matrix */
|
|
373
320
|
--a-warning-bg: var(--a-warning-strong);
|
|
374
321
|
--a-warning-bg-hover: var(--a-warning-hover);
|
|
375
|
-
--a-warning-bg-active: var(--a-warning-active);
|
|
376
|
-
--a-warning-bg-selected: var(--a-warning-selected);
|
|
377
|
-
--a-warning-bg-disabled: var(--a-warning-muted);
|
|
378
|
-
--a-warning-bg-invalid: var(--a-danger-muted);
|
|
379
322
|
|
|
380
323
|
--a-warning-fg: var(--a-warning-text-strong);
|
|
381
324
|
--a-warning-fg-hover: var(--a-warning-text-strong);
|
|
382
|
-
--a-warning-fg-active: var(--a-warning-text-strong);
|
|
383
|
-
--a-warning-fg-selected: var(--a-warning-text-strong);
|
|
384
|
-
--a-warning-fg-disabled: var(--a-warning-text-disabled);
|
|
385
|
-
--a-warning-fg-invalid: var(--a-danger-text);
|
|
386
325
|
|
|
387
326
|
/* L3 border state matrix — rest state is --a-warning-border (L2 above). */
|
|
388
327
|
--a-warning-border-hover: var(--a-warning-border-strong);
|
|
389
|
-
--a-warning-border-active: var(--a-warning-border-strong);
|
|
390
|
-
--a-warning-border-selected: var(--a-warning-border-strong);
|
|
391
|
-
--a-warning-border-disabled: var(--a-warning-border-subtle);
|
|
392
|
-
--a-warning-border-invalid: var(--a-danger-border);
|
|
393
328
|
|
|
394
329
|
/* ══════════════════════════════════════════════════════════════
|
|
395
330
|
DANGER
|
|
@@ -418,24 +353,12 @@
|
|
|
418
353
|
/* L3 — Danger surface role matrix */
|
|
419
354
|
--a-danger-bg: var(--a-danger-strong);
|
|
420
355
|
--a-danger-bg-hover: var(--a-danger-hover);
|
|
421
|
-
--a-danger-bg-active: var(--a-danger-active);
|
|
422
|
-
--a-danger-bg-selected: var(--a-danger-selected);
|
|
423
|
-
--a-danger-bg-disabled: var(--a-danger-muted);
|
|
424
|
-
--a-danger-bg-invalid: var(--a-danger-muted);
|
|
425
356
|
|
|
426
357
|
--a-danger-fg: var(--a-danger-text-strong);
|
|
427
358
|
--a-danger-fg-hover: var(--a-danger-text-strong);
|
|
428
|
-
--a-danger-fg-active: var(--a-danger-text-strong);
|
|
429
|
-
--a-danger-fg-selected: var(--a-danger-text-strong);
|
|
430
|
-
--a-danger-fg-disabled: var(--a-danger-text-disabled);
|
|
431
|
-
--a-danger-fg-invalid: var(--a-danger-text);
|
|
432
359
|
|
|
433
360
|
/* L3 border state matrix — rest state is --a-danger-border (L2 above). */
|
|
434
361
|
--a-danger-border-hover: var(--a-danger-border-strong);
|
|
435
|
-
--a-danger-border-active: var(--a-danger-border-strong);
|
|
436
|
-
--a-danger-border-selected: var(--a-danger-border-strong);
|
|
437
|
-
--a-danger-border-disabled: var(--a-danger-border-subtle);
|
|
438
|
-
--a-danger-border-invalid: var(--a-danger-border);
|
|
439
362
|
|
|
440
363
|
/* ══════════════════════════════════════════════════════════════
|
|
441
364
|
BUCKETS — sequential 5-step ramp per family for data-vis consumers
|