@juspay/svelte-ui-components 4.31.3 → 4.32.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.
Files changed (44) hide show
  1. package/dist/Accordion/Accordion.svelte +6 -1
  2. package/dist/Animations/ModalAnimation.svelte +71 -8
  3. package/dist/Animations/OverlayAnimation.svelte +29 -4
  4. package/dist/Animations/tokenizedFly.d.ts +17 -0
  5. package/dist/Animations/tokenizedFly.js +153 -0
  6. package/dist/AreaChart/AreaChart.svelte +7 -5
  7. package/dist/BarChart/BarChart.svelte +2 -1
  8. package/dist/Book/Book.svelte +6 -3
  9. package/dist/ChatBubble/ChatBubble.svelte +18 -3
  10. package/dist/Checkbox/Checkbox.svelte +15 -3
  11. package/dist/Choicebox/Choicebox.svelte +24 -2
  12. package/dist/Combobox/Combobox.svelte +15 -1
  13. package/dist/CommandMenu/CommandMenu.svelte +72 -2
  14. package/dist/ContextMenu/ContextMenu.svelte +41 -1
  15. package/dist/DualAxisBarChart/DualAxisBarChart.svelte +4 -3
  16. package/dist/FunnelChart/FunnelChart.svelte +5 -4
  17. package/dist/Gallery/Gallery.svelte +53 -1
  18. package/dist/Gallery/properties.d.ts +5 -3
  19. package/dist/Gauge/Gauge.svelte +2 -1
  20. package/dist/LineChart/LineChart.svelte +7 -5
  21. package/dist/MediaPlayer/MediaPlayer.svelte +9 -1
  22. package/dist/MediaPlayer/MediaPlayer.svelte.d.ts +1 -1
  23. package/dist/MediaUpload/MediaUpload.svelte +1 -1
  24. package/dist/Menu/Menu.svelte +38 -1
  25. package/dist/Modal/Modal.svelte +18 -4
  26. package/dist/Pagination/Pagination.svelte +9 -1
  27. package/dist/PieChart/PieChart.svelte +2 -2
  28. package/dist/Radio/Radio.svelte +14 -2
  29. package/dist/RatingGroup/RatingGroup.svelte +9 -0
  30. package/dist/SankeyChart/SankeyChart.svelte +6 -3
  31. package/dist/Select/Select.svelte +26 -4
  32. package/dist/Sheet/Sheet.svelte +34 -2
  33. package/dist/Slider/Slider.svelte +26 -5
  34. package/dist/Table/Table.svelte +9 -2
  35. package/dist/Tabs/Tabs.svelte +27 -4
  36. package/dist/TaskList/TaskList.svelte +10 -2
  37. package/dist/ThemeSwitcher/ThemeSwitcher.svelte +13 -6
  38. package/dist/ThinkingIndicator/ThinkingIndicator.svelte +5 -3
  39. package/dist/Toast/Toast.svelte +216 -116
  40. package/dist/Toggle/Toggle.svelte +8 -4
  41. package/dist/ToolCallLog/ToolCallLog.svelte +10 -2
  42. package/dist/Tooltip/Tooltip.svelte +4 -2
  43. package/dist-wc/index.js +599 -410
  44. package/package.json +1 -1
@@ -99,7 +99,12 @@
99
99
  the panel stretched (or centered, etc.) could not otherwise override
100
100
  this one declaration without reaching for :global() or !important. */
101
101
  align-self: var(--accordion-align-self, start);
102
- transition: grid-template-rows var(--accordion-transition, 0.2s ease-out);
102
+ transition: grid-template-rows
103
+ var(
104
+ --accordion-transition,
105
+ var(--accordion-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
106
+ var(--accordion-transition-easing, var(--ease-out, var(--motion-easing, ease-out)))
107
+ );
103
108
  }
104
109
 
105
110
  .accordion.expanded {
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import { fly, fade } from 'svelte/transition';
3
+ import { linear } from 'svelte/easing';
4
+ import { tokenizedFly } from './tokenizedFly';
4
5
  import { prefersReducedMotion } from '../utils';
5
6
  import type { ModalAlign, ModalEntryAnimation } from '../Modal/properties';
6
7
  import type { ModalTransition } from '../types';
@@ -31,7 +32,11 @@
31
32
  // paints the modal 300px off-target on its first frame and then snaps. Returning
32
33
  // `base` unchanged is exactly "appear in place" -- it already has x and y at 0 --
33
34
  // so only the duration needs zeroing alongside it. These values feed
34
- // `in:fly|global={...}`, which no stylesheet can reach, so the guard is here.
35
+ // `in:tokenizedFly|global={...}`: x/y at 0 stay exactly 0 regardless of any
36
+ // distance token (tokenizedFly preserves a 0 rather than resizing it). A
37
+ // consumer-set duration token can no longer revive motion here either --
38
+ // tokenizedFly's own reduced-motion backstop (reducedMotion.current) clamps
39
+ // duration to near-zero regardless of what any token chain resolves to.
35
40
  if (prefersReducedMotion()) {
36
41
  return { ...base, duration: 0 };
37
42
  }
@@ -61,6 +66,64 @@
61
66
  // rather than argue the distinction at every call site.
62
67
  let fadeAnimationProperties = $derived({ duration: prefersReducedMotion() ? 0 : 300 });
63
68
 
69
+ // Open/close get distinct duration tokens (the literal fallback stays 380 both
70
+ // ways, matching flyAnimationProperties today) so a consumer can set an
71
+ // asymmetric close duration without this component inventing that gap itself.
72
+ // The fade-shaped branches below reuse the same token names: exactly one of
73
+ // the four template branches renders per instance, so "content-open" means
74
+ // the same thing to a consumer whether this instance flies or fades in.
75
+ // Each token array is element-token -> --motion-duration/-easing root, skipping
76
+ // a named tier: 380ms/300ms don't match any named duration tier, consistent
77
+ // with every other agent in this batch skipping non-matching literals rather
78
+ // than forcing one. --distance-overlay is the named tier both fly branches
79
+ // share (60px, this batch's Modal/Sheet travel-distance reduction).
80
+ let flyInParams = $derived({
81
+ x: flyAnimationProperties.x,
82
+ y: flyAnimationProperties.y,
83
+ durationTokens: ['--modal-content-open-transition-duration', '--motion-duration'],
84
+ fallbackDuration: flyAnimationProperties.duration,
85
+ distanceTokens: ['--modal-content-open-transition-distance', '--distance-overlay'],
86
+ fallbackDistance: 60,
87
+ easingTokens: ['--modal-content-open-transition-easing', '--ease-smooth-out', '--motion-easing']
88
+ });
89
+ let flyOutParams = $derived({
90
+ x: flyAnimationProperties.x,
91
+ y: flyAnimationProperties.y,
92
+ durationTokens: ['--modal-content-close-transition-duration', '--motion-duration'],
93
+ fallbackDuration: flyAnimationProperties.duration,
94
+ distanceTokens: ['--modal-content-close-transition-distance', '--distance-overlay'],
95
+ fallbackDistance: 60,
96
+ easingTokens: [
97
+ '--modal-content-close-transition-easing',
98
+ '--ease-smooth-out',
99
+ '--motion-easing'
100
+ ]
101
+ });
102
+ // No distanceTokens: these branches are opacity-only, same as fade's own
103
+ // params today. `easing` stays the ultimate JS fallback -- tokenizedFly
104
+ // otherwise defaults to cubicOut (fly's default), fade's own default is
105
+ // linear -- used only if no easingTokens entry resolves either.
106
+ let fadeInParams = $derived({
107
+ durationTokens: ['--modal-content-open-transition-duration', '--motion-duration'],
108
+ fallbackDuration: fadeAnimationProperties.duration,
109
+ easingTokens: [
110
+ '--modal-content-open-transition-easing',
111
+ '--ease-smooth-out',
112
+ '--motion-easing'
113
+ ],
114
+ easing: linear
115
+ });
116
+ let fadeOutParams = $derived({
117
+ durationTokens: ['--modal-content-close-transition-duration', '--motion-duration'],
118
+ fallbackDuration: fadeAnimationProperties.duration,
119
+ easingTokens: [
120
+ '--modal-content-close-transition-easing',
121
+ '--ease-smooth-out',
122
+ '--motion-easing'
123
+ ],
124
+ easing: linear
125
+ });
126
+
64
127
  let useFlyAnimation = $derived(
65
128
  entryAnimation != null ? entryAnimation !== 'fade' : align === 'top' || align === 'bottom'
66
129
  );
@@ -70,8 +133,8 @@
70
133
  {#if enable}
71
134
  {#if useFlyAnimation && useOutTransition}
72
135
  <div
73
- in:fly|global={flyAnimationProperties}
74
- out:fly|global={flyAnimationProperties}
136
+ in:tokenizedFly|global={flyInParams}
137
+ out:tokenizedFly|global={flyOutParams}
75
138
  data-pw={typeof testId === 'string' ? testId : null}
76
139
  testID={typeof testId === 'string' ? testId : null}
77
140
  >
@@ -79,7 +142,7 @@
79
142
  </div>
80
143
  {:else if useFlyAnimation}
81
144
  <div
82
- in:fly|global={flyAnimationProperties}
145
+ in:tokenizedFly|global={flyInParams}
83
146
  data-pw={typeof testId === 'string' ? testId : null}
84
147
  testID={typeof testId === 'string' ? testId : null}
85
148
  >
@@ -87,8 +150,8 @@
87
150
  </div>
88
151
  {:else if useOutTransition}
89
152
  <div
90
- in:fade|global={fadeAnimationProperties}
91
- out:fade|global={fadeAnimationProperties}
153
+ in:tokenizedFly|global={fadeInParams}
154
+ out:tokenizedFly|global={fadeOutParams}
92
155
  data-pw={typeof testId === 'string' ? testId : null}
93
156
  testID={typeof testId === 'string' ? testId : null}
94
157
  >
@@ -96,7 +159,7 @@
96
159
  </div>
97
160
  {:else}
98
161
  <div
99
- in:fade|global={fadeAnimationProperties}
162
+ in:tokenizedFly|global={fadeInParams}
100
163
  data-pw={typeof testId === 'string' ? testId : null}
101
164
  testID={typeof testId === 'string' ? testId : null}
102
165
  >
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
- import { fade } from 'svelte/transition';
2
+ import { linear } from 'svelte/easing';
3
3
  import type { Snippet } from 'svelte';
4
+ import { tokenizedFly } from './tokenizedFly';
4
5
 
5
6
  type Props = {
6
7
  children?: Snippet;
@@ -10,6 +11,30 @@
10
11
  };
11
12
 
12
13
  let { children, testId, fadeIn = false }: Props = $props();
14
+
15
+ // Opacity-only, so no distanceTokens; easing is explicit because tokenizedFly
16
+ // otherwise defaults to cubicOut (fly's default) -- fade's own default is
17
+ // linear, and this replaces a fade. No reduced-motion guard here, matching
18
+ // today's fade calls below, which never had one either. No named duration
19
+ // tier: this component's easing was deliberately left untokenized (never
20
+ // part of the original brief -- a pure background-scrim fade doesn't
21
+ // warrant a new, unvalidated easing decision), and 350ms wasn't treated as
22
+ // evidence for a recurring named tier on its own. The chain still ends at
23
+ // --motion-duration, though: Overlay mounts in lockstep with ModalAnimation
24
+ // (both are children of the same Modal), so leaving the root out would let
25
+ // a consumer's "everything N% faster" override speed up the content panel
26
+ // while the backdrop fade stayed put -- a visibly half-tokenized common case,
27
+ // not a scope boundary worth preserving.
28
+ const openParams = {
29
+ durationTokens: ['--modal-overlay-open-transition-duration', '--motion-duration'],
30
+ fallbackDuration: 350,
31
+ easing: linear
32
+ };
33
+ const closeParams = {
34
+ durationTokens: ['--modal-overlay-close-transition-duration', '--motion-duration'],
35
+ fallbackDuration: 350,
36
+ easing: linear
37
+ };
13
38
  </script>
14
39
 
15
40
  {#if fadeIn}
@@ -20,8 +45,8 @@
20
45
  this prop exists for (see docs/OverlayAnimation.md). `|global` makes
21
46
  the intro play on creation regardless. -->
22
47
  <div
23
- in:fade|global={{ duration: 350 }}
24
- out:fade={{ duration: 350 }}
48
+ in:tokenizedFly|global={openParams}
49
+ out:tokenizedFly={closeParams}
25
50
  data-pw={typeof testId === 'string' ? testId : null}
26
51
  testID={typeof testId === 'string' ? testId : null}
27
52
  >
@@ -29,7 +54,7 @@
29
54
  </div>
30
55
  {:else}
31
56
  <div
32
- out:fade={{ duration: 350 }}
57
+ out:tokenizedFly={closeParams}
33
58
  data-pw={typeof testId === 'string' ? testId : null}
34
59
  testID={typeof testId === 'string' ? testId : null}
35
60
  >
@@ -0,0 +1,17 @@
1
+ import type { EasingFunction, TransitionConfig } from 'svelte/transition';
2
+ export declare function parseCssEasing(raw: string): EasingFunction | null;
3
+ export type TokenizedFlyParams = {
4
+ x?: number;
5
+ y?: number;
6
+ durationTokens: string[];
7
+ fallbackDuration: number;
8
+ distanceTokens?: string[];
9
+ fallbackDistance?: number;
10
+ easingTokens?: string[];
11
+ easing?: EasingFunction;
12
+ };
13
+ export type ResolvedTokenizedFly = TransitionConfig & {
14
+ css: NonNullable<TransitionConfig['css']>;
15
+ easing: NonNullable<TransitionConfig['easing']>;
16
+ };
17
+ export declare function tokenizedFly(node: Element, params: TokenizedFlyParams): ResolvedTokenizedFly;
@@ -0,0 +1,153 @@
1
+ import { cubicOut } from 'svelte/easing';
2
+ import { reducedMotion } from '../reduced-motion.svelte';
3
+ // `Number.parseFloat` on a unit with no numeric prefix (a malformed token
4
+ // value like `--foo-duration: s;`, not just an unset one) returns NaN, and
5
+ // `??` does not catch NaN the way it catches null/undefined -- Number.isFinite
6
+ // is what keeps a typo'd token from flowing straight through as a duration or
7
+ // distance instead of falling back the way an unset token already does.
8
+ const parseCssTime = (raw) => {
9
+ const value = raw.trim();
10
+ if (value.endsWith('ms')) {
11
+ const amount = Number.parseFloat(value);
12
+ return Number.isFinite(amount) ? amount : null;
13
+ }
14
+ if (value.endsWith('s')) {
15
+ const amount = Number.parseFloat(value);
16
+ return Number.isFinite(amount) ? amount * 1000 : null;
17
+ }
18
+ return null;
19
+ };
20
+ const parseCssLength = (raw) => {
21
+ const value = raw.trim();
22
+ if (!value.endsWith('px')) {
23
+ return null;
24
+ }
25
+ const amount = Number.parseFloat(value);
26
+ return Number.isFinite(amount) ? amount : null;
27
+ };
28
+ const CSS_KEYWORD_EASINGS = {
29
+ ease: [0.25, 0.1, 0.25, 1],
30
+ 'ease-in': [0.42, 0, 1, 1],
31
+ 'ease-out': [0, 0, 0.58, 1],
32
+ 'ease-in-out': [0.42, 0, 0.58, 1]
33
+ };
34
+ // Standard WebKit UnitBezier approach: express the Bezier x(u)/y(u) curves in
35
+ // polynomial form, Newton-Raphson solve x(u) = x for u, then evaluate y(u).
36
+ // Verified against a published reference value for CSS `ease` and against an
37
+ // independent brute-force (dense sampling) implementation before this was
38
+ // wired into any live component -- both matched to 6+ significant figures.
39
+ function cubicBezierEasing(x1, y1, x2, y2) {
40
+ const a = (n1, n2) => 1 - 3 * n2 + 3 * n1;
41
+ const b = (n1, n2) => 3 * n2 - 6 * n1;
42
+ const c = (n1) => 3 * n1;
43
+ const calc = (t, n1, n2) => ((a(n1, n2) * t + b(n1, n2)) * t + c(n1)) * t;
44
+ const slope = (t, n1, n2) => 3 * a(n1, n2) * t * t + 2 * b(n1, n2) * t + c(n1);
45
+ const solveT = (x) => {
46
+ let t = x;
47
+ for (let i = 0; i < 8; i += 1) {
48
+ const s = slope(t, x1, x2);
49
+ if (Math.abs(s) < 1e-6) {
50
+ break;
51
+ }
52
+ t -= (calc(t, x1, x2) - x) / s;
53
+ }
54
+ return t;
55
+ };
56
+ return (x) => {
57
+ if (x <= 0) {
58
+ return 0;
59
+ }
60
+ if (x >= 1) {
61
+ return 1;
62
+ }
63
+ return calc(solveT(x), y1, y2);
64
+ };
65
+ }
66
+ const BEZIER_PATTERN = /^cubic-bezier\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)$/;
67
+ export function parseCssEasing(raw) {
68
+ const value = raw.trim();
69
+ if (value === '') {
70
+ return null;
71
+ }
72
+ if (value === 'linear') {
73
+ return (t) => t;
74
+ }
75
+ const known = CSS_KEYWORD_EASINGS[value];
76
+ if (known) {
77
+ return cubicBezierEasing(...known);
78
+ }
79
+ const match = BEZIER_PATTERN.exec(value);
80
+ if (!match) {
81
+ return null;
82
+ }
83
+ const [x1, y1, x2, y2] = match.slice(1).map(Number.parseFloat);
84
+ if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) {
85
+ return null;
86
+ } // invalid per CSS spec: not a function of x
87
+ return cubicBezierEasing(x1, y1, x2, y2);
88
+ }
89
+ // Consumers read tokens via getComputedStyle rather than CSS var(), which has
90
+ // no native cascade -- this walks an element-token -> named-tier -> root-token
91
+ // fallback chain manually, returning the first token that actually resolves.
92
+ const resolveToken = (styles, tokens) => {
93
+ for (const name of tokens) {
94
+ const raw = styles.getPropertyValue(name).trim();
95
+ if (raw !== '') {
96
+ return raw;
97
+ }
98
+ }
99
+ return null;
100
+ };
101
+ export function tokenizedFly(node, params) {
102
+ const styles = getComputedStyle(node);
103
+ const durationRaw = resolveToken(styles, params.durationTokens);
104
+ let duration = (durationRaw !== null ? parseCssTime(durationRaw) : null) ?? params.fallbackDuration;
105
+ let x = params.x ?? 0;
106
+ let y = params.y ?? 0;
107
+ if (params.distanceTokens) {
108
+ const distRaw = resolveToken(styles, params.distanceTokens);
109
+ const resolvedDist = (distRaw !== null ? parseCssLength(distRaw) : null) ??
110
+ params.fallbackDistance ??
111
+ Math.max(Math.abs(x), Math.abs(y));
112
+ // Math.abs, not the raw resolved value: a negative distance (a theming
113
+ // mistake -- `--distance-overlay: -60px`, say -- rather than anything a
114
+ // normal interaction can trigger) must resize the magnitude only, same as
115
+ // any other distance. Without it, Math.sign(x) * dist below would flip the
116
+ // travel DIRECTION too, silently contradicting the next comment's own
117
+ // stated invariant.
118
+ const dist = Math.abs(resolvedDist);
119
+ // Preserve the sign/direction the caller specified, resize the magnitude only --
120
+ // a 0 (no travel on that axis) must stay exactly 0 regardless of any token or
121
+ // fallback, since Math.sign(0) is 0 anyway but this keeps the intent explicit.
122
+ x = x === 0 ? 0 : Math.sign(x) * dist;
123
+ y = y === 0 ? 0 : Math.sign(y) * dist;
124
+ }
125
+ const easingRaw = params.easingTokens ? resolveToken(styles, params.easingTokens) : null;
126
+ const parsedEasing = easingRaw !== null ? parseCssEasing(easingRaw) : null;
127
+ // svelte/transition's own `fly` defaults to cubicOut, but the runtime
128
+ // (transitions.js) falls back to *linear* for any TransitionConfig that
129
+ // omits `easing` -- so this default has to be supplied explicitly to stay
130
+ // byte-identical to the `fly` calls this replaces. Fade-shaped call sites
131
+ // (no distanceTokens, x/y at 0) pass `easing: linear` themselves to match
132
+ // `fade`'s own default instead.
133
+ const easing = parsedEasing ?? params.easing ?? cubicOut;
134
+ // A consumer-set duration token can otherwise revive non-instant motion even
135
+ // under prefers-reduced-motion -- reducedMotion.current is the live OS
136
+ // preference, so this backstop applies regardless of what the token chain
137
+ // above resolved to. 0.001 rather than a hard 0: still visually instant, but
138
+ // keeps this a real (non-zero-duration) transition for Svelte's own
139
+ // lifecycle/event handling instead of a degenerate edge case.
140
+ if (reducedMotion.current) {
141
+ duration = 0.001;
142
+ }
143
+ const targetOpacity = +styles.opacity;
144
+ const transform = styles.transform === 'none' ? '' : styles.transform;
145
+ return {
146
+ duration,
147
+ easing,
148
+ css: (t) => `
149
+ transform: ${transform} translate(${(1 - t) * x}px, ${(1 - t) * y}px);
150
+ opacity: ${targetOpacity * t};
151
+ `
152
+ };
153
+ }
@@ -815,9 +815,10 @@
815
815
  }
816
816
  .area-fill {
817
817
  transition:
818
- fill-opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
818
+ fill-opacity
819
+ var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
819
820
  var(--chart-transition-easing, var(--motion-easing, ease)),
820
- opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
821
+ opacity var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
821
822
  var(--chart-transition-easing, var(--motion-easing, ease));
822
823
  pointer-events: none;
823
824
  }
@@ -825,7 +826,8 @@
825
826
  opacity: var(--areachart-dimmed-opacity, 0.1);
826
827
  }
827
828
  .area-line {
828
- transition: opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
829
+ transition: opacity
830
+ var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
829
831
  var(--chart-transition-easing, var(--motion-easing, ease));
830
832
  stroke-linecap: round;
831
833
  stroke-linejoin: round;
@@ -842,9 +844,9 @@
842
844
  }
843
845
  .dot {
844
846
  transition:
845
- r var(--chart-transition-duration, var(--motion-duration, 0.2s))
847
+ r var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
846
848
  var(--chart-transition-easing, var(--motion-easing, ease)),
847
- opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
849
+ opacity var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
848
850
  var(--chart-transition-easing, var(--motion-easing, ease));
849
851
  stroke: var(--chart-dot-stroke, light-dark(#fff, #111827));
850
852
  stroke-width: 2;
@@ -1182,7 +1182,8 @@
1182
1182
  position: relative;
1183
1183
  }
1184
1184
  .bar {
1185
- transition: opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
1185
+ transition: opacity
1186
+ var(--chart-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
1186
1187
  var(--chart-transition-easing, var(--motion-easing, ease));
1187
1188
  cursor: pointer;
1188
1189
  }
@@ -216,7 +216,8 @@
216
216
  position: absolute;
217
217
  top: 0;
218
218
  left: 0;
219
- transition: transform var(--book-transition-duration, 0.3s) ease;
219
+ transition: transform var(--book-transition-duration, var(--motion-duration, 0.3s))
220
+ var(--book-transition-easing, var(--motion-easing, ease));
220
221
  }
221
222
 
222
223
  .page-fade {
@@ -224,7 +225,8 @@
224
225
  top: 0;
225
226
  left: 0;
226
227
  opacity: 0;
227
- transition: opacity var(--book-transition-duration, 0.3s) ease;
228
+ transition: opacity var(--book-transition-duration, var(--motion-duration, 0.3s))
229
+ var(--book-transition-easing, var(--motion-easing, ease));
228
230
  pointer-events: none;
229
231
  }
230
232
 
@@ -282,7 +284,8 @@
282
284
  border: none;
283
285
  padding: 0;
284
286
  cursor: pointer;
285
- transition: background-color var(--book-dot-transition-duration, var(--motion-duration, 0.2s))
287
+ transition: background-color
288
+ var(--book-dot-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
286
289
  var(--book-dot-transition-easing, var(--motion-easing, ease));
287
290
  }
288
291
 
@@ -319,7 +319,14 @@
319
319
  .chat-bubble {
320
320
  position: fixed;
321
321
  z-index: var(--chat-bubble-z-index, 1000);
322
- transition: var(--chat-bubble-snap-transition, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1));
322
+ transition: var(
323
+ --chat-bubble-snap-transition,
324
+ transform 0.28s
325
+ var(
326
+ --chat-bubble-snap-transition-easing,
327
+ var(--ease-smooth-out, cubic-bezier(0.23, 1, 0.32, 1))
328
+ )
329
+ );
323
330
  }
324
331
 
325
332
  .chat-bubble.dragging {
@@ -351,8 +358,16 @@
351
358
  --resizable-handle-color: var(--chat-bubble-resize-handle-color, transparent);
352
359
  --resizable-transition: var(
353
360
  --chat-bubble-expand-transition,
354
- width 0.32s cubic-bezier(0.22, 1, 0.36, 1),
355
- height 0.32s cubic-bezier(0.22, 1, 0.36, 1)
361
+ width 0.32s
362
+ var(
363
+ --chat-bubble-expand-transition-easing,
364
+ var(--ease-smooth-out, cubic-bezier(0.23, 1, 0.32, 1))
365
+ ),
366
+ height 0.32s
367
+ var(
368
+ --chat-bubble-expand-transition-easing,
369
+ var(--ease-smooth-out, cubic-bezier(0.23, 1, 0.32, 1))
370
+ )
356
371
  );
357
372
  }
358
373
 
@@ -262,9 +262,21 @@
262
262
  border: var(--checkbox-border, 2px solid #757575);
263
263
  border-radius: var(--checkbox-border-radius, var(--radius, 4px));
264
264
  background-color: var(--checkbox-background, transparent);
265
- transition:
266
- background-color var(--checkbox-transition, 0.2s),
267
- border var(--checkbox-transition, 0.2s);
265
+ /* --checkbox-transition wraps the WHOLE value (matching Choicebox/Toggle's
266
+ own "-transition" tokens), not just the duration -- a consumer filling it
267
+ with e.g. "0.3s ease-in-out" replaces the entire expression cleanly. Putting
268
+ it in only the duration slot with a separate -easing term appended after
269
+ would make that same override produce an invalid transition (two
270
+ timing-function components), dropping the whole declaration to its
271
+ initial value instead of just using a different timing. */
272
+ transition: var(
273
+ --checkbox-transition,
274
+ background-color
275
+ var(--checkbox-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
276
+ var(--checkbox-transition-easing, var(--motion-easing, ease)),
277
+ border var(--checkbox-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
278
+ var(--checkbox-transition-easing, var(--motion-easing, ease))
279
+ );
268
280
  flex-shrink: 0;
269
281
  }
270
282
 
@@ -225,7 +225,15 @@
225
225
  gap: var(--choicebox-gap, 12px);
226
226
  cursor: var(--choicebox-cursor, pointer);
227
227
  font-family: var(--choicebox-font-family, inherit);
228
- transition: var(--choicebox-transition, border-color 0.2s, background 0.2s);
228
+ transition: var(
229
+ --choicebox-transition,
230
+ border-color
231
+ var(--choicebox-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
232
+ var(--choicebox-transition-easing, var(--motion-easing, ease)),
233
+ background
234
+ var(--choicebox-transition-duration, var(--duration-base, var(--motion-duration, 0.2s)))
235
+ var(--choicebox-transition-easing, var(--motion-easing, ease))
236
+ );
229
237
  -webkit-tap-highlight-color: transparent;
230
238
  }
231
239
 
@@ -265,7 +273,21 @@
265
273
  height: var(--choicebox-indicator-size, 20px);
266
274
  border: var(--choicebox-indicator-border, 2px solid #757575);
267
275
  background: var(--choicebox-indicator-background, transparent);
268
- transition: var(--choicebox-indicator-transition, background 0.2s, border-color 0.2s);
276
+ transition: var(
277
+ --choicebox-indicator-transition,
278
+ background
279
+ var(
280
+ --choicebox-indicator-transition-duration,
281
+ var(--duration-base, var(--motion-duration, 0.2s))
282
+ )
283
+ var(--choicebox-indicator-transition-easing, var(--motion-easing, ease)),
284
+ border-color
285
+ var(
286
+ --choicebox-indicator-transition-duration,
287
+ var(--duration-base, var(--motion-duration, 0.2s))
288
+ )
289
+ var(--choicebox-indicator-transition-easing, var(--motion-easing, ease))
290
+ );
269
291
  }
270
292
 
271
293
  .indicator.radio {
@@ -580,7 +580,21 @@
580
580
  background: var(--combobox-input-background, #ffffff);
581
581
  border: var(--combobox-input-border, 1px solid #cccccc);
582
582
  border-radius: var(--combobox-input-border-radius, var(--radius, 4px));
583
- transition: var(--combobox-input-transition, border-color 0.15s, box-shadow 0.15s);
583
+ transition: var(
584
+ --combobox-input-transition,
585
+ border-color
586
+ var(
587
+ --combobox-input-transition-duration,
588
+ var(--duration-quick, var(--motion-duration, 0.15s))
589
+ )
590
+ var(--combobox-input-transition-easing, var(--motion-easing, ease)),
591
+ box-shadow
592
+ var(
593
+ --combobox-input-transition-duration,
594
+ var(--duration-quick, var(--motion-duration, 0.15s))
595
+ )
596
+ var(--combobox-input-transition-easing, var(--motion-easing, ease))
597
+ );
584
598
  }
585
599
 
586
600
  /* Multi-select control: pills wrap above the typeahead input. */