@adia-ai/web-components 0.0.21 → 0.0.23
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/README.md +1 -1
- package/components/action-list/action-list.css +13 -4
- package/components/agent-reasoning/agent-reasoning.js +6 -3
- package/components/alert/alert.css +19 -15
- package/components/alert/alert.js +8 -13
- package/components/avatar/avatar.css +8 -9
- package/components/button/button.css +27 -24
- package/components/card/card.css +1 -0
- package/components/check/check.css +15 -10
- package/components/col/col.css +3 -0
- package/components/description-list/description-list.css +9 -9
- package/components/grid/grid.css +6 -1
- package/components/input/input.css +15 -4
- package/components/list/list.css +8 -0
- package/components/menu/menu.css +22 -11
- package/components/modal/modal.js +40 -4
- package/components/option-card/option-card.css +9 -5
- package/components/progress/progress.css +8 -0
- package/components/progress-row/progress-row.js +9 -0
- package/components/radio/radio.css +13 -9
- package/components/rating/rating.css +7 -4
- package/components/richtext/richtext.css +138 -0
- package/components/row/row.css +7 -1
- package/components/segment/segment.css +8 -5
- package/components/select/select.css +4 -1
- package/components/stack/stack.css +3 -0
- package/components/stepper/stepper.css +12 -4
- package/components/stepper/stepper.js +7 -7
- package/components/swiper/swiper.css +10 -0
- package/components/swiper/swiper.js +77 -0
- package/components/switch/switch.css +15 -9
- package/components/tag/tag.css +7 -4
- package/components/toggle-group/toggle-group.css +9 -4
- package/package.json +1 -1
- package/patterns/app-nav/app-nav.js +13 -0
- package/patterns/app-nav-item/app-nav-item.css +15 -9
- package/patterns/section-nav-item/section-nav-item.css +13 -2
- package/styles/colors/semantics.css +5 -5
- package/styles/prose.css +5 -5
- package/styles/tokens.css +57 -0
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope*:hover [descendant]` inside `@scope` doesn't
|
|
2
|
+
match the scope root. The entire selector (including the descendant)
|
|
3
|
+
moves out together. See docs/BROWSER-COMPAT.md §3a. */
|
|
4
|
+
radio-ui:not([disabled]):hover [slot="dot"] {
|
|
5
|
+
border-color: var(--radio-border-hover);
|
|
6
|
+
background: var(--radio-bg-hover);
|
|
7
|
+
}
|
|
8
|
+
radio-ui[checked]:not([disabled]):hover [slot="dot"] {
|
|
9
|
+
background: var(--radio-bg-checked-hover);
|
|
10
|
+
border-color: var(--radio-bg-checked-hover);
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
@scope (radio-ui) {
|
|
2
14
|
:where(:scope) {
|
|
3
15
|
/* ── Layout ── (size scales with universal [size] attribute via --a-toggle-size) */
|
|
@@ -77,21 +89,13 @@
|
|
|
77
89
|
height: calc(var(--radio-size) * 0.6);
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
border-color: var(--radio-border-hover);
|
|
82
|
-
background: var(--radio-bg-hover);
|
|
83
|
-
}
|
|
92
|
+
/* hover rules moved outside @scope — see Safari 17.x bug note at top. */
|
|
84
93
|
|
|
85
94
|
:scope[checked] [slot="dot"] {
|
|
86
95
|
background: var(--radio-bg-checked);
|
|
87
96
|
border-color: var(--radio-border-checked);
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
:scope[checked]:not([disabled]):hover [slot="dot"] {
|
|
91
|
-
background: var(--radio-bg-checked-hover);
|
|
92
|
-
border-color: var(--radio-bg-checked-hover);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
99
|
/* Label */
|
|
96
100
|
:scope[label]::after { content: attr(label); }
|
|
97
101
|
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope:hover` inside `@scope` doesn't match the scope
|
|
2
|
+
root. Plain selector outside the @scope works. See
|
|
3
|
+
docs/BROWSER-COMPAT.md §3a. */
|
|
4
|
+
rating-ui:hover [data-rating-symbol] {
|
|
5
|
+
color: var(--rating-fg-hover);
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
@scope (rating-ui) {
|
|
2
9
|
:where(:scope) {
|
|
3
10
|
/* ── Layout ── */
|
|
@@ -72,10 +79,6 @@
|
|
|
72
79
|
clip-path: inset(0 0 0 0);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
:scope:hover [data-rating-symbol] {
|
|
76
|
-
color: var(--rating-fg-hover);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
82
|
/* ── Variants override TOKENS only ── */
|
|
80
83
|
:scope[variant="accent"] {
|
|
81
84
|
--rating-fg-filled: var(--a-accent-bg);
|
|
@@ -128,6 +128,29 @@
|
|
|
128
128
|
margin: var(--richtext-h4-margin-top) 0 var(--richtext-h4-margin-bottom);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
[data-richtext-body] h5 {
|
|
132
|
+
font-size: var(--richtext-body-size);
|
|
133
|
+
font-weight: var(--richtext-h4-weight);
|
|
134
|
+
color: var(--richtext-fg-muted);
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
letter-spacing: 0.04em;
|
|
137
|
+
margin: var(--richtext-h4-margin-top) 0 var(--richtext-h4-margin-bottom);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
[data-richtext-body] h6 {
|
|
141
|
+
font-size: var(--richtext-code-size);
|
|
142
|
+
font-weight: var(--richtext-h4-weight);
|
|
143
|
+
color: var(--richtext-fg-muted);
|
|
144
|
+
text-transform: uppercase;
|
|
145
|
+
letter-spacing: 0.06em;
|
|
146
|
+
margin: var(--richtext-h4-margin-top) 0 var(--richtext-h4-margin-bottom);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* First-element rhythm reset — first heading shouldn't push content
|
|
150
|
+
down with its top margin (already controlled at the body inset). */
|
|
151
|
+
[data-richtext-body] > :first-child { margin-top: 0; }
|
|
152
|
+
[data-richtext-body] > :last-child { margin-bottom: 0; }
|
|
153
|
+
|
|
131
154
|
/* ── Body text ── */
|
|
132
155
|
|
|
133
156
|
[data-richtext-body] p {
|
|
@@ -180,6 +203,112 @@
|
|
|
180
203
|
margin-bottom: var(--richtext-li-margin-bottom);
|
|
181
204
|
}
|
|
182
205
|
|
|
206
|
+
/* Nested lists — collapse the vertical block margin so children
|
|
207
|
+
don't add a full paragraph gap before/after the parent <li>. */
|
|
208
|
+
[data-richtext-body] li > ul,
|
|
209
|
+
[data-richtext-body] li > ol {
|
|
210
|
+
margin-block: var(--a-space-1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* ── Definition lists ── */
|
|
214
|
+
[data-richtext-body] dl {
|
|
215
|
+
margin: var(--richtext-list-my) 0;
|
|
216
|
+
}
|
|
217
|
+
[data-richtext-body] dt {
|
|
218
|
+
font-weight: var(--richtext-strong-weight);
|
|
219
|
+
color: var(--richtext-fg);
|
|
220
|
+
margin-top: var(--a-space-2);
|
|
221
|
+
}
|
|
222
|
+
[data-richtext-body] dd {
|
|
223
|
+
margin: 0 0 var(--richtext-li-margin-bottom) var(--richtext-list-indent);
|
|
224
|
+
color: var(--richtext-fg-muted);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* ── Blockquote ── */
|
|
228
|
+
[data-richtext-body] blockquote {
|
|
229
|
+
margin: var(--richtext-block-my) 0;
|
|
230
|
+
padding: var(--a-space-1) var(--a-space-4);
|
|
231
|
+
border-inline-start: 3px solid var(--richtext-border);
|
|
232
|
+
color: var(--richtext-fg-muted);
|
|
233
|
+
font-style: italic;
|
|
234
|
+
}
|
|
235
|
+
[data-richtext-body] blockquote > :last-child { margin-bottom: 0; }
|
|
236
|
+
|
|
237
|
+
/* ── Inline emphasis & semantic glyphs ── */
|
|
238
|
+
[data-richtext-body] mark {
|
|
239
|
+
background: var(--a-warning-muted, var(--richtext-code-bg));
|
|
240
|
+
color: inherit;
|
|
241
|
+
padding: 0 0.2em;
|
|
242
|
+
border-radius: var(--richtext-code-radius);
|
|
243
|
+
}
|
|
244
|
+
[data-richtext-body] del,
|
|
245
|
+
[data-richtext-body] s {
|
|
246
|
+
color: var(--richtext-fg-muted);
|
|
247
|
+
text-decoration: line-through;
|
|
248
|
+
}
|
|
249
|
+
[data-richtext-body] kbd {
|
|
250
|
+
font-family: var(--richtext-font-code);
|
|
251
|
+
font-size: 0.85em;
|
|
252
|
+
background: var(--richtext-code-bg);
|
|
253
|
+
border: 1px solid var(--richtext-border);
|
|
254
|
+
border-bottom-width: 2px;
|
|
255
|
+
border-radius: var(--richtext-code-radius);
|
|
256
|
+
padding: 0.05em 0.4em;
|
|
257
|
+
box-shadow: inset 0 -1px 0 var(--richtext-border);
|
|
258
|
+
white-space: nowrap;
|
|
259
|
+
}
|
|
260
|
+
[data-richtext-body] sup,
|
|
261
|
+
[data-richtext-body] sub {
|
|
262
|
+
font-size: 0.75em;
|
|
263
|
+
line-height: 0;
|
|
264
|
+
}
|
|
265
|
+
[data-richtext-body] abbr[title] {
|
|
266
|
+
text-decoration: underline dotted;
|
|
267
|
+
text-underline-offset: 2px;
|
|
268
|
+
cursor: help;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* ── Media ── */
|
|
272
|
+
[data-richtext-body] img,
|
|
273
|
+
[data-richtext-body] video {
|
|
274
|
+
max-width: 100%;
|
|
275
|
+
height: auto;
|
|
276
|
+
display: block;
|
|
277
|
+
margin: var(--richtext-block-my) auto;
|
|
278
|
+
border-radius: var(--richtext-pre-radius);
|
|
279
|
+
}
|
|
280
|
+
[data-richtext-body] figure {
|
|
281
|
+
margin: var(--richtext-block-my) 0;
|
|
282
|
+
}
|
|
283
|
+
[data-richtext-body] figcaption {
|
|
284
|
+
font-size: var(--richtext-code-size);
|
|
285
|
+
color: var(--richtext-fg-muted);
|
|
286
|
+
text-align: center;
|
|
287
|
+
margin-top: var(--a-space-1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* ── Disclosure ── */
|
|
291
|
+
[data-richtext-body] details {
|
|
292
|
+
margin: var(--richtext-block-my) 0;
|
|
293
|
+
border: 1px solid var(--richtext-border);
|
|
294
|
+
border-radius: var(--richtext-pre-radius);
|
|
295
|
+
padding: var(--a-space-3) var(--a-space-4);
|
|
296
|
+
}
|
|
297
|
+
[data-richtext-body] details[open] {
|
|
298
|
+
background: var(--richtext-code-bg);
|
|
299
|
+
}
|
|
300
|
+
[data-richtext-body] summary {
|
|
301
|
+
cursor: pointer;
|
|
302
|
+
font-weight: var(--richtext-strong-weight);
|
|
303
|
+
margin: calc(-1 * var(--a-space-3)) calc(-1 * var(--a-space-4));
|
|
304
|
+
padding: var(--a-space-3) var(--a-space-4);
|
|
305
|
+
list-style-position: inside;
|
|
306
|
+
}
|
|
307
|
+
[data-richtext-body] details[open] summary {
|
|
308
|
+
margin-bottom: var(--a-space-2);
|
|
309
|
+
border-bottom: 1px solid var(--richtext-border);
|
|
310
|
+
}
|
|
311
|
+
|
|
183
312
|
/* ── Tables ── */
|
|
184
313
|
|
|
185
314
|
[data-richtext-body] table {
|
|
@@ -217,9 +346,18 @@
|
|
|
217
346
|
color: var(--richtext-link);
|
|
218
347
|
text-decoration: underline;
|
|
219
348
|
text-underline-offset: 2px;
|
|
349
|
+
transition: color var(--a-duration-fast) var(--a-easing);
|
|
220
350
|
}
|
|
221
351
|
|
|
222
352
|
[data-richtext-body] a:hover {
|
|
223
353
|
color: var(--richtext-link-hover);
|
|
224
354
|
}
|
|
355
|
+
|
|
356
|
+
[data-richtext-body] a:focus-visible {
|
|
357
|
+
outline: var(--a-focus-ring);
|
|
358
|
+
outline-offset: 2px;
|
|
359
|
+
border-radius: var(--a-radius-sm);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
[data-richtext-body] small { font-size: var(--richtext-code-size); color: var(--richtext-fg-muted); }
|
|
225
363
|
}
|
package/components/row/row.css
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
@scope (row-ui) {
|
|
2
2
|
:where(:scope) {
|
|
3
|
-
|
|
3
|
+
/* `--a-gap` defaults to `--a-gap-md` at :root in tokens.css; the
|
|
4
|
+
universal `[gap="N"]` rules override it on the element, so reading
|
|
5
|
+
`--a-gap` here picks up `<row-ui gap="…">` automatically. */
|
|
6
|
+
--row-gap: var(--a-gap);
|
|
4
7
|
--row-justify: flex-start;
|
|
5
8
|
--row-align: center;
|
|
6
9
|
--row-drag-bg-active: var(--a-accent-muted);
|
|
@@ -13,6 +16,9 @@
|
|
|
13
16
|
gap: var(--row-gap);
|
|
14
17
|
justify-content: var(--row-justify);
|
|
15
18
|
align-items: var(--row-align);
|
|
19
|
+
/* Universal [padding] / [margin] opt-in — see tokens.css for scale. */
|
|
20
|
+
padding: var(--a-padding, 0);
|
|
21
|
+
margin: var(--a-margin, 0);
|
|
16
22
|
text-align: inherit;
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope:not(...):hover` inside `@scope` doesn't match
|
|
2
|
+
the scope root. Plain selector outside works. See
|
|
3
|
+
docs/BROWSER-COMPAT.md §3a. */
|
|
4
|
+
segment-ui:not([disabled]):not([selected]):hover {
|
|
5
|
+
color: var(--segment-fg-hover);
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
@scope (segment-ui) {
|
|
2
9
|
:where(:scope) {
|
|
3
10
|
/* ── Layout ── */
|
|
@@ -73,11 +80,7 @@
|
|
|
73
80
|
white-space: nowrap;
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
/* States */
|
|
77
|
-
:scope:not([disabled]):not([selected]):hover {
|
|
78
|
-
color: var(--segment-fg-hover);
|
|
79
|
-
}
|
|
80
|
-
|
|
83
|
+
/* States — hover rule moved outside @scope; see Safari 17.x bug note at top. */
|
|
81
84
|
:scope[selected] {
|
|
82
85
|
color: var(--segment-fg-selected);
|
|
83
86
|
}
|
|
@@ -84,9 +84,12 @@
|
|
|
84
84
|
background: var(--select-bg);
|
|
85
85
|
line-height: 1;
|
|
86
86
|
cursor: pointer;
|
|
87
|
+
/* Match every property the hover / focus / invalid states change. */
|
|
87
88
|
transition:
|
|
89
|
+
background var(--select-duration) var(--select-easing),
|
|
88
90
|
border-color var(--select-duration) var(--select-easing),
|
|
89
|
-
|
|
91
|
+
color var(--select-duration) var(--select-easing),
|
|
92
|
+
box-shadow var(--select-duration) var(--select-easing);
|
|
90
93
|
}
|
|
91
94
|
[slot="trigger"]:hover {
|
|
92
95
|
border-color: var(--select-border-hover);
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
box-sizing: border-box;
|
|
8
8
|
display: grid;
|
|
9
9
|
place-items: var(--stack-align);
|
|
10
|
+
/* Universal [padding] / [margin] opt-in — see tokens.css for scale. */
|
|
11
|
+
padding: var(--a-padding, 0);
|
|
12
|
+
margin: var(--a-margin, 0);
|
|
10
13
|
/* Same UA override as row-ui/col-ui: [align=...] is for flex/grid, not text. */
|
|
11
14
|
text-align: inherit;
|
|
12
15
|
}
|
|
@@ -32,10 +32,17 @@
|
|
|
32
32
|
--stepper-line-done: var(--a-accent);
|
|
33
33
|
--stepper-line-size: 2px;
|
|
34
34
|
|
|
35
|
-
/* ── Layout ──
|
|
35
|
+
/* ── Layout ──
|
|
36
|
+
Parent tokens use the bare `--stepper-*` name (no `-item-` infix)
|
|
37
|
+
so the child's `:where(stepper-item-ui)` block can pull from
|
|
38
|
+
parent → child via `var(--stepper-X, …)` without name collisions.
|
|
39
|
+
A self-named child declaration like
|
|
40
|
+
`--stepper-item-gap-sm: var(--stepper-item-gap-sm, …)` resolves
|
|
41
|
+
to invalid (cycle), which silently breaks any calc() that
|
|
42
|
+
consumes it. */
|
|
36
43
|
--stepper-radius-full: var(--a-radius-full);
|
|
37
|
-
--stepper-
|
|
38
|
-
--stepper-
|
|
44
|
+
--stepper-gap: var(--a-space-3);
|
|
45
|
+
--stepper-gap-sm: var(--a-space-2);
|
|
39
46
|
--stepper-pad-x: var(--a-space-4);
|
|
40
47
|
--stepper-pad-y: var(--a-space-6);
|
|
41
48
|
--stepper-offset-xs: var(--a-space-1);
|
|
@@ -82,7 +89,8 @@
|
|
|
82
89
|
--stepper-item-line-size: var(--stepper-line-size, 2px);
|
|
83
90
|
|
|
84
91
|
--stepper-item-radius-full: var(--a-radius-full);
|
|
85
|
-
--stepper-item-gap
|
|
92
|
+
--stepper-item-gap: var(--stepper-gap, var(--a-space-3));
|
|
93
|
+
--stepper-item-gap-sm: var(--stepper-gap-sm, var(--a-space-2));
|
|
86
94
|
--stepper-item-pad-x: var(--stepper-pad-x, var(--a-space-4));
|
|
87
95
|
--stepper-item-pad-y: var(--stepper-pad-y, var(--a-space-6));
|
|
88
96
|
--stepper-item-offset-xs: var(--stepper-offset-xs, var(--a-space-1));
|
|
@@ -38,18 +38,18 @@ class AdiaStepper extends AdiaElement {
|
|
|
38
38
|
const last = items[items.length - 1];
|
|
39
39
|
if (last) last.setAttribute('data-last', '');
|
|
40
40
|
|
|
41
|
-
// Parent drives child state via step index
|
|
41
|
+
// Parent drives child state via step index. Canonical Phase 6
|
|
42
|
+
// contract: `status` enum (`completed` / `active` / `idle`).
|
|
43
|
+
// CSS only matches `[status="…"]`; the legacy `[completed]` /
|
|
44
|
+
// `[active]` Booleans were removed in 0.0.20.
|
|
42
45
|
items.forEach((item, i) => {
|
|
43
46
|
item.setAttribute('data-index', i);
|
|
44
47
|
if (i < this.step) {
|
|
45
|
-
item.setAttribute('
|
|
46
|
-
item.removeAttribute('active');
|
|
48
|
+
item.setAttribute('status', 'completed');
|
|
47
49
|
} else if (i === this.step) {
|
|
48
|
-
item.
|
|
49
|
-
item.setAttribute('active', '');
|
|
50
|
+
item.setAttribute('status', 'active');
|
|
50
51
|
} else {
|
|
51
|
-
item.
|
|
52
|
-
item.removeAttribute('active');
|
|
52
|
+
item.setAttribute('status', 'idle');
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -89,6 +89,16 @@
|
|
|
89
89
|
|
|
90
90
|
:scope > [data-swiper-track]::-webkit-scrollbar { display: none; }
|
|
91
91
|
|
|
92
|
+
/* Click-and-drag affordance for cursor users — touch keeps native pan. */
|
|
93
|
+
@media (hover: hover) {
|
|
94
|
+
:scope > [data-swiper-track] { cursor: grab; }
|
|
95
|
+
:scope > [data-swiper-track][data-dragging] {
|
|
96
|
+
cursor: grabbing;
|
|
97
|
+
scroll-snap-type: none; /* let the pointer track 1:1 during drag */
|
|
98
|
+
user-select: none;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
92
102
|
/* ── Slides ── */
|
|
93
103
|
|
|
94
104
|
:scope > [data-swiper-track] > * {
|
|
@@ -43,6 +43,7 @@ class AdiaSwiper extends AdiaElement {
|
|
|
43
43
|
#activeIndex = 0;
|
|
44
44
|
#bound = false;
|
|
45
45
|
#fallbackNav = false;
|
|
46
|
+
#drag = null; /* { pointerId, startX, startScrollLeft, hasMoved } */
|
|
46
47
|
|
|
47
48
|
get slides() {
|
|
48
49
|
if (!this.#track) return [];
|
|
@@ -88,6 +89,14 @@ class AdiaSwiper extends AdiaElement {
|
|
|
88
89
|
|
|
89
90
|
// Keyboard navigation
|
|
90
91
|
this.addEventListener('keydown', this.#onKeydown);
|
|
92
|
+
|
|
93
|
+
// Click + drag (mouse / pen). Touch keeps native pan via scroll-snap.
|
|
94
|
+
this.#track.addEventListener('pointerdown', this.#onPointerDown);
|
|
95
|
+
this.#track.addEventListener('pointermove', this.#onPointerMove);
|
|
96
|
+
this.#track.addEventListener('pointerup', this.#onPointerUp);
|
|
97
|
+
this.#track.addEventListener('pointercancel', this.#onPointerUp);
|
|
98
|
+
// Capture-phase click suppression so a drag-on-button doesn't activate it.
|
|
99
|
+
this.#track.addEventListener('click', this.#onClickCapture, true);
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
if (this.autoplay) this.play();
|
|
@@ -109,6 +118,14 @@ class AdiaSwiper extends AdiaElement {
|
|
|
109
118
|
this.removeEventListener('focusin', this.#onPauseFocus);
|
|
110
119
|
this.removeEventListener('focusout', this.#onResumeFocus);
|
|
111
120
|
this.removeEventListener('keydown', this.#onKeydown);
|
|
121
|
+
if (this.#track) {
|
|
122
|
+
this.#track.removeEventListener('pointerdown', this.#onPointerDown);
|
|
123
|
+
this.#track.removeEventListener('pointermove', this.#onPointerMove);
|
|
124
|
+
this.#track.removeEventListener('pointerup', this.#onPointerUp);
|
|
125
|
+
this.#track.removeEventListener('pointercancel', this.#onPointerUp);
|
|
126
|
+
this.#track.removeEventListener('click', this.#onClickCapture, true);
|
|
127
|
+
}
|
|
128
|
+
this.#drag = null;
|
|
112
129
|
this.#track = null;
|
|
113
130
|
this.#bound = false;
|
|
114
131
|
}
|
|
@@ -280,6 +297,66 @@ class AdiaSwiper extends AdiaElement {
|
|
|
280
297
|
if (e.key === 'ArrowRight') { e.preventDefault(); this.next(); }
|
|
281
298
|
if (e.key === 'ArrowLeft') { e.preventDefault(); this.prev(); }
|
|
282
299
|
};
|
|
300
|
+
|
|
301
|
+
// ── Click + drag (mouse / pen) ──
|
|
302
|
+
// Native touch already scroll-pans the track; we only intercept mouse and
|
|
303
|
+
// pen so cursor users get the same swipe affordance as touch users.
|
|
304
|
+
|
|
305
|
+
#DRAG_THRESHOLD_PX = 5;
|
|
306
|
+
|
|
307
|
+
#onPointerDown = (e) => {
|
|
308
|
+
if (e.pointerType === 'touch') return; /* native pan handles touch */
|
|
309
|
+
if (e.button !== 0) return; /* primary button only */
|
|
310
|
+
if (e.target.closest('button, button-ui, a, input, select, textarea')) return;
|
|
311
|
+
this.#drag = {
|
|
312
|
+
pointerId: e.pointerId,
|
|
313
|
+
startX: e.clientX,
|
|
314
|
+
startScrollLeft: this.#track.scrollLeft,
|
|
315
|
+
hasMoved: false,
|
|
316
|
+
};
|
|
317
|
+
/* Disable smooth scroll during drag so the bar tracks the pointer 1:1. */
|
|
318
|
+
this.#track.style.scrollBehavior = 'auto';
|
|
319
|
+
this.#track.setAttribute('data-dragging', '');
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
#onPointerMove = (e) => {
|
|
323
|
+
if (!this.#drag || e.pointerId !== this.#drag.pointerId) return;
|
|
324
|
+
const dx = e.clientX - this.#drag.startX;
|
|
325
|
+
if (Math.abs(dx) >= this.#DRAG_THRESHOLD_PX) {
|
|
326
|
+
if (!this.#drag.hasMoved) {
|
|
327
|
+
this.#drag.hasMoved = true;
|
|
328
|
+
/* Capture pointer so we keep tracking even if it leaves the track. */
|
|
329
|
+
try { this.#track.setPointerCapture(e.pointerId); } catch { /* noop */ }
|
|
330
|
+
}
|
|
331
|
+
this.#track.scrollLeft = this.#drag.startScrollLeft - dx;
|
|
332
|
+
e.preventDefault(); /* suppress text selection while dragging */
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
#onPointerUp = (e) => {
|
|
337
|
+
if (!this.#drag || e.pointerId !== this.#drag.pointerId) return;
|
|
338
|
+
const wasMoved = this.#drag.hasMoved;
|
|
339
|
+
if (wasMoved) {
|
|
340
|
+
try { this.#track.releasePointerCapture(e.pointerId); } catch { /* noop */ }
|
|
341
|
+
}
|
|
342
|
+
this.#drag = null;
|
|
343
|
+
/* Re-enable smooth scrolling; scroll-snap will glide to the nearest slide. */
|
|
344
|
+
this.#track.style.scrollBehavior = '';
|
|
345
|
+
this.#track.removeAttribute('data-dragging');
|
|
346
|
+
/* Ensure dragging stays "active" through the click event that follows
|
|
347
|
+
on the same gesture so #onClickCapture can suppress it. */
|
|
348
|
+
if (wasMoved) {
|
|
349
|
+
this.#track.setAttribute('data-just-dragged', '');
|
|
350
|
+
requestAnimationFrame(() => this.#track?.removeAttribute('data-just-dragged'));
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
#onClickCapture = (e) => {
|
|
355
|
+
if (this.#track?.hasAttribute('data-just-dragged')) {
|
|
356
|
+
e.preventDefault();
|
|
357
|
+
e.stopPropagation();
|
|
358
|
+
}
|
|
359
|
+
};
|
|
283
360
|
}
|
|
284
361
|
|
|
285
362
|
customElements.define('swiper-ui', AdiaSwiper);
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope[attr]:hover` inside `@scope` doesn't match the
|
|
2
|
+
scope root. Plain selectors outside work. See docs/BROWSER-COMPAT.md §3a. */
|
|
3
|
+
switch-ui:not([disabled]):hover {
|
|
4
|
+
--switch-track-bg: var(--switch-track-bg-hover);
|
|
5
|
+
--switch-track-border: var(--switch-track-border-hover);
|
|
6
|
+
}
|
|
7
|
+
switch-ui[checked]:not([disabled]):hover {
|
|
8
|
+
--switch-track-bg: var(--switch-track-bg-checked-hover);
|
|
9
|
+
--switch-track-border: var(--switch-track-border-checked-hover);
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
@scope (switch-ui) {
|
|
2
13
|
:where(:scope) {
|
|
3
14
|
/* ── Layout ── */
|
|
@@ -66,19 +77,12 @@
|
|
|
66
77
|
background var(--switch-duration) var(--switch-easing),
|
|
67
78
|
border-color var(--switch-duration) var(--switch-easing);
|
|
68
79
|
}
|
|
69
|
-
|
|
70
|
-
--switch-track-bg: var(--switch-track-bg-hover);
|
|
71
|
-
--switch-track-border: var(--switch-track-border-hover);
|
|
72
|
-
}
|
|
80
|
+
/* hover rules moved outside @scope — see Safari 17.x bug note at top. */
|
|
73
81
|
:scope[checked] {
|
|
74
82
|
--switch-track-bg: var(--switch-track-bg-checked);
|
|
75
83
|
--switch-track-border: var(--switch-track-border-checked);
|
|
76
84
|
--switch-thumb-bg: var(--switch-thumb-bg-checked);
|
|
77
85
|
}
|
|
78
|
-
:scope[checked]:not([disabled]):hover {
|
|
79
|
-
--switch-track-bg: var(--switch-track-bg-checked-hover);
|
|
80
|
-
--switch-track-border: var(--switch-track-border-checked-hover);
|
|
81
|
-
}
|
|
82
86
|
|
|
83
87
|
[slot="thumb"] {
|
|
84
88
|
position: absolute;
|
|
@@ -89,7 +93,9 @@
|
|
|
89
93
|
border-radius: var(--switch-radius);
|
|
90
94
|
background: var(--switch-thumb-bg);
|
|
91
95
|
box-shadow: var(--switch-thumb-shadow);
|
|
92
|
-
transition:
|
|
96
|
+
transition:
|
|
97
|
+
transform var(--switch-duration) var(--switch-easing),
|
|
98
|
+
background var(--switch-duration) var(--switch-easing);
|
|
93
99
|
}
|
|
94
100
|
:scope[checked] [slot="thumb"] {
|
|
95
101
|
transform: translateX(var(--switch-thumb-travel));
|
package/components/tag/tag.css
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope[attr]:hover` inside `@scope` doesn't match the
|
|
2
|
+
scope root. Plain selector outside works. See docs/BROWSER-COMPAT.md §3a. */
|
|
3
|
+
tag-ui[removable]:not([disabled]):hover {
|
|
4
|
+
--tag-bg: var(--tag-bg-hover);
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
@scope (tag-ui) {
|
|
2
8
|
:where(:scope) {
|
|
3
9
|
/* ── Tokens ── */
|
|
@@ -75,10 +81,7 @@
|
|
|
75
81
|
|
|
76
82
|
/* Size handled by universal [size] attribute system. */
|
|
77
83
|
|
|
78
|
-
/*
|
|
79
|
-
:scope[removable]:not([disabled]):hover {
|
|
80
|
-
--tag-bg: var(--tag-bg-hover);
|
|
81
|
-
}
|
|
84
|
+
/* hover rule moved outside @scope — see Safari 17.x bug note at top. */
|
|
82
85
|
|
|
83
86
|
/* ── Focus ── */
|
|
84
87
|
:scope:focus-visible {
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope:not(...):hover` inside `@scope` doesn't match
|
|
2
|
+
the scope root. Plain selector outside works. See
|
|
3
|
+
docs/BROWSER-COMPAT.md §3a. */
|
|
4
|
+
toggle-group-ui:not([disabled]):hover {
|
|
5
|
+
--toggle-option-bg: var(--toggle-option-bg-hover);
|
|
6
|
+
--toggle-option-fg: var(--toggle-option-fg-hover);
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
@scope (toggle-group-ui) {
|
|
2
10
|
:where(:scope) {
|
|
3
11
|
/* ── Tokens ── */
|
|
@@ -69,10 +77,7 @@
|
|
|
69
77
|
border-inline-start: var(--toggle-option-border-width) solid var(--toggle-option-border-color);
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
--toggle-option-bg: var(--toggle-option-bg-hover);
|
|
74
|
-
--toggle-option-fg: var(--toggle-option-fg-hover);
|
|
75
|
-
}
|
|
80
|
+
/* hover rule moved outside @scope — see Safari 17.x bug note at top. */
|
|
76
81
|
|
|
77
82
|
:scope:focus-visible {
|
|
78
83
|
outline: none;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-utils.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -71,6 +71,7 @@ class AdiaAppNav extends AdiaElement {
|
|
|
71
71
|
const item = e.target.closest('app-nav-item-ui');
|
|
72
72
|
if (item && this.contains(item)) {
|
|
73
73
|
this.select(item);
|
|
74
|
+
this.#flushHoverState();
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -88,6 +89,18 @@ class AdiaAppNav extends AdiaElement {
|
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
// Safari macOS leaves `:hover` stuck on items the cursor passed through
|
|
93
|
+
// when the DOM mutates during click+route navigation (no `mouseleave`
|
|
94
|
+
// fires). Toggling pointer-events on the container forces Safari to
|
|
95
|
+
// re-evaluate hover state on next paint without flickering layout.
|
|
96
|
+
// Documented in docs/BROWSER-COMPAT.md §3a.
|
|
97
|
+
#flushHoverState() {
|
|
98
|
+
this.style.pointerEvents = 'none';
|
|
99
|
+
requestAnimationFrame(() => {
|
|
100
|
+
this.style.pointerEvents = '';
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
91
104
|
disconnected() {
|
|
92
105
|
this.removeEventListener('click', this.#onClick);
|
|
93
106
|
this.#ro?.disconnect();
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope:hover` inside `@scope` doesn't match the scope
|
|
2
|
+
root. Plain selector outside the @scope works — custom props from the
|
|
3
|
+
`:where(:scope)` block are still set on the element via inheritance, so
|
|
4
|
+
`var(--token)` lookups resolve correctly. Specificity (0,1,1) is
|
|
5
|
+
preserved. Safari < 18 is below the §1 floor (ADR-0007); this fix
|
|
6
|
+
protects opt-in consumers extending the floor downward and is
|
|
7
|
+
harmlessly redundant on engines without the bug. */
|
|
8
|
+
app-nav-item-ui:hover {
|
|
9
|
+
background: var(--nav-item-bg-hover);
|
|
10
|
+
color: var(--nav-item-fg-hover);
|
|
11
|
+
}
|
|
12
|
+
app-nav-item-ui:hover [slot="icon"] {
|
|
13
|
+
color: var(--nav-item-fg-hover);
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
@scope (app-nav-item-ui) {
|
|
2
17
|
:where(:scope) {
|
|
3
18
|
--nav-item-accent: var(--a-accent);
|
|
@@ -40,15 +55,6 @@
|
|
|
40
55
|
outline: none;
|
|
41
56
|
}
|
|
42
57
|
|
|
43
|
-
:scope:hover {
|
|
44
|
-
background: var(--nav-item-bg-hover);
|
|
45
|
-
color: var(--nav-item-fg-hover);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
:scope:hover [slot="icon"] {
|
|
49
|
-
color: var(--nav-item-fg-hover);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
58
|
:scope:focus-visible {
|
|
53
59
|
background: var(--nav-item-bg-hover);
|
|
54
60
|
color: var(--nav-item-fg-hover);
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/* Safari 17.x bug: `:scope:hover` inside `@scope` doesn't match the scope
|
|
2
|
+
root. Plain selector outside the @scope works. `:focus-visible` matches
|
|
3
|
+
correctly inside @scope, so the combined `:hover, :focus-visible` rule
|
|
4
|
+
is split — `:hover` moves out, `:focus-visible` stays. See
|
|
5
|
+
docs/BROWSER-COMPAT.md §3a. */
|
|
6
|
+
section-nav-item-ui:hover:not([disabled]) {
|
|
7
|
+
background: var(--section-nav-item-bg-hover);
|
|
8
|
+
color: var(--section-nav-item-fg-hover);
|
|
9
|
+
}
|
|
10
|
+
section-nav-item-ui:hover:not([disabled]) > icon-ui {
|
|
11
|
+
color: var(--section-nav-item-fg-hover);
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
@scope (section-nav-item-ui) {
|
|
2
15
|
:where(:scope) {
|
|
3
16
|
--section-nav-item-row-height: var(--a-size);
|
|
@@ -58,12 +71,10 @@
|
|
|
58
71
|
text-overflow: ellipsis;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
|
-
:scope:hover:not([disabled]),
|
|
62
74
|
:scope:focus-visible:not([disabled]) {
|
|
63
75
|
background: var(--section-nav-item-bg-hover);
|
|
64
76
|
color: var(--section-nav-item-fg-hover);
|
|
65
77
|
}
|
|
66
|
-
:scope:hover:not([disabled]) > icon-ui,
|
|
67
78
|
:scope:focus-visible:not([disabled]) > icon-ui {
|
|
68
79
|
color: var(--section-nav-item-fg-hover);
|
|
69
80
|
}
|