@urbicon-ui/blocks 6.22.0 → 6.23.0

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.
@@ -4,7 +4,7 @@ export declare const stepperVariants: ((props?: {
4
4
  orientation?: "horizontal" | "vertical" | undefined;
5
5
  size?: "sm" | "md" | "lg" | undefined;
6
6
  variant?: "default" | "outlined" | "minimal" | undefined;
7
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
7
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
8
8
  clickable?: boolean | undefined;
9
9
  stepDisabled?: boolean | undefined;
10
10
  separatorComplete?: boolean | undefined;
@@ -14,7 +14,7 @@ export declare const stepperVariants: ((props?: {
14
14
  orientation?: "horizontal" | "vertical" | undefined;
15
15
  size?: "sm" | "md" | "lg" | undefined;
16
16
  variant?: "default" | "outlined" | "minimal" | undefined;
17
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
17
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
18
18
  clickable?: boolean | undefined;
19
19
  stepDisabled?: boolean | undefined;
20
20
  separatorComplete?: boolean | undefined;
@@ -26,7 +26,7 @@ export declare const stepperVariants: ((props?: {
26
26
  orientation?: "horizontal" | "vertical" | undefined;
27
27
  size?: "sm" | "md" | "lg" | undefined;
28
28
  variant?: "default" | "outlined" | "minimal" | undefined;
29
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
29
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
30
30
  clickable?: boolean | undefined;
31
31
  stepDisabled?: boolean | undefined;
32
32
  separatorComplete?: boolean | undefined;
@@ -38,7 +38,7 @@ export declare const stepperVariants: ((props?: {
38
38
  orientation?: "horizontal" | "vertical" | undefined;
39
39
  size?: "sm" | "md" | "lg" | undefined;
40
40
  variant?: "default" | "outlined" | "minimal" | undefined;
41
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
41
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
42
42
  clickable?: boolean | undefined;
43
43
  stepDisabled?: boolean | undefined;
44
44
  separatorComplete?: boolean | undefined;
@@ -50,7 +50,7 @@ export declare const stepperVariants: ((props?: {
50
50
  orientation?: "horizontal" | "vertical" | undefined;
51
51
  size?: "sm" | "md" | "lg" | undefined;
52
52
  variant?: "default" | "outlined" | "minimal" | undefined;
53
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
53
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
54
54
  clickable?: boolean | undefined;
55
55
  stepDisabled?: boolean | undefined;
56
56
  separatorComplete?: boolean | undefined;
@@ -62,7 +62,7 @@ export declare const stepperVariants: ((props?: {
62
62
  orientation?: "horizontal" | "vertical" | undefined;
63
63
  size?: "sm" | "md" | "lg" | undefined;
64
64
  variant?: "default" | "outlined" | "minimal" | undefined;
65
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
65
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
66
66
  clickable?: boolean | undefined;
67
67
  stepDisabled?: boolean | undefined;
68
68
  separatorComplete?: boolean | undefined;
@@ -74,7 +74,7 @@ export declare const stepperVariants: ((props?: {
74
74
  orientation?: "horizontal" | "vertical" | undefined;
75
75
  size?: "sm" | "md" | "lg" | undefined;
76
76
  variant?: "default" | "outlined" | "minimal" | undefined;
77
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
77
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
78
78
  clickable?: boolean | undefined;
79
79
  stepDisabled?: boolean | undefined;
80
80
  separatorComplete?: boolean | undefined;
@@ -86,7 +86,7 @@ export declare const stepperVariants: ((props?: {
86
86
  orientation?: "horizontal" | "vertical" | undefined;
87
87
  size?: "sm" | "md" | "lg" | undefined;
88
88
  variant?: "default" | "outlined" | "minimal" | undefined;
89
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
89
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
90
90
  clickable?: boolean | undefined;
91
91
  stepDisabled?: boolean | undefined;
92
92
  separatorComplete?: boolean | undefined;
@@ -98,7 +98,7 @@ export declare const stepperVariants: ((props?: {
98
98
  orientation?: "horizontal" | "vertical" | undefined;
99
99
  size?: "sm" | "md" | "lg" | undefined;
100
100
  variant?: "default" | "outlined" | "minimal" | undefined;
101
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
101
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
102
102
  clickable?: boolean | undefined;
103
103
  stepDisabled?: boolean | undefined;
104
104
  separatorComplete?: boolean | undefined;
@@ -110,7 +110,7 @@ export declare const stepperVariants: ((props?: {
110
110
  orientation?: "horizontal" | "vertical" | undefined;
111
111
  size?: "sm" | "md" | "lg" | undefined;
112
112
  variant?: "default" | "outlined" | "minimal" | undefined;
113
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
113
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
114
114
  clickable?: boolean | undefined;
115
115
  stepDisabled?: boolean | undefined;
116
116
  separatorComplete?: boolean | undefined;
@@ -122,7 +122,7 @@ export declare const stepperVariants: ((props?: {
122
122
  orientation?: "horizontal" | "vertical" | undefined;
123
123
  size?: "sm" | "md" | "lg" | undefined;
124
124
  variant?: "default" | "outlined" | "minimal" | undefined;
125
- stepState?: "complete" | "active" | "error" | "warning" | "inactive" | undefined;
125
+ stepState?: "error" | "warning" | "active" | "inactive" | "complete" | undefined;
126
126
  clickable?: boolean | undefined;
127
127
  stepDisabled?: boolean | undefined;
128
128
  separatorComplete?: boolean | undefined;
@@ -29,6 +29,7 @@
29
29
  slotClasses: slotClassesProp = {},
30
30
  preset,
31
31
  oninput: userOnInput,
32
+ 'aria-describedby': ariaDescribedby,
32
33
  ...restProps
33
34
  }: TextareaProps = $props();
34
35
 
@@ -50,6 +51,14 @@
50
51
  disabled
51
52
  }));
52
53
 
54
+ // Consumer-supplied `aria-describedby` (e.g. an external hint rendered
55
+ // outside the component) merges with the internal error/helper chain instead
56
+ // of replacing it — internal descriptions first, the consumer's supplemental
57
+ // one last (mirrors the Input role model, XC-2).
58
+ const describedBy = $derived(
59
+ [ff.describedBy, ariaDescribedby].filter(Boolean).join(' ') || undefined
60
+ );
61
+
53
62
  const charCount = $derived(typeof value === 'string' ? value.length : 0);
54
63
  const counterState = $derived.by(() => {
55
64
  if (!maxlength) return 'normal' as const;
@@ -145,7 +154,7 @@
145
154
  {readonly}
146
155
  {required}
147
156
  aria-invalid={ff.invalid ? 'true' : undefined}
148
- aria-describedby={ff.describedBy}
157
+ aria-describedby={describedBy}
149
158
  oninput={handleInput}></textarea>
150
159
 
151
160
  {#if showFooter}
@@ -56,6 +56,36 @@
56
56
  return toaster.registerSubscriber();
57
57
  });
58
58
 
59
+ // Sonner-style hover-to-pause. The pointer or keyboard focus being anywhere in
60
+ // the toaster region freezes the whole visible stack (all auto-dismiss timers
61
+ // + the progress bars) and leaving resumes each from its remaining time.
62
+ //
63
+ // These are bubbling over/out + focusin/focusout, NOT pointerenter/leave: the
64
+ // container is `pointer-events-none` (so clicks fall through the gaps between
65
+ // toasts to the page), which means it never becomes a pointer target itself —
66
+ // only its `pointer-events-auto` toast children do. enter/leave don't bubble
67
+ // and so would never fire here; over/out bubble up from the toasts. The
68
+ // `relatedTarget` containment check collapses the noisy per-descendant out/blur
69
+ // events into a single "the pointer/focus actually left the region" signal.
70
+ let pointerInside = $state(false);
71
+ let focusInside = $state(false);
72
+
73
+ $effect(() => {
74
+ if (pointerInside || focusInside) toaster.pause();
75
+ else toaster.resume();
76
+ });
77
+
78
+ // Safety net: if the Toaster unmounts while the stack is frozen (pointer/focus
79
+ // was still inside), un-freeze the singleton store so its timers aren't stranded.
80
+ $effect(() => {
81
+ return () => toaster.resume();
82
+ });
83
+
84
+ function leftRegion(event: PointerEvent | FocusEvent) {
85
+ const next = event.relatedTarget as Node | null;
86
+ return !(event.currentTarget as HTMLElement).contains(next);
87
+ }
88
+
59
89
  const visibleToasts = $derived(toaster.toasts.slice(-max));
60
90
 
61
91
  const styles = $derived(
@@ -124,6 +154,14 @@
124
154
  class={[slot('container'), className].filter(Boolean).join(' ')}
125
155
  aria-live="polite"
126
156
  aria-relevant="additions removals"
157
+ onpointerover={() => (pointerInside = true)}
158
+ onpointerout={(e) => {
159
+ if (leftRegion(e)) pointerInside = false;
160
+ }}
161
+ onfocusin={() => (focusInside = true)}
162
+ onfocusout={(e) => {
163
+ if (leftRegion(e)) focusInside = false;
164
+ }}
127
165
  {...restProps}
128
166
  >
129
167
  {#each visibleToasts as toast (toast.id)}
@@ -177,10 +215,16 @@
177
215
  </button>
178
216
  {/if}
179
217
 
180
- {#if toast.showProgress && toast.duration > 0}
218
+ {#if toast.showProgress && Number.isFinite(toast.duration) && toast.duration > 0}
219
+ <!-- Duration comes from the toast itself (the real countdown), not a token.
220
+ `animation-play-state` tracks the store's paused flag so the bar freezes
221
+ with the timer on hover/focus and resumes in place — changing only the
222
+ play-state never restarts the animation. -->
181
223
  <div
182
224
  class={slot('progress', toast.intent)}
183
- style="animation: blocks-toast-progress {toast.duration}ms linear forwards;"
225
+ style="animation: blocks-toast-progress {toast.duration}ms linear forwards; animation-play-state: {toaster.paused
226
+ ? 'paused'
227
+ : 'running'};"
184
228
  ></div>
185
229
  {/if}
186
230
  </div>
@@ -14,6 +14,13 @@ import type { ToastPlacement } from './toast.variants.js';
14
14
  declare class ToastStore {
15
15
  toasts: ToastData[];
16
16
  placement: ToastPlacement;
17
+ /**
18
+ * Whether the whole visible stack is currently frozen because the pointer or
19
+ * keyboard focus is inside the toaster region. Drives each progress bar's
20
+ * `animation-play-state`; the auto-dismiss timers freeze together with it. Set
21
+ * by `<Toaster>` via {@link pause} / {@link resume}.
22
+ */
23
+ paused: boolean;
17
24
  private timers;
18
25
  private subscriberCount;
19
26
  private hasWarnedNoSubscriber;
@@ -22,6 +29,15 @@ declare class ToastStore {
22
29
  * the store knows whether a renderer is present. Returns an unsubscribe fn.
23
30
  */
24
31
  registerSubscriber(): () => void;
32
+ /**
33
+ * Arm the auto-dismiss timer for `id` — or, if the stack is currently paused,
34
+ * pre-register it frozen so it starts counting when the stack resumes.
35
+ * Persistent toasts (`duration <= 0` or non-finite, e.g. `Infinity`, and the
36
+ * loading leg of a promise toast) get no timer at all.
37
+ */
38
+ private armTimer;
39
+ /** Cancel and forget the timer for `id` (if any). */
40
+ private clearTimer;
25
41
  /** Create a toast with full control over all options. Returns the toast ID for programmatic dismissal. */
26
42
  add(input: ToastInput): string;
27
43
  /**
@@ -50,8 +66,23 @@ declare class ToastStore {
50
66
  promise<T>(promise: Promise<T>, opts: ToastPromiseOptions<T>): string;
51
67
  /** Remove a single toast by ID. Clears its auto-dismiss timer. */
52
68
  dismiss(id: string): void;
53
- /** Remove all toasts and cancel their timers. */
69
+ /** Remove all toasts, cancel their timers, and un-freeze the stack. */
54
70
  clear(): void;
71
+ /**
72
+ * Freeze the whole visible stack (Sonner-style hover-to-pause): cancel every
73
+ * running auto-dismiss timer, banking the time each has left, and flag
74
+ * {@link paused} so the progress bars stop. Idempotent — `<Toaster>` calls it
75
+ * on pointer-enter / focus-in of the region. Persistent toasts and a promise's
76
+ * loading leg have no timer and are untouched; the banked remainders are
77
+ * restarted by {@link resume}.
78
+ */
79
+ pause(): void;
80
+ /**
81
+ * Resume the stack: restart every frozen timer from its banked remaining time
82
+ * (not from the full duration) and clear {@link paused} so the progress bars
83
+ * run again. Idempotent — called on pointer-leave / focus-out of the region.
84
+ */
85
+ resume(): void;
55
86
  /** Shorthand for `add()` with `intent: 'info'`. Shows an info icon. */
56
87
  info(title: string, opts?: ToastShorthandOpts): string;
57
88
  /** Shorthand for `add()` with `intent: 'success'`. Shows a check icon. */
@@ -16,6 +16,13 @@ function uid() {
16
16
  class ToastStore {
17
17
  toasts = $state([]);
18
18
  placement = $state('bottom-right');
19
+ /**
20
+ * Whether the whole visible stack is currently frozen because the pointer or
21
+ * keyboard focus is inside the toaster region. Drives each progress bar's
22
+ * `animation-play-state`; the auto-dismiss timers freeze together with it. Set
23
+ * by `<Toaster>` via {@link pause} / {@link resume}.
24
+ */
25
+ paused = $state(false);
19
26
  timers = new Map();
20
27
  subscriberCount = 0;
21
28
  hasWarnedNoSubscriber = false;
@@ -31,6 +38,34 @@ class ToastStore {
31
38
  this.subscriberCount = Math.max(0, this.subscriberCount - 1);
32
39
  };
33
40
  }
41
+ /**
42
+ * Arm the auto-dismiss timer for `id` — or, if the stack is currently paused,
43
+ * pre-register it frozen so it starts counting when the stack resumes.
44
+ * Persistent toasts (`duration <= 0` or non-finite, e.g. `Infinity`, and the
45
+ * loading leg of a promise toast) get no timer at all.
46
+ */
47
+ armTimer(id, duration) {
48
+ if (!Number.isFinite(duration) || duration <= 0)
49
+ return;
50
+ if (this.paused) {
51
+ // A toast that arrives while the user is hovering the stack starts frozen;
52
+ // resume() lights its timer once the pointer/focus leaves.
53
+ this.timers.set(id, { handle: undefined, remaining: duration, startedAt: 0 });
54
+ return;
55
+ }
56
+ this.timers.set(id, {
57
+ handle: setTimeout(() => this.dismiss(id), duration),
58
+ remaining: duration,
59
+ startedAt: Date.now()
60
+ });
61
+ }
62
+ /** Cancel and forget the timer for `id` (if any). */
63
+ clearTimer(id) {
64
+ const entry = this.timers.get(id);
65
+ if (entry?.handle !== undefined)
66
+ clearTimeout(entry.handle);
67
+ this.timers.delete(id);
68
+ }
34
69
  /** Create a toast with full control over all options. Returns the toast ID for programmatic dismissal. */
35
70
  add(input) {
36
71
  if (this.subscriberCount === 0 &&
@@ -56,9 +91,7 @@ class ToastStore {
56
91
  loading: input.loading ?? false
57
92
  };
58
93
  this.toasts = [...this.toasts, toast];
59
- if (toast.duration > 0) {
60
- this.timers.set(id, setTimeout(() => this.dismiss(id), toast.duration));
61
- }
94
+ this.armTimer(id, toast.duration);
62
95
  return id;
63
96
  }
64
97
  /**
@@ -73,14 +106,9 @@ class ToastStore {
73
106
  return;
74
107
  const next = { ...this.toasts[idx], ...input };
75
108
  this.toasts = [...this.toasts.slice(0, idx), next, ...this.toasts.slice(idx + 1)];
76
- const timer = this.timers.get(id);
77
- if (timer) {
78
- clearTimeout(timer);
79
- this.timers.delete(id);
80
- }
81
- if (next.duration > 0) {
82
- this.timers.set(id, setTimeout(() => this.dismiss(id), next.duration));
83
- }
109
+ // Reset the auto-dismiss clock to the new duration (respecting a current pause).
110
+ this.clearTimer(id);
111
+ this.armTimer(id, next.duration);
84
112
  }
85
113
  /**
86
114
  * Drive a toast through a promise's lifecycle (Sonner-style). Shows a
@@ -150,19 +178,56 @@ class ToastStore {
150
178
  }
151
179
  /** Remove a single toast by ID. Clears its auto-dismiss timer. */
152
180
  dismiss(id) {
153
- const timer = this.timers.get(id);
154
- if (timer) {
155
- clearTimeout(timer);
156
- this.timers.delete(id);
157
- }
181
+ this.clearTimer(id);
158
182
  this.toasts = this.toasts.filter((t) => t.id !== id);
159
183
  }
160
- /** Remove all toasts and cancel their timers. */
184
+ /** Remove all toasts, cancel their timers, and un-freeze the stack. */
161
185
  clear() {
162
- for (const timer of this.timers.values())
163
- clearTimeout(timer);
186
+ for (const entry of this.timers.values()) {
187
+ if (entry.handle !== undefined)
188
+ clearTimeout(entry.handle);
189
+ }
164
190
  this.timers.clear();
165
191
  this.toasts = [];
192
+ this.paused = false;
193
+ }
194
+ /**
195
+ * Freeze the whole visible stack (Sonner-style hover-to-pause): cancel every
196
+ * running auto-dismiss timer, banking the time each has left, and flag
197
+ * {@link paused} so the progress bars stop. Idempotent — `<Toaster>` calls it
198
+ * on pointer-enter / focus-in of the region. Persistent toasts and a promise's
199
+ * loading leg have no timer and are untouched; the banked remainders are
200
+ * restarted by {@link resume}.
201
+ */
202
+ pause() {
203
+ if (this.paused)
204
+ return;
205
+ this.paused = true;
206
+ const now = Date.now();
207
+ for (const entry of this.timers.values()) {
208
+ if (entry.handle === undefined)
209
+ continue;
210
+ clearTimeout(entry.handle);
211
+ entry.remaining = Math.max(0, entry.remaining - (now - entry.startedAt));
212
+ entry.handle = undefined;
213
+ }
214
+ }
215
+ /**
216
+ * Resume the stack: restart every frozen timer from its banked remaining time
217
+ * (not from the full duration) and clear {@link paused} so the progress bars
218
+ * run again. Idempotent — called on pointer-leave / focus-out of the region.
219
+ */
220
+ resume() {
221
+ if (!this.paused)
222
+ return;
223
+ this.paused = false;
224
+ const now = Date.now();
225
+ for (const [id, entry] of this.timers) {
226
+ if (entry.handle !== undefined)
227
+ continue;
228
+ entry.startedAt = now;
229
+ entry.handle = setTimeout(() => this.dismiss(id), entry.remaining);
230
+ }
166
231
  }
167
232
  /** Shorthand for `add()` with `intent: 'info'`. Shows an info icon. */
168
233
  info(title, opts) {
@@ -1,70 +1,70 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
2
  export declare const toastVariants: ((props?: {
3
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
3
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
4
4
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
5
5
  } | undefined) => {
6
6
  container: (props?: ({
7
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
7
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
8
8
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
9
9
  } & {
10
10
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
11
11
  }) | undefined) => string;
12
12
  toast: (props?: ({
13
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
13
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
14
14
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
15
15
  } & {
16
16
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
17
17
  }) | undefined) => string;
18
18
  icon: (props?: ({
19
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
19
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
20
20
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
21
21
  } & {
22
22
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
23
23
  }) | undefined) => string;
24
24
  content: (props?: ({
25
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
25
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
26
26
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
27
27
  } & {
28
28
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
29
29
  }) | undefined) => string;
30
30
  title: (props?: ({
31
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
31
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
32
32
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
33
33
  } & {
34
34
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
35
35
  }) | undefined) => string;
36
36
  description: (props?: ({
37
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
37
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
38
38
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
39
39
  } & {
40
40
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
41
41
  }) | undefined) => string;
42
42
  actions: (props?: ({
43
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
43
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
44
44
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
45
45
  } & {
46
46
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
47
47
  }) | undefined) => string;
48
48
  actionButton: (props?: ({
49
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
49
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
50
50
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
51
51
  } & {
52
52
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
53
53
  }) | undefined) => string;
54
54
  cancelButton: (props?: ({
55
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
55
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
56
56
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
57
57
  } & {
58
58
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
59
59
  }) | undefined) => string;
60
60
  dismissButton: (props?: ({
61
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
61
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
62
62
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
63
63
  } & {
64
64
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
65
65
  }) | undefined) => string;
66
66
  progress: (props?: ({
67
- intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
67
+ intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
68
68
  placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
69
69
  } & {
70
70
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
@@ -28,6 +28,7 @@
28
28
  slotClasses: slotClassesProp = {},
29
29
  preset,
30
30
  withBorder = false,
31
+ 'aria-describedby': ariaDescribedby,
31
32
  ...restProps
32
33
  }: ToggleProps = $props();
33
34
 
@@ -42,6 +43,14 @@
42
43
  disabled
43
44
  }));
44
45
 
46
+ // Consumer-supplied `aria-describedby` (e.g. an external hint rendered
47
+ // outside the component) merges with the internal error/helper chain instead
48
+ // of replacing it — internal descriptions first, the consumer's supplemental
49
+ // one last (mirrors the Input role model, XC-2).
50
+ const describedBy = $derived(
51
+ [ff.describedBy, ariaDescribedby].filter(Boolean).join(' ') || undefined
52
+ );
53
+
45
54
  const blocksConfig = getBlocksConfig();
46
55
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
47
56
 
@@ -113,7 +122,7 @@
113
122
  class="peer sr-only"
114
123
  aria-checked={checked}
115
124
  aria-label={label ? undefined : bt('accessibility.toggle') || 'Toggle'}
116
- aria-describedby={ff.describedBy}
125
+ aria-describedby={describedBy}
117
126
  aria-invalid={ff.invalid ? 'true' : undefined}
118
127
  onchange={handleChange}
119
128
  {...restProps}
@@ -1,19 +1,19 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
2
  export declare const tooltipVariants: ((props?: {
3
3
  open?: boolean | undefined;
4
- intent?: "info" | "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
4
+ intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
5
5
  size?: "sm" | "md" | "lg" | undefined;
6
6
  } | undefined) => {
7
7
  base: (props?: ({
8
8
  open?: boolean | undefined;
9
- intent?: "info" | "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
9
+ intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
10
10
  size?: "sm" | "md" | "lg" | undefined;
11
11
  } & {
12
12
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
13
13
  }) | undefined) => string;
14
14
  arrow: (props?: ({
15
15
  open?: boolean | undefined;
16
- intent?: "info" | "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
16
+ intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
17
17
  size?: "sm" | "md" | "lg" | undefined;
18
18
  } & {
19
19
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
@@ -153,7 +153,7 @@
153
153
  --color-chart-6: light-dark(var(--color-danger-500), var(--color-danger-400));
154
154
 
155
155
  /* === BORDERS ===
156
- border-hairline (Lighter-Refactor) is the quietest tier — structural
156
+ border-hairline is the quietest tier — structural
157
157
  trennlinien (row dividers, Card header/footer separators, inline
158
158
  section dividers). Sits below border-subtle, which is now reserved
159
159
  for quiet container outlines (e. g. Popover). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/blocks",
3
- "version": "6.22.0",
3
+ "version": "6.23.0",
4
4
  "description": "Svelte 5 UI component library with Tailwind CSS 4, OKLCH design tokens and zero runtime dependencies",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -91,8 +91,8 @@
91
91
  "@sveltejs/package": "^2.5.8",
92
92
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
93
93
  "@tailwindcss/vite": "^4.3.1",
94
- "@urbicon-ui/i18n": "6.22.0",
95
- "@urbicon-ui/shared-types": "6.22.0",
94
+ "@urbicon-ui/i18n": "6.23.0",
95
+ "@urbicon-ui/shared-types": "6.23.0",
96
96
  "prettier": "^3.8.4",
97
97
  "prettier-plugin-svelte": "^4.1.1",
98
98
  "prettier-plugin-tailwindcss": "^0.8.0",