@dryui/ui 1.9.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/alert-dialog/alert-dialog-root.svelte +2 -2
  2. package/dist/avatar/avatar.svelte +2 -0
  3. package/dist/badge/badge.svelte +34 -32
  4. package/dist/button/button.svelte +67 -38
  5. package/dist/chromatic-aberration/chromatic-aberration.svelte +2 -2
  6. package/dist/collapsible/collapsible-root.svelte +3 -2
  7. package/dist/collapsible/context.svelte.d.ts +1 -2
  8. package/dist/collapsible/context.svelte.js +1 -2
  9. package/dist/color-picker/color-picker-channel-input.svelte +1 -0
  10. package/dist/combobox/combobox-content.svelte +1 -0
  11. package/dist/combobox/combobox-input-root.svelte +3 -3
  12. package/dist/command-palette/command-palette-dialog-root.svelte +3 -3
  13. package/dist/command-palette/command-palette-item.svelte +3 -3
  14. package/dist/context-menu/context-menu-root.svelte +2 -0
  15. package/dist/date-field/date-field-segment.svelte +26 -4
  16. package/dist/date-picker/datepicker-input-root.svelte +2 -0
  17. package/dist/date-range-picker/date-range-picker-root.svelte +2 -0
  18. package/dist/dialog/dialog-root.svelte +2 -2
  19. package/dist/drag-and-drop/drag-and-drop-root.svelte +34 -3
  20. package/dist/drawer/drawer-root.svelte +2 -2
  21. package/dist/dropdown-menu/dropdown-menu-root.svelte +2 -0
  22. package/dist/field/field-root.svelte +3 -2
  23. package/dist/file-select/file-select-root.svelte +1 -0
  24. package/dist/file-upload/file-upload-item.svelte +1 -0
  25. package/dist/heading/heading.svelte +6 -2
  26. package/dist/image/image.svelte +2 -0
  27. package/dist/internal/date-family-controller.svelte.d.ts +2 -1
  28. package/dist/internal/date-family-controller.svelte.js +4 -4
  29. package/dist/internal/menu-root-state.svelte.d.ts +1 -0
  30. package/dist/internal/menu-root-state.svelte.js +2 -3
  31. package/dist/internal/modal-content.svelte +2 -0
  32. package/dist/internal/picker-popover-content.svelte +1 -0
  33. package/dist/link-preview/link-preview-root.svelte +3 -3
  34. package/dist/logo-mark/logo-mark.svelte +1 -0
  35. package/dist/markdown-renderer/markdown-renderer.svelte +9 -3
  36. package/dist/markdown-renderer/markdown-renderer.svelte.d.ts +1 -1
  37. package/dist/mega-menu/mega-menu-item.svelte +4 -4
  38. package/dist/menubar/menubar-content.svelte +1 -0
  39. package/dist/menubar/menubar-menu.svelte +2 -2
  40. package/dist/menubar/menubar-root.svelte +1 -0
  41. package/dist/multi-select-combobox/multi-select-combobox-content.svelte +1 -0
  42. package/dist/multi-select-combobox/multi-select-combobox-item.svelte +2 -2
  43. package/dist/multi-select-combobox/multi-select-combobox-root-input.svelte +4 -3
  44. package/dist/navigation-menu/context.svelte.d.ts +0 -2
  45. package/dist/navigation-menu/context.svelte.js +1 -2
  46. package/dist/navigation-menu/navigation-menu-item.svelte +5 -8
  47. package/dist/notification-center/notification-center-root.svelte +3 -3
  48. package/dist/popover/popover-root.svelte +3 -3
  49. package/dist/radio-group/radio-group.svelte +2 -2
  50. package/dist/rich-text-editor/rich-text-editor-content.svelte +14 -6
  51. package/dist/rich-text-editor/rich-text-editor-root.svelte +19 -9
  52. package/dist/rich-text-editor/rich-text-editor-root.svelte.d.ts +1 -0
  53. package/dist/rich-text-editor/rich-text-editor-toolbar-button-input.svelte +1 -0
  54. package/dist/select/select-root-input.svelte +3 -3
  55. package/dist/tags-input/tags-input-root.svelte +1 -0
  56. package/dist/timeline/timeline-title.svelte +6 -0
  57. package/dist/toast/toast-root.svelte +1 -0
  58. package/dist/toolbar/toolbar-root.svelte +1 -0
  59. package/dist/tooltip/tooltip-root.svelte +3 -3
  60. package/dist/video-embed/video-embed-button.svelte +1 -0
  61. package/package.json +3 -3
  62. package/skills/dryui/SKILL.md +4 -1
  63. package/skills/dryui/rules/composition.md +1 -1
  64. package/skills/dryui/rules/theming.md +2 -2
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import { generateFormId } from '@dryui/primitives';
4
3
  import { setAlertDialogCtx } from './context.svelte.js';
5
4
 
6
5
  interface Props {
@@ -10,7 +9,8 @@
10
9
 
11
10
  let { open = $bindable(false), children }: Props = $props();
12
11
 
13
- const headerId = generateFormId('alert-dialog');
12
+ const uid = $props.id();
13
+ const headerId = `alert-dialog-${uid}`;
14
14
 
15
15
  setAlertDialogCtx({
16
16
  get open() {
@@ -66,6 +66,7 @@
66
66
  {...rest}
67
67
  >
68
68
  {#if showImage}
69
+ <!-- dryui-allow raw-img: Avatar owns the intrinsic image primitive and fallback/error lifecycle. -->
69
70
  <img {src} {alt} onerror={handleError} />
70
71
  {:else if children}
71
72
  {@render children()}
@@ -90,6 +91,7 @@
90
91
  {...rest}
91
92
  >
92
93
  {#if showImage}
94
+ <!-- dryui-allow raw-img: Avatar owns the intrinsic image primitive and fallback/error lifecycle. -->
93
95
  <img {src} {alt} onerror={handleError} />
94
96
  {:else if children}
95
97
  {@render children()}
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
  import { variantAttrs } from '@dryui/primitives';
5
+ import { getPageHeaderMetaCtx } from '@dryui/primitives/page-header';
5
6
  import { resolveAlias } from '../internal/color-aliases.js';
6
7
  import type { BadgeColor } from './index.js';
7
8
 
@@ -21,9 +22,9 @@
21
22
  }
22
23
 
23
24
  let {
24
- variant = 'soft',
25
- color = 'gray',
26
- size = 'sm',
25
+ variant: variantProp,
26
+ color: colorProp,
27
+ size: sizeProp,
27
28
  class: className,
28
29
  children,
29
30
  pulse,
@@ -31,43 +32,42 @@
31
32
  numeric = false,
32
33
  ...rest
33
34
  }: Props = $props();
35
+
36
+ const ctx = getPageHeaderMetaCtx();
37
+ const variant = $derived(variantProp ?? ctx?.variant ?? 'soft');
38
+ const color = $derived<BadgeColor>(colorProp ?? ctx?.color ?? 'gray');
39
+ const size = $derived(sizeProp ?? ctx?.size ?? 'sm');
34
40
  const resolvedColor = $derived(resolveAlias(color, 'gray'));
35
41
  const isDot = $derived(variant === 'dot');
36
42
  const hasIcon = $derived(!!icon && !isDot);
37
43
  </script>
38
44
 
39
- <span data-wrapper>
40
- <span
41
- class={className}
42
- data-badge={isDot ? undefined : ''}
43
- data-dot={isDot ? '' : undefined}
44
- data-numeric={numeric && !isDot ? 'true' : undefined}
45
- {...variantAttrs({
46
- variant: isDot ? undefined : variant,
47
- color: resolvedColor,
48
- size: isDot ? undefined : size,
49
- 'data-icon-only': hasIcon ? '' : undefined,
50
- 'data-pulse': pulse ? '' : undefined
51
- })}
52
- {...rest}
53
- >
54
- {#if isDot}
55
- <!-- dot variant renders no content -->
56
- {:else if icon}
57
- {@render icon()}
58
- {:else if children}
59
- {@render children()}
60
- {/if}
61
- </span>
45
+ <span
46
+ class={className}
47
+ data-badge={isDot ? undefined : ''}
48
+ data-dot={isDot ? '' : undefined}
49
+ data-numeric={numeric && !isDot ? 'true' : undefined}
50
+ {...variantAttrs({
51
+ variant: isDot ? undefined : variant,
52
+ color: resolvedColor,
53
+ size: isDot ? undefined : size,
54
+ 'data-icon-only': hasIcon ? '' : undefined,
55
+ 'data-pulse': pulse ? '' : undefined
56
+ })}
57
+ {...rest}
58
+ >
59
+ {#if isDot}
60
+ <!-- dot variant renders no content -->
61
+ {:else if icon}
62
+ {@render icon()}
63
+ {:else if children}
64
+ {@render children()}
65
+ {/if}
62
66
  </span>
63
67
 
64
68
  <style>
65
- [data-wrapper] {
66
- display: inline-grid;
67
- justify-self: start;
68
- }
69
-
70
69
  [data-badge] {
70
+ justify-self: start;
71
71
  /* Component tokens (Tier 3) */
72
72
  --_badge-bg-default: var(--dry-color-fill);
73
73
  --_badge-color-default: var(--dry-color-text-weak);
@@ -85,7 +85,9 @@
85
85
  --_badge-radius: var(--dry-badge-radius, var(--_badge-radius-default));
86
86
 
87
87
  display: inline-grid;
88
- place-items: center;
88
+ grid-auto-flow: column;
89
+ align-items: center;
90
+ gap: var(--dry-space-1);
89
91
  padding: var(--_badge-padding-y) var(--_badge-padding-x);
90
92
  font-size: var(--_badge-font-size);
91
93
  font-family: var(--dry-font-sans);
@@ -81,9 +81,33 @@
81
81
  (onclick as ((event: MouseEvent) => void) | undefined)?.(event);
82
82
  }
83
83
 
84
- function attachRef(node: HTMLButtonElement | HTMLAnchorElement) {
84
+ function syncIconEdgeAttrs(node: HTMLButtonElement | HTMLAnchorElement) {
85
+ const children = Array.from(node.children).filter(
86
+ (child): child is HTMLElement => child instanceof HTMLElement
87
+ );
88
+ const firstChild = children[0];
89
+ const lastChild = children[children.length - 1];
90
+
91
+ node.toggleAttribute('data-icon-start', firstChild?.hasAttribute('data-dry-icon') === true);
92
+ node.toggleAttribute('data-icon-end', lastChild?.hasAttribute('data-dry-icon') === true);
93
+ }
94
+
95
+ function attachButton(node: HTMLButtonElement | HTMLAnchorElement) {
85
96
  ref?.(node);
86
- return () => ref?.(null);
97
+ syncIconEdgeAttrs(node);
98
+
99
+ const observer = new MutationObserver(() => syncIconEdgeAttrs(node));
100
+ observer.observe(node, {
101
+ attributeFilter: ['data-dry-icon'],
102
+ attributes: true,
103
+ childList: true,
104
+ subtree: true
105
+ });
106
+
107
+ return () => {
108
+ observer.disconnect();
109
+ ref?.(null);
110
+ };
87
111
  }
88
112
  </script>
89
113
 
@@ -98,11 +122,14 @@
98
122
  aria-disabled={disabled || undefined}
99
123
  data-disabled={disabled || undefined}
100
124
  data-optical={optical}
125
+ data-dry-button
126
+ data-icon-start={undefined}
127
+ data-icon-end={undefined}
101
128
  tabindex={disabled ? -1 : undefined}
102
129
  {...variantAttrs({ variant, size, color })}
103
130
  class={className}
104
131
  onclick={handleLinkClick}
105
- {@attach attachRef}
132
+ {@attach attachButton}
106
133
  >
107
134
  {@render children()}
108
135
  </a>
@@ -112,11 +139,14 @@
112
139
  {disabled}
113
140
  data-disabled={disabled || undefined}
114
141
  data-optical={optical}
142
+ data-dry-button
143
+ data-icon-start={undefined}
144
+ data-icon-end={undefined}
115
145
  {...variantAttrs({ variant, size, color })}
116
146
  class={className}
117
147
  {onclick}
118
148
  {...rest}
119
- {@attach attachRef}
149
+ {@attach attachButton}
120
150
  >
121
151
  {@render children()}
122
152
  </button>
@@ -136,8 +166,7 @@
136
166
  display: inline-grid;
137
167
  }
138
168
 
139
- a,
140
- button {
169
+ [data-dry-button] {
141
170
  /* Resolve public button tokens without stomping inherited overrides. */
142
171
  --_dry-btn-accent: var(--dry-btn-accent, var(--dry-color-fill-brand));
143
172
  --_dry-btn-accent-fg: var(--dry-btn-accent-fg, var(--dry-color-text-brand));
@@ -227,17 +256,17 @@
227
256
  to disable the nudge. Data-attribute selectors pierce Svelte style
228
257
  scoping without needing `:global()`, which is banned by
229
258
  `dryui/no-global`. */
230
- :is(a, button)[data-optical='auto']:has(> [data-dry-icon]:first-child) {
259
+ [data-dry-button][data-optical='auto'][data-icon-start] {
231
260
  padding-inline-start: calc(var(--_dry-btn-padding-x) - var(--dry-optical-icon-offset));
232
261
  }
233
262
 
234
- :is(a, button)[data-optical='auto']:has(> [data-dry-icon]:last-child) {
263
+ [data-dry-button][data-optical='auto'][data-icon-end] {
235
264
  padding-inline-end: calc(var(--_dry-btn-padding-x) - var(--dry-optical-icon-offset));
236
265
  }
237
266
 
238
267
  /* ── Variants ──────────────────────────────────────────────────────────────── */
239
268
 
240
- :is(a, button)[data-variant='solid'] {
269
+ [data-dry-button][data-variant='solid'] {
241
270
  --_dry-btn-bg: var(--dry-btn-bg, var(--_dry-btn-accent));
242
271
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-on-accent));
243
272
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -252,7 +281,7 @@
252
281
  }
253
282
  }
254
283
 
255
- :is(a, button)[data-variant='outline'] {
284
+ [data-dry-button][data-variant='outline'] {
256
285
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
257
286
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-accent-fg));
258
287
  --_dry-btn-border: var(--dry-btn-border, var(--_dry-btn-accent-stroke));
@@ -272,7 +301,7 @@
272
301
  }
273
302
  }
274
303
 
275
- :is(a, button)[data-variant='ghost'] {
304
+ [data-dry-button][data-variant='ghost'] {
276
305
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
277
306
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-accent-fg));
278
307
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -294,7 +323,7 @@
294
323
  }
295
324
  }
296
325
 
297
- :is(a, button)[data-variant='soft'] {
326
+ [data-dry-button][data-variant='soft'] {
298
327
  --_dry-btn-bg: var(--dry-btn-bg, var(--_dry-btn-accent-weak));
299
328
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-accent-fg));
300
329
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -311,7 +340,7 @@
311
340
  }
312
341
  }
313
342
 
314
- :is(a, button)[data-variant='secondary'] {
343
+ [data-dry-button][data-variant='secondary'] {
315
344
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
316
345
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-accent-fg));
317
346
  --_dry-btn-border: var(--dry-btn-border, var(--_dry-btn-accent-stroke));
@@ -330,7 +359,7 @@
330
359
  }
331
360
  }
332
361
 
333
- :is(a, button)[data-variant='bare'] {
362
+ [data-dry-button][data-variant='bare'] {
334
363
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
335
364
  --_dry-btn-color: var(--dry-btn-color, inherit);
336
365
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -346,7 +375,7 @@
346
375
  }
347
376
  }
348
377
 
349
- :is(a, button)[data-variant='link'] {
378
+ [data-dry-button][data-variant='link'] {
350
379
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
351
380
  --_dry-btn-color: var(--dry-btn-color, var(--_dry-btn-accent-fg));
352
381
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -366,7 +395,7 @@
366
395
  }
367
396
  }
368
397
 
369
- :is(a, button)[data-variant='trigger'] {
398
+ [data-dry-button][data-variant='trigger'] {
370
399
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
371
400
  --_dry-btn-color: var(--dry-btn-color, var(--dry-color-text-strong));
372
401
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -389,7 +418,7 @@
389
418
  }
390
419
  }
391
420
 
392
- :is(a, button)[data-variant='nav'] {
421
+ [data-dry-button][data-variant='nav'] {
393
422
  --_dry-btn-bg: var(--dry-btn-bg, var(--dry-color-bg-raised));
394
423
  --_dry-btn-color: var(--dry-btn-color, var(--dry-color-text-strong));
395
424
  --_dry-btn-border: var(--dry-btn-border, var(--dry-color-stroke-weak));
@@ -407,7 +436,7 @@
407
436
  }
408
437
  }
409
438
 
410
- :is(a, button)[data-variant='tab'] {
439
+ [data-dry-button][data-variant='tab'] {
411
440
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
412
441
  --_dry-btn-color: var(--dry-btn-color, var(--dry-color-text-weak));
413
442
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -429,7 +458,7 @@
429
458
  }
430
459
  }
431
460
 
432
- :is(a, button)[data-variant='toggle'] {
461
+ [data-dry-button][data-variant='toggle'] {
433
462
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
434
463
  --_dry-btn-color: var(--dry-btn-color, var(--dry-color-text-weak));
435
464
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -446,7 +475,7 @@
446
475
  }
447
476
  }
448
477
 
449
- :is(a, button)[data-variant='pill'] {
478
+ [data-dry-button][data-variant='pill'] {
450
479
  --_dry-btn-bg: var(--dry-btn-bg, transparent);
451
480
  --_dry-btn-color: var(--dry-btn-color, var(--dry-color-text-weak));
452
481
  --_dry-btn-border: var(--dry-btn-border, transparent);
@@ -463,7 +492,7 @@
463
492
  }
464
493
  }
465
494
 
466
- :is(a, button)[data-color='primary'] {
495
+ [data-dry-button][data-color='primary'] {
467
496
  --_dry-btn-accent: var(--dry-btn-accent, var(--dry-color-fill-brand));
468
497
  --_dry-btn-accent-fg: var(--dry-btn-accent-fg, var(--dry-color-text-brand));
469
498
  --_dry-btn-accent-stroke: var(--dry-btn-accent-stroke, var(--dry-color-stroke-brand));
@@ -472,7 +501,7 @@
472
501
  --_dry-btn-accent-active: var(--dry-btn-accent-active, var(--dry-color-fill-brand-active));
473
502
  }
474
503
 
475
- :is(a, button)[data-color='danger'] {
504
+ [data-dry-button][data-color='danger'] {
476
505
  --_dry-btn-accent: var(--dry-btn-accent, var(--dry-color-fill-error));
477
506
  --_dry-btn-accent-fg: var(--dry-btn-accent-fg, var(--dry-color-text-error));
478
507
  --_dry-btn-accent-stroke: var(--dry-btn-accent-stroke, var(--dry-color-stroke-error));
@@ -487,7 +516,7 @@
487
516
  light theme → near-black bg + white text; dark theme → white bg + near-black
488
517
  text. Any consumer token override (--dry-btn-bg etc.) still wins because the
489
518
  variant styles read from the public layer first. */
490
- :is(a, button)[data-color='ink'] {
519
+ [data-dry-button][data-color='ink'] {
491
520
  --_dry-btn-accent: var(--dry-btn-accent, var(--dry-color-bg-inverse));
492
521
  --_dry-btn-accent-fg: var(--dry-btn-accent-fg, var(--dry-color-text-inverse));
493
522
  --_dry-btn-accent-stroke: var(--dry-btn-accent-stroke, var(--dry-color-stroke-strong));
@@ -508,7 +537,7 @@
508
537
 
509
538
  /* ── Sizes ─────────────────────────────────────────────────────────────────── */
510
539
 
511
- :is(a, button)[data-size='sm'] {
540
+ [data-dry-button][data-size='sm'] {
512
541
  --_dry-btn-padding-x: var(--dry-btn-padding-x, var(--dry-space-3));
513
542
  --_dry-btn-padding-y: var(--dry-btn-padding-y, var(--dry-space-1_5));
514
543
  --_dry-btn-font-size: var(
@@ -519,7 +548,7 @@
519
548
  min-height: var(--dry-btn-min-height, var(--dry-space-8));
520
549
  }
521
550
 
522
- :is(a, button)[data-size='md'] {
551
+ [data-dry-button][data-size='md'] {
523
552
  --_dry-btn-padding-x: var(--dry-btn-padding-x, var(--dry-space-4));
524
553
  --_dry-btn-padding-y: var(--dry-btn-padding-y, var(--dry-space-2_5));
525
554
  --_dry-btn-font-size: var(
@@ -530,7 +559,7 @@
530
559
  min-height: var(--dry-btn-min-height, var(--dry-space-12));
531
560
  }
532
561
 
533
- :is(a, button)[data-size='lg'] {
562
+ [data-dry-button][data-size='lg'] {
534
563
  --_dry-btn-padding-x: var(--dry-btn-padding-x, var(--dry-space-6));
535
564
  --_dry-btn-padding-y: var(--dry-btn-padding-y, var(--dry-space-3));
536
565
  --_dry-btn-font-size: var(
@@ -543,7 +572,7 @@
543
572
 
544
573
  /* ── Icon-only sizes (square aspect ratio) ────────────────────────────── */
545
574
 
546
- :is(a, button)[data-size='icon'] {
575
+ [data-dry-button][data-size='icon'] {
547
576
  --_dry-btn-padding-x: var(--dry-btn-padding-x, 0);
548
577
  --_dry-btn-padding-y: var(--dry-btn-padding-y, 0);
549
578
  aspect-ratio: 1;
@@ -551,7 +580,7 @@
551
580
  --_dry-btn-radius: var(--dry-btn-radius, var(--dry-radius-md));
552
581
  }
553
582
 
554
- :is(a, button)[data-size='icon-sm'] {
583
+ [data-dry-button][data-size='icon-sm'] {
555
584
  --_dry-btn-padding-x: var(--dry-btn-padding-x, 0);
556
585
  --_dry-btn-padding-y: var(--dry-btn-padding-y, 0);
557
586
  aspect-ratio: 1;
@@ -563,7 +592,7 @@
563
592
  );
564
593
  }
565
594
 
566
- :is(a, button)[data-size='icon-lg'] {
595
+ [data-dry-button][data-size='icon-lg'] {
567
596
  --_dry-btn-padding-x: var(--dry-btn-padding-x, 0);
568
597
  --_dry-btn-padding-y: var(--dry-btn-padding-y, 0);
569
598
  aspect-ratio: 1;
@@ -577,47 +606,47 @@
577
606
 
578
607
  /* ── Button-group integration ─────────────────────────────────────── */
579
608
 
580
- .wrapper[data-in-group] :is(a, button) {
609
+ .wrapper[data-in-group] [data-dry-button] {
581
610
  border-radius: 0;
582
611
  }
583
612
 
584
613
  /* Horizontal: first child gets left radii */
585
- .wrapper[data-in-group][data-group-orientation='horizontal']:first-child :is(a, button) {
614
+ .wrapper[data-in-group][data-group-orientation='horizontal']:first-child [data-dry-button] {
586
615
  border-top-left-radius: var(--dry-button-group-radius);
587
616
  border-bottom-left-radius: var(--dry-button-group-radius);
588
617
  }
589
618
 
590
619
  /* Horizontal: last child gets right radii */
591
- .wrapper[data-in-group][data-group-orientation='horizontal']:last-child :is(a, button) {
620
+ .wrapper[data-in-group][data-group-orientation='horizontal']:last-child [data-dry-button] {
592
621
  border-top-right-radius: var(--dry-button-group-radius);
593
622
  border-bottom-right-radius: var(--dry-button-group-radius);
594
623
  }
595
624
 
596
625
  /* Horizontal: non-first child removes inline-start border */
597
- .wrapper[data-in-group][data-group-orientation='horizontal']:not(:first-child) :is(a, button) {
626
+ .wrapper[data-in-group][data-group-orientation='horizontal']:not(:first-child) [data-dry-button] {
598
627
  border-inline-start: 0;
599
628
  }
600
629
 
601
630
  /* Vertical: first child gets top radii */
602
- .wrapper[data-in-group][data-group-orientation='vertical']:first-child :is(a, button) {
631
+ .wrapper[data-in-group][data-group-orientation='vertical']:first-child [data-dry-button] {
603
632
  border-top-left-radius: var(--dry-button-group-radius);
604
633
  border-top-right-radius: var(--dry-button-group-radius);
605
634
  }
606
635
 
607
636
  /* Vertical: last child gets bottom radii */
608
- .wrapper[data-in-group][data-group-orientation='vertical']:last-child :is(a, button) {
637
+ .wrapper[data-in-group][data-group-orientation='vertical']:last-child [data-dry-button] {
609
638
  border-bottom-left-radius: var(--dry-button-group-radius);
610
639
  border-bottom-right-radius: var(--dry-button-group-radius);
611
640
  }
612
641
 
613
642
  /* Vertical: non-first child removes block-start border */
614
- .wrapper[data-in-group][data-group-orientation='vertical']:not(:first-child) :is(a, button) {
643
+ .wrapper[data-in-group][data-group-orientation='vertical']:not(:first-child) [data-dry-button] {
615
644
  border-block-start: 0;
616
645
  }
617
646
 
618
647
  /* Hover/focus z-index for grouped buttons */
619
- .wrapper[data-in-group]:hover :is(a, button),
620
- .wrapper[data-in-group]:focus-within :is(a, button) {
648
+ .wrapper[data-in-group]:hover [data-dry-button],
649
+ .wrapper[data-in-group]:focus-within [data-dry-button] {
621
650
  z-index: var(--dry-button-group-hover-z-index);
622
651
  position: relative;
623
652
  }
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import { createId } from '@dryui/primitives';
3
2
  import type { Snippet } from 'svelte';
4
3
  import type { HTMLAttributes } from 'svelte/elements';
5
4
 
@@ -11,7 +10,8 @@
11
10
 
12
11
  let { offset = 3, angle = 0, children, class: className, style, ...rest }: Props = $props();
13
12
 
14
- const filterId = createId('dry-chromatic');
13
+ const uid = $props.id();
14
+ const filterId = `dry-chromatic-${uid}`;
15
15
 
16
16
  const offsetX = $derived(Math.round(offset * Math.cos((angle * Math.PI) / 180)));
17
17
  const offsetY = $derived(Math.round(offset * Math.sin((angle * Math.PI) / 180)));
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { setCollapsibleCtx, createId } from './context.svelte.js';
4
+ import { setCollapsibleCtx } from './context.svelte.js';
5
5
 
6
6
  interface Props extends HTMLAttributes<HTMLDivElement> {
7
7
  open?: boolean;
@@ -17,7 +17,8 @@
17
17
  ...rest
18
18
  }: Props = $props();
19
19
 
20
- const contentId = createId('collapsible-content');
20
+ const uid = $props.id();
21
+ const contentId = `collapsible-content-${uid}`;
21
22
 
22
23
  setCollapsibleCtx({
23
24
  get open() {
@@ -1,4 +1,3 @@
1
- import { createId } from '@dryui/primitives';
2
1
  interface CollapsibleContext {
3
2
  readonly open: boolean;
4
3
  readonly disabled: boolean;
@@ -6,4 +5,4 @@ interface CollapsibleContext {
6
5
  toggle: () => void;
7
6
  }
8
7
  export declare const setCollapsibleCtx: (ctx: CollapsibleContext) => CollapsibleContext, getCollapsibleCtx: () => CollapsibleContext;
9
- export { createId };
8
+ export {};
@@ -1,3 +1,2 @@
1
- import { createContext, createId } from '@dryui/primitives';
1
+ import { createContext } from '@dryui/primitives';
2
2
  export const [setCollapsibleCtx, getCollapsibleCtx] = createContext('collapsible');
3
- export { createId };
@@ -59,6 +59,7 @@
59
59
  font-family: var(--dry-font-mono);
60
60
  color: var(--dry-color-text-strong);
61
61
  background: var(--dry-color-bg-raised);
62
+ /* dryui-allow solid-border-on-raised: numeric color channel inputs use field affordance inside a raised picker. */
62
63
  border: 1px solid var(--dry-color-stroke-strong);
63
64
  border-radius: var(--dry-radius-md);
64
65
  transition:
@@ -94,6 +94,7 @@
94
94
  max-content
95
95
  );
96
96
  background: var(--dry-color-bg-overlay);
97
+ /* dryui-allow solid-border-on-raised: popover listbox keeps a crisp edge for option scanning. */
97
98
  border: 1px solid var(--dry-color-stroke-weak);
98
99
  border-radius: var(--dry-radius-md);
99
100
  box-shadow: var(--dry-shadow-lg);
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import { generateFormId } from '@dryui/primitives';
4
3
  import { setComboboxCtx } from './context.svelte.js';
5
4
 
6
5
  interface Props {
@@ -19,8 +18,9 @@
19
18
  children
20
19
  }: Props = $props();
21
20
 
22
- const inputId = generateFormId('combobox-input');
23
- const contentId = generateFormId('combobox-content');
21
+ const uid = $props.id();
22
+ const inputId = `combobox-input-${uid}`;
23
+ const contentId = `combobox-content-${uid}`;
24
24
 
25
25
  let displayText = $state('');
26
26
  let inputValue = $state('');
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { generateFormId } from '@dryui/primitives';
5
4
  import { setCommandPaletteCtx } from './context.svelte.js';
6
5
 
7
6
  interface Props extends HTMLAttributes<HTMLDialogElement> {
@@ -11,8 +10,9 @@
11
10
 
12
11
  let { open = $bindable(false), class: className, children, ...rest }: Props = $props();
13
12
 
14
- const listId = generateFormId('cmd-list');
15
- const inputId = generateFormId('cmd-input');
13
+ const uid = $props.id();
14
+ const listId = `cmd-list-${uid}`;
15
+ const inputId = `cmd-input-${uid}`;
16
16
 
17
17
  let query = $state('');
18
18
  let activeItemId = $state('');
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { generateFormId } from '@dryui/primitives';
5
4
  import { getCommandPaletteCtx } from './context.svelte.js';
6
5
 
7
6
  interface Props extends HTMLAttributes<HTMLDivElement> {
@@ -14,7 +13,8 @@
14
13
  let { value, disabled, onSelect, class: className, children, ...rest }: Props = $props();
15
14
 
16
15
  const ctx = getCommandPaletteCtx();
17
- const id = generateFormId('cmd-item');
16
+ const uid = $props.id();
17
+ const id = `cmd-item-${uid}`;
18
18
 
19
19
  let visible = $derived.by(() => {
20
20
  const q = ctx.query.toLowerCase().trim();
@@ -58,7 +58,7 @@
58
58
 
59
59
  <style>
60
60
  [data-command-palette-item] {
61
- --dry-cmd-item-radius: var(--dry-radius-nested-dialog);
61
+ --dry-cmd-item-radius: min(var(--dry-radius-nested-dialog), var(--dry-space-4));
62
62
 
63
63
  display: grid;
64
64
  grid-auto-flow: column;
@@ -9,10 +9,12 @@
9
9
  }
10
10
 
11
11
  let { open = $bindable(false), children }: Props = $props();
12
+ const uid = $props.id();
12
13
 
13
14
  setContextMenuCtx(
14
15
  createPositionedMenuRootState({
15
16
  idBase: 'context-menu',
17
+ uid,
16
18
  getOpen: () => open,
17
19
  setOpen: (value) => {
18
20
  open = value;