beez-ui 2.6.2 → 2.7.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.
@@ -6,8 +6,8 @@ import { Slot } from "radix-ui";
6
6
  import { animate, hover, press, usePresence, frame } from "motion/react";
7
7
  import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
8
8
  import { HOVER_CAPABLE_QUERY, MILLISECONDS_PER_SECOND, MOTION_ENTERING_ATTRIBUTE } from "../constants/motion.js";
9
- import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE, MOTION_INVALID_SHAKE_PX, MOTION_ITEM_CHECK_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_LIST_ITEM_DISTANCE, MOTION_LIST_STAGGER_LIMIT, MOTION_EASE_IN_OUT, MOTION_REVEAL_BLUR_PX, MOTION_REVEAL_SCALE, MOTION_SHIMMER_TRANSLATE, MOTION_CARD_SHADOW_LIFT, MOTION_CARD_SHADOW_REST, SPRING_CHEVRON, SPRING_PANEL, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
10
- import { surfaceEnter, surfaceExit } from "./surface-keyframes.js";
9
+ import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE, MOTION_ITEM_CHECK_SCALE, MOTION_ITEM_ENTER_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_EASE_IN_OUT, MOTION_REVEAL_BLUR_PX, MOTION_REVEAL_SCALE, MOTION_SHIMMER_TRANSLATE, MOTION_CARD_SHADOW_LIFT, MOTION_CARD_SHADOW_REST, SPRING_CHEVRON, SPRING_PANEL, SPRING_POP, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
10
+ import { surfaceEnter, surfaceExit, withRestingStyle } from "./surface-keyframes.js";
11
11
  import { useAttachedElement } from "./use-attached-element.js";
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
13
  const EASE_OUT = [...MOTION_EASE];
@@ -33,8 +33,6 @@ const ANIMATED_PROPERTIES = [
33
33
  "filter",
34
34
  "clipPath"
35
35
  ];
36
- /** Input groups draw the field frame; their controls are borderless. */
37
- const INPUT_GROUP_SLOT = "input-group";
38
36
  /** SVG attributes Motion writes to draw a stroke with `pathLength`. */
39
37
  const STROKE_DRAW_ATTRIBUTES = [
40
38
  "pathLength",
@@ -44,7 +42,6 @@ const STROKE_DRAW_ATTRIBUTES = [
44
42
  /** Attributes whose changes re-announce a field's state with a subtle settle. */
45
43
  const FIELD_STATE_ATTRIBUTES = [
46
44
  "data-state",
47
- "aria-invalid",
48
45
  "aria-selected",
49
46
  "aria-checked",
50
47
  "data-loading",
@@ -87,14 +84,6 @@ function rotationOf(transform) {
87
84
  const { a, b } = matrixOf(transform);
88
85
  return Math.atan2(b, a) * 180 / Math.PI;
89
86
  }
90
- /** Position of an item among its siblings of the same slot inside the closest list or menu. */
91
- function listIndexOf(element) {
92
- const slot = element.getAttribute("data-slot");
93
- const list = element.closest("[role=\"listbox\"],[role=\"menu\"],[role=\"list\"]");
94
- if (!slot || !list) return 0;
95
- const items = Array.from(list.querySelectorAll(`[data-slot="${slot}"]`));
96
- return Math.max(items.indexOf(element), 0);
97
- }
98
87
  /** Checked and indeterminate indicators are visible; only unchecked ones are hidden. */
99
88
  function isChecked(element) {
100
89
  const state = element.getAttribute("data-state");
@@ -192,13 +181,18 @@ export function MotionSlot(t0) {
192
181
  snapshot[property_1] = value;
193
182
  }
194
183
  });
195
- interruptedRef.current = snapshot;
184
+ interruptedRef.current = {
185
+ element,
186
+ values: snapshot
187
+ };
196
188
  }
197
189
  cleanups.forEach(_temp);
198
190
  cancel();
199
191
  restore();
200
192
  element.removeAttribute(MOTION_ENTERING_ATTRIBUTE);
201
193
  };
194
+ const interrupted = interruptedRef.current;
195
+ interruptedRef.current = null;
202
196
  if (reduceMotion || !isPresent && element.hasAttribute("data-beez-theme-menu")) {
203
197
  if (!isPresent) {
204
198
  queueMicrotask(() => {
@@ -253,13 +247,15 @@ export function MotionSlot(t0) {
253
247
  });
254
248
  };
255
249
  const tween = _temp2;
256
- const interrupted = interruptedRef.current;
257
- interruptedRef.current = null;
258
250
  if (SURFACE_KINDS.includes(kind)) {
259
251
  const step = isPresent ? surfaceEnter(element, kind) : surfaceExit(element, kind);
260
- const keyframes_0 = { ...step.keyframes };
261
- if (!isPresent && interrupted) {
262
- Object.entries(interrupted).forEach((t10) => {
252
+ const computed_0 = getComputedStyle(element);
253
+ const keyframes_0 = withRestingStyle(step.keyframes, {
254
+ transform: computed_0.transform,
255
+ opacity: Number.parseFloat(computed_0.opacity)
256
+ });
257
+ if (interrupted?.element === element) {
258
+ Object.entries(interrupted.values).forEach((t10) => {
263
259
  const [property_8, value_0] = t10;
264
260
  const frames_0 = keyframes_0[property_8];
265
261
  if (!Array.isArray(frames_0) || !value_0) {
@@ -298,11 +294,10 @@ export function MotionSlot(t0) {
298
294
  if (kind === "icon-swap") {
299
295
  play({
300
296
  opacity: [0, 1],
301
- transform: [`scale(${MOTION_ICON_SWAP_SCALE})`, "scale(1)"],
302
- filter: ["blur(8px)", "blur(0px)"]
297
+ transform: [`scale(${MOTION_ICON_SWAP_SCALE})`, "scale(1)"]
303
298
  }, {
304
- duration: MOTION_TIMING.iconSwap,
305
- ease: [...MOTION_EASE_IN_OUT]
299
+ ...SPRING_POP,
300
+ opacity: tween(MOTION_TIMING.iconSwap)
306
301
  });
307
302
  } else {
308
303
  if (kind === "item-check") {
@@ -315,16 +310,10 @@ export function MotionSlot(t0) {
315
310
  });
316
311
  } else {
317
312
  if (kind === "list-item") {
318
- const index = listIndexOf(element);
319
- const position = Math.min(index, MOTION_LIST_STAGGER_LIMIT);
320
- play(index < MOTION_LIST_STAGGER_LIMIT ? {
313
+ play({
321
314
  opacity: [0, 1],
322
- transform: [`translateY(${-MOTION_LIST_ITEM_DISTANCE}px)`, "translateY(0px)"],
323
- filter: ["blur(3px)", "blur(0px)"]
324
- } : { opacity: [0, 1] }, {
325
- ...tween(MOTION_TIMING.listItem),
326
- delay: MOTION_TIMING.listDelay + position * MOTION_TIMING.listStagger
327
- });
315
+ transform: [`scale(${MOTION_ITEM_ENTER_SCALE})`, "scale(1)"]
316
+ }, tween(MOTION_TIMING.listItem));
328
317
  } else {
329
318
  if (kind === "delayed-pop" && typeof element.animate === "function") {
330
319
  const pop = element.animate({
@@ -356,8 +345,7 @@ export function MotionSlot(t0) {
356
345
  if (kind === "message") {
357
346
  play({
358
347
  opacity: [0, 1],
359
- transform: [`translateY(${-MOTION_CONTENT_DISTANCE}px)`, "translateY(0px)"],
360
- filter: ["blur(4px)", "blur(0px)"]
348
+ transform: [`scale(${MOTION_ITEM_ENTER_SCALE})`, "scale(1)"]
361
349
  }, tween(MOTION_TIMING.message));
362
350
  } else {
363
351
  if (kind === "fade" || kind === "card" || kind === "row") {
@@ -450,26 +438,10 @@ export function MotionSlot(t0) {
450
438
  };
451
439
  element.addEventListener("focusin", settleField);
452
440
  cleanups.push(() => element.removeEventListener("focusin", settleField));
453
- const isGroup = element.getAttribute("data-slot") === INPUT_GROUP_SLOT;
454
- const isGroupControl = !isGroup && element.parentElement?.closest(`[data-slot="${INPUT_GROUP_SLOT}"]`) != null;
455
- const readInvalid = () => isGroup ? element.querySelector("[data-slot][aria-invalid=\"true\"]") !== null : element.getAttribute("aria-invalid") === "true";
456
- let wasInvalid = readInvalid();
457
- const observer = new MutationObserver((records) => {
458
- const isInvalid = readInvalid();
459
- const becameInvalid = isInvalid && !wasInvalid;
460
- wasInvalid = isInvalid;
461
- if (becameInvalid && !isGroupControl && !element.hasAttribute("hidden")) {
462
- play({ transform: MOTION_INVALID_SHAKE_PX.map(_temp3) }, { duration: MOTION_TIMING.shake });
463
- } else {
464
- if (records.some((record) => record.target === element && record.attributeName !== "aria-invalid")) {
465
- settleField();
466
- }
467
- }
468
- });
441
+ const observer = new MutationObserver(settleField);
469
442
  observer.observe(element, {
470
443
  attributes: true,
471
- attributeFilter: FIELD_STATE_ATTRIBUTES,
472
- subtree: isGroup
444
+ attributeFilter: FIELD_STATE_ATTRIBUTES
473
445
  });
474
446
  cleanups.push(() => observer.disconnect());
475
447
  }
@@ -477,9 +449,9 @@ export function MotionSlot(t0) {
477
449
  let wasChecked = isChecked(element);
478
450
  const strokeDraws = new Set();
479
451
  const settleStrokes = () => {
480
- strokeDraws.forEach(_temp4);
452
+ strokeDraws.forEach(_temp3);
481
453
  strokeDraws.clear();
482
- element.querySelectorAll("path").forEach(_temp5);
454
+ element.querySelectorAll("path").forEach(_temp4);
483
455
  };
484
456
  cleanups.push(settleStrokes);
485
457
  const observer_0 = new MutationObserver(() => {
@@ -644,7 +616,7 @@ export function MotionSlot(t0) {
644
616
  release.cancel();
645
617
  squish = null;
646
618
  }
647
- }).catch(_temp6);
619
+ }).catch(_temp5);
648
620
  };
649
621
  }));
650
622
  }
@@ -697,16 +669,13 @@ export function MotionSlot(t0) {
697
669
  }
698
670
  return t6;
699
671
  }
700
- function _temp6() {}
701
- function _temp5(path) {
672
+ function _temp5() {}
673
+ function _temp4(path) {
702
674
  STROKE_DRAW_ATTRIBUTES.forEach((attribute) => path.removeAttribute(attribute));
703
675
  }
704
- function _temp4(draw) {
676
+ function _temp3(draw) {
705
677
  return draw.cancel();
706
678
  }
707
- function _temp3(offset) {
708
- return `translateX(${offset}px)`;
709
- }
710
679
  function _temp2(duration) {
711
680
  return {
712
681
  duration,
@@ -7,7 +7,18 @@ export interface MotionStep {
7
7
  keyframes: DOMKeyframesDefinition;
8
8
  transition: AnimationOptions;
9
9
  }
10
- /** Entrance playback matching the beui surface family for each kind. */
10
+ /** Entrance playback for each surface kind, on Arc's motion presets. */
11
11
  export declare function surfaceEnter(element: Element, kind: SurfaceKind): MotionStep;
12
+ /** Transform and opacity a surface rests at, which its caller may set through a class or style. */
13
+ export interface RestingStyle {
14
+ transform: string;
15
+ opacity: number;
16
+ }
17
+ /**
18
+ * Composes surface playback with the surface's resting transform and opacity, so a caller-owned
19
+ * tilt, offset or translucency holds through the whole entrance and exit instead of being
20
+ * replaced by identity and full opacity, then snapping back once playback restores it.
21
+ */
22
+ export declare function withRestingStyle(keyframes: DOMKeyframesDefinition, resting: RestingStyle): DOMKeyframesDefinition;
12
23
  /** Exit playback: brief and eased so dismissal never blocks the next interaction. */
13
24
  export declare function surfaceExit(element: Element, kind: SurfaceKind): MotionStep;
@@ -1,7 +1,7 @@
1
- import { MOTION_CENTER_MORPH_FOLDED_PERCENT, MOTION_DIALOG_DISTANCE, MOTION_EASE, MOTION_EASE_DRAWER, MOTION_EASE_UNFOLD, MOTION_SURFACE_CLIP_BLEED_PX, MOTION_SURFACE_CLIP_PERCENT, MOTION_SURFACE_SCALE, MOTION_TIMING, MOTION_TOOLTIP_DISTANCE, SPRING_PANEL, SPRING_TOOLTIP } from "./tokens.js";
1
+ import { MOTION_DIALOG_DISTANCE, MOTION_DIALOG_EXIT_DISTANCE, MOTION_EASE, MOTION_EASE_DRAWER, MOTION_EASE_STANDARD, MOTION_EXIT_SCALE, MOTION_SURFACE_CLIP_BLEED_PX, MOTION_SURFACE_CLIP_PERCENT, MOTION_SURFACE_SCALE, MOTION_TIMING, MOTION_TOOLTIP_DISTANCE, MOTION_TOOLTIP_SCALE, SPRING_PANEL } from "./tokens.js";
2
2
  const EASE_OUT = [...MOTION_EASE];
3
3
  const EASE_DRAWER = [...MOTION_EASE_DRAWER];
4
- const EASE_UNFOLD = [...MOTION_EASE_UNFOLD];
4
+ const EASE_STANDARD = [...MOTION_EASE_STANDARD];
5
5
  const SIDES = [
6
6
  "top",
7
7
  "right",
@@ -70,45 +70,14 @@ function collapsedClip(side, align) {
70
70
  const [top, bottom] = crossAxis(align);
71
71
  return side === "right" ? `inset(${top} ${hidden} ${bottom} ${OPEN_EDGE})` : `inset(${top} ${OPEN_EDGE} ${bottom} ${hidden})`;
72
72
  }
73
- /** Computed radius of each corner, clockwise from the top left, as border-radius serializes it. */
74
- const CORNER_RADIUS_PROPERTIES = [
75
- "borderTopLeftRadius",
76
- "borderTopRightRadius",
77
- "borderBottomRightRadius",
78
- "borderBottomLeftRadius"
79
- ];
80
- /**
81
- * Builds the `round` part of an inset clip that repeats the surface's own corners, each grown by
82
- * the bleed so the clip edge stays parallel to the border. A corner serializes as one radius or
83
- * as a horizontal and vertical pair (elliptical), in pixels or percentages of the box.
84
- */
85
- function bleedingCornerRadii(element) {
86
- const computed = getComputedStyle(element);
87
- const grow = (radius) => `calc(${radius} + ${MOTION_SURFACE_CLIP_BLEED_PX}px)`;
88
- const corners = CORNER_RADIUS_PROPERTIES.map((property) => {
89
- const [horizontal = "0px", vertical = horizontal] = computed[property].trim().split(/\s+/);
90
- return {
91
- horizontal: grow(horizontal),
92
- vertical: grow(vertical)
93
- };
94
- });
95
- const horizontals = corners.map((corner) => corner.horizontal).join(" ");
96
- const verticals = corners.map((corner) => corner.vertical).join(" ");
97
- return `round ${horizontals} / ${verticals}`;
98
- }
99
73
  /**
100
- * Folded and unfolded clips of a center-morph surface. Both share the surface's own corners,
101
- * so the shape matches the panel once unfolded and the clip never spends its last frames
102
- * rounding corners.
74
+ * Arc's dialog leave: a short tween on the standard curve, so it never lingers and a reopen
75
+ * retargets from wherever the panel is.
103
76
  */
104
- function centerMorphClips(element) {
105
- const round = bleedingCornerRadii(element);
106
- const folded = `${MOTION_CENTER_MORPH_FOLDED_PERCENT}%`;
107
- return {
108
- folded: `inset(${folded} ${folded} ${folded} ${folded} ${round})`,
109
- unfolded: `inset(${OPEN_EDGE} ${OPEN_EDGE} ${OPEN_EDGE} ${OPEN_EDGE} ${round})`
110
- };
111
- }
77
+ const DIALOG_LEAVE = {
78
+ duration: MOTION_TIMING.fast,
79
+ ease: EASE_STANDARD
80
+ };
112
81
  /** Edge-panel travel: fully off-screen on the side the sheet is attached to. */
113
82
  function sheetOffset(element) {
114
83
  const side = readPlacement(element).side ?? "right";
@@ -129,7 +98,7 @@ function sheetOffset(element) {
129
98
  y: "0%"
130
99
  };
131
100
  }
132
- /** Entrance playback matching the beui surface family for each kind. */
101
+ /** Entrance playback for each surface kind, on Arc's motion presets. */
133
102
  export function surfaceEnter(element, kind) {
134
103
  const { side, align } = readPlacement(element);
135
104
  if (kind === "overlay") {
@@ -142,42 +111,33 @@ export function surfaceEnter(element, kind) {
142
111
  };
143
112
  }
144
113
  if (kind === "tooltip") {
114
+ // Arc's tooltip: a short rise from its trigger and a fade, without blur.
145
115
  const offset = offsetAwayFromTrigger(side, MOTION_TOOLTIP_DISTANCE);
146
116
  return {
147
117
  keyframes: {
148
118
  opacity: [0, 1],
149
- transform: [transform(offset.x, offset.y, .9), transform(0, 0)],
150
- filter: ["blur(5px)", "blur(0px)"]
119
+ transform: [transform(offset.x, offset.y, MOTION_TOOLTIP_SCALE), transform(0, 0)]
151
120
  },
152
121
  transition: {
153
- ...SPRING_TOOLTIP,
154
- opacity: {
155
- duration: MOTION_TIMING.fast,
156
- ease: EASE_OUT
157
- },
158
- filter: {
159
- duration: MOTION_TIMING.enter,
160
- ease: EASE_OUT
161
- }
122
+ duration: MOTION_TIMING.fast,
123
+ ease: EASE_OUT
162
124
  }
163
125
  };
164
126
  }
165
- if (kind === "dialog") {
127
+ // The deprecated center morph now plays Arc's dialog transition, like the library's own dialogs.
128
+ if (kind === "dialog" || kind === "center-morph") {
129
+ // Arc's dialog: the panel rises and scales in on the smooth spring while it fades in quickly.
166
130
  return {
167
131
  keyframes: {
168
132
  opacity: [0, 1],
169
- transform: [transform(0, MOTION_DIALOG_DISTANCE, .97), transform(0, 0)]
133
+ transform: [transform(0, MOTION_DIALOG_DISTANCE, MOTION_SURFACE_SCALE), transform(0, 0)]
170
134
  },
171
- transition: SPRING_PANEL
172
- };
173
- }
174
- if (kind === "center-morph") {
175
- const { folded, unfolded } = centerMorphClips(element);
176
- return {
177
- keyframes: { clipPath: [folded, unfolded] },
178
135
  transition: {
179
- duration: MOTION_TIMING.centerMorph,
180
- ease: EASE_UNFOLD
136
+ ...SPRING_PANEL,
137
+ opacity: {
138
+ duration: MOTION_TIMING.fast,
139
+ ease: EASE_OUT
140
+ }
181
141
  }
182
142
  };
183
143
  }
@@ -211,6 +171,22 @@ export function surfaceEnter(element, kind) {
211
171
  }
212
172
  };
213
173
  }
174
+ /**
175
+ * Composes surface playback with the surface's resting transform and opacity, so a caller-owned
176
+ * tilt, offset or translucency holds through the whole entrance and exit instead of being
177
+ * replaced by identity and full opacity, then snapping back once playback restores it.
178
+ */
179
+ export function withRestingStyle(keyframes, resting) {
180
+ const composed = { ...keyframes };
181
+ const { transform, opacity } = composed;
182
+ if (Array.isArray(transform) && resting.transform && resting.transform !== "none") {
183
+ composed.transform = transform.map((value) => `${String(value)} ${resting.transform}`);
184
+ }
185
+ if (Array.isArray(opacity) && Number.isFinite(resting.opacity) && resting.opacity !== 1) {
186
+ composed.opacity = opacity.map((value) => Number(value) * resting.opacity);
187
+ }
188
+ return composed;
189
+ }
214
190
  /** Exit playback: brief and eased so dismissal never blocks the next interaction. */
215
191
  export function surfaceExit(element, kind) {
216
192
  const exitTween = {
@@ -220,40 +196,29 @@ export function surfaceExit(element, kind) {
220
196
  if (kind === "overlay") {
221
197
  return {
222
198
  keyframes: { opacity: [1, 0] },
223
- transition: exitTween
199
+ transition: DIALOG_LEAVE
224
200
  };
225
201
  }
226
202
  if (kind === "tooltip") {
227
- const offset = offsetAwayFromTrigger(readPlacement(element).side, MOTION_TOOLTIP_DISTANCE * .6);
203
+ // Arc's tooltip leaves in place, shrinking slightly while it fades, on the instant timing.
228
204
  return {
229
205
  keyframes: {
230
206
  opacity: [1, 0],
231
- transform: [transform(0, 0), transform(offset.x, offset.y, .94)],
232
- filter: ["blur(0px)", "blur(3px)"]
207
+ transform: [transform(0, 0), transform(0, 0, MOTION_EXIT_SCALE)]
233
208
  },
234
- transition: exitTween
209
+ transition: {
210
+ duration: MOTION_TIMING.instant,
211
+ ease: EASE_STANDARD
212
+ }
235
213
  };
236
214
  }
237
- if (kind === "dialog") {
215
+ if (kind === "dialog" || kind === "center-morph") {
238
216
  return {
239
217
  keyframes: {
240
218
  opacity: [1, 0],
241
- transform: [transform(0, 0), transform(0, MOTION_DIALOG_DISTANCE, .98)]
219
+ transform: [transform(0, 0), transform(0, MOTION_DIALOG_EXIT_DISTANCE, MOTION_EXIT_SCALE)]
242
220
  },
243
- transition: {
244
- duration: MOTION_TIMING.enter,
245
- ease: EASE_OUT
246
- }
247
- };
248
- }
249
- if (kind === "center-morph") {
250
- const { folded, unfolded } = centerMorphClips(element);
251
- return {
252
- keyframes: { clipPath: [unfolded, folded] },
253
- transition: {
254
- duration: MOTION_TIMING.centerMorphFold,
255
- ease: EASE_UNFOLD
256
- }
221
+ transition: DIALOG_LEAVE
257
222
  };
258
223
  }
259
224
  if (kind === "sheet") {
@@ -1,30 +1,49 @@
1
- /**
2
- * Defines the timing, curves and physics shared by Motion animations, aligned with beui.dev/components/motion.
3
- * Public: consumers animate their own product surfaces with these tokens so they move like the library.
4
- */
5
1
  export declare const MOTION_TIMING: {
6
- readonly fast: 0.14;
7
- readonly enter: 0.18;
8
- readonly message: 0.2;
9
- readonly panel: 0.22;
10
- readonly overlay: 0.2;
11
- readonly exit: 0.12;
2
+ /** Arc `duration.instant`: color answers and fades that must feel immediate. */
3
+ readonly instant: 0.12;
4
+ /** Arc `duration.fast`: small fades, the opacity of a spring entrance, and dialog exits. */
5
+ readonly fast: 0.16;
6
+ /** Arc `duration.standard`: entrance tweens. */
7
+ readonly enter: 0.24;
8
+ /** Arc `duration.standard`: tween length for a deliberate on-screen change. */
9
+ readonly standard: 0.24;
10
+ /** Arc `duration.considered`: the slowest tween, for large layout changes. */
11
+ readonly considered: 0.48;
12
+ /** Arc `duration.standard`: inline messages that appear next to the content they describe. */
13
+ readonly message: 0.24;
14
+ /** Arc `duration.standard`: opacity of panels whose size moves on a spring. */
15
+ readonly panel: 0.24;
16
+ /** Arc `duration.standard`: scrims fading in behind dialogs and sheets. */
17
+ readonly overlay: 0.24;
18
+ /** Arc `duration.exit`: leaving is faster than arriving. */
19
+ readonly exit: 0.18;
12
20
  readonly surfaceClip: 0.32;
21
+ /**
22
+ * @deprecated Invalid fields no longer shake: the error is told by color and message. Kept for consumers that
23
+ * animate their own fields.
24
+ */
13
25
  readonly shake: 0.45;
14
26
  readonly checkDraw: 0.3;
15
27
  readonly checkDrawDelay: 0.04;
16
- readonly iconSwap: 0.2;
17
- readonly listItem: 0.18;
28
+ /** Arc `duration.fast`: the fade of an icon swapped in place; its scale rides `SPRING_POP`. */
29
+ readonly iconSwap: 0.16;
30
+ /** Arc `duration.fast`: options fading in with their list. */
31
+ readonly listItem: 0.16;
18
32
  readonly listDelay: 0.05;
33
+ /** Arc `stagger.item`. */
19
34
  readonly listStagger: 0.035;
20
35
  readonly closeEnter: 0.2;
21
36
  readonly closeDelay: 0.16;
37
+ /** Arc `duration.standard`: edge panels leaving the viewport. */
22
38
  readonly sheetExit: 0.24;
23
- /** Dialog surface unfolding from its center toward every edge. */
39
+ /**
40
+ * @deprecated Dialogs no longer unfold from their center; they rise and scale in on `SPRING_PANEL`. Kept for
41
+ * consumers that animate their own surfaces with it.
42
+ */
24
43
  readonly centerMorph: 0.43;
25
44
  /**
26
- * Dialog surface folding back to its center. Shorter than the unfold: the page stays hidden
27
- * from assistive technology and focus returns to the trigger only once the fold finishes.
45
+ * @deprecated Dialogs no longer fold back to their center; they leave in `MOTION_TIMING.fast`. Kept for consumers
46
+ * that animate their own surfaces with it.
28
47
  */
29
48
  readonly centerMorphFold: 0.24;
30
49
  readonly reveal: 0.4;
@@ -32,96 +51,95 @@ export declare const MOTION_TIMING: {
32
51
  readonly pop: 0.32;
33
52
  /** Height of expanding and collapsing regions. */
34
53
  readonly collapse: 0.26;
54
+ /** Arc `duration.standard`. */
35
55
  readonly monthSwap: 0.24;
36
56
  readonly cursorBlink: 1;
37
57
  readonly skeleton: 1.8;
38
58
  };
39
59
  /** Items beyond this position share the last stagger delay, so long lists never lag behind. */
40
60
  export declare const MOTION_LIST_STAGGER_LIMIT = 10;
41
- /** Strong ease-out used for entrances; weak defaults such as `ease-out` feel sluggish. */
61
+ /** Arc `ease.enter`: strong ease-out for entrances; weak defaults such as `ease-out` feel sluggish. */
42
62
  export declare const MOTION_EASE: readonly [0.16, 1, 0.3, 1];
43
- /** Symmetric curve for looping or reversible effects. */
44
- export declare const MOTION_EASE_IN_OUT: readonly [0.77, 0, 0.175, 1];
63
+ /** Arc `ease.exit`: accelerates away, for elements leaving the screen. */
64
+ export declare const MOTION_EASE_EXIT: readonly [0.7, 0, 0.84, 0];
65
+ /** Arc `ease.standard`: color and opacity changes, and quick exits that must not linger. */
66
+ export declare const MOTION_EASE_STANDARD: readonly [0.22, 1, 0.36, 1];
67
+ /** Arc `ease.inOut`: elements that move while already on screen, and looping effects. */
68
+ export declare const MOTION_EASE_IN_OUT: readonly [0.65, 0, 0.35, 1];
45
69
  /** Drawer curve for edge panels leaving the viewport. */
46
70
  export declare const MOTION_EASE_DRAWER: readonly [0.32, 0.72, 0, 1];
47
71
  /**
48
72
  * Near-linear curve for the center morph: a spring or strong ease-out would resolve the clip
49
73
  * early and spend the last frames barely moving, so the whole duration reads as unfolding.
74
+ * @deprecated Dialogs no longer unfold from their center. Kept for consumers that animate their own surfaces with it.
50
75
  */
51
76
  export declare const MOTION_EASE_UNFOLD: readonly [0.2, 0, 0.2, 1];
52
- /** Press feedback on buttons and other tappable surfaces. */
77
+ /** Arc `spring.snappy`: press feedback on buttons and other tappable surfaces. */
53
78
  export declare const SPRING_PRESS: {
54
79
  readonly type: "spring";
55
- readonly stiffness: 500;
56
- readonly damping: 30;
57
- readonly mass: 0.6;
80
+ readonly visualDuration: 0.26;
81
+ readonly bounce: 0.12;
58
82
  };
59
- /** Overlay panel entrances — dialogs, sheets and floating surfaces. */
83
+ /** Arc `spring.smooth`: overlay panel entrances (dialogs, sheets and floating surfaces); never overshoots. */
60
84
  export declare const SPRING_PANEL: {
61
85
  readonly type: "spring";
62
- readonly stiffness: 420;
63
- readonly damping: 40;
64
- readonly mass: 0.5;
86
+ readonly visualDuration: 0.4;
87
+ readonly bounce: 0;
65
88
  };
66
- /** Shared-layout glides — indicators and highlights moving between items. */
89
+ /** Arc `spring.morph`: shared-layout glides, indicators and highlights moving between items. */
67
90
  export declare const SPRING_LAYOUT: {
68
91
  readonly type: "spring";
69
- readonly stiffness: 360;
70
- readonly damping: 32;
71
- readonly mass: 0.6;
92
+ readonly visualDuration: 0.42;
93
+ readonly bounce: 0.16;
72
94
  };
73
- /** Keyboard highlight in menus and lists: quick enough to keep up with a held arrow key. */
95
+ /** Arc `spring.snappy`: keyboard highlight in menus and lists, quick enough to keep up with a held arrow key. */
74
96
  export declare const SPRING_HIGHLIGHT: {
75
97
  readonly type: "spring";
76
- readonly stiffness: 700;
77
- readonly damping: 45;
78
- readonly mass: 0.5;
98
+ readonly visualDuration: 0.26;
99
+ readonly bounce: 0.12;
79
100
  };
80
- /** Tab indicator glide, tuned to settle without overshooting the list edges. */
101
+ /** Arc `spring.morph`: the tab indicator gliding between tabs. */
81
102
  export declare const SPRING_TABS: {
82
103
  readonly type: "spring";
83
- readonly stiffness: 245;
84
- readonly damping: 36;
85
- readonly mass: 1.2;
104
+ readonly visualDuration: 0.42;
105
+ readonly bounce: 0.16;
86
106
  };
87
- /** Lighter spawn for small tooltip surfaces. */
107
+ /** Arc `spring.snappy`: small tooltip surfaces. */
88
108
  export declare const SPRING_TOOLTIP: {
89
109
  readonly type: "spring";
90
- readonly stiffness: 380;
91
- readonly damping: 30;
92
- readonly mass: 0.7;
110
+ readonly visualDuration: 0.26;
111
+ readonly bounce: 0.12;
93
112
  };
94
- /** Disclosure chevrons rotate with a little bounce, as if flicked. */
113
+ /** Arc `spring.snappy`: disclosure chevrons turning toward their new state. */
95
114
  export declare const SPRING_CHEVRON: {
96
115
  readonly type: "spring";
97
- readonly duration: 0.4;
98
- readonly bounce: 0.3;
116
+ readonly visualDuration: 0.26;
117
+ readonly bounce: 0.12;
99
118
  };
100
- /** Heavy, deliberate switch thumb — high mass keeps the travel weighty without wobble. */
119
+ /** Arc `spring.smooth`: the switch thumb reports state, so it lands without overshoot. */
101
120
  export declare const SPRING_THUMB: {
102
121
  readonly type: "spring";
103
- readonly stiffness: 800;
104
- readonly damping: 80;
105
- readonly mass: 4;
122
+ readonly visualDuration: 0.4;
123
+ readonly bounce: 0;
106
124
  };
107
- /** Small, confident pops for counters, badges and toggled icons. */
125
+ /** Arc `spring.snappy`: small, confident pops for counters, badges and swapped icons. */
108
126
  export declare const SPRING_POP: {
109
127
  readonly type: "spring";
110
- readonly stiffness: 520;
111
- readonly damping: 30;
112
- readonly mass: 0.5;
128
+ readonly visualDuration: 0.26;
129
+ readonly bounce: 0.12;
113
130
  };
114
- /** Primary actions compress noticeably; dense navigation items stay subtle. */
115
- export declare const MOTION_PRESS_SCALE = 0.93;
131
+ /** Arc's press: about 0.97, so every pressable reads as the same push. */
132
+ export declare const MOTION_PRESS_SCALE = 0.97;
116
133
  export declare const MOTION_SUBTLE_PRESS_SCALE = 0.98;
117
134
  export declare const MOTION_TOGGLE_PRESS_SCALE = 0.92;
118
135
  export declare const MOTION_HOVER_SCALE = 1.02;
119
136
  export declare const MOTION_THUMB_SQUISH_SCALE = 0.9;
120
137
  export declare const MOTION_ITEM_CHECK_SCALE = 0.75;
121
- export declare const MOTION_ICON_SWAP_SCALE = 0.25;
138
+ /** Arc's icon swap: the incoming icon grows from 0.6 on `SPRING_POP`. */
139
+ export declare const MOTION_ICON_SWAP_SCALE = 0.6;
122
140
  export declare const MOTION_CLOSE_BUTTON_SCALE = 0.8;
123
141
  export declare const MOTION_LIST_ITEM_DISTANCE = 6;
124
- /** Scale a freshly inserted list item starts from, and a leaving one shrinks to. */
142
+ /** Scale a freshly inserted list item, option or inline message starts from, and a leaving one shrinks to. */
125
143
  export declare const MOTION_ITEM_ENTER_SCALE = 0.98;
126
144
  /** Loaded images settle from a slight zoom, so they develop in place instead of popping in. */
127
145
  export declare const MOTION_REVEAL_SCALE = 1.06;
@@ -133,17 +151,35 @@ export declare const MOTION_SHIMMER_TRANSLATE: readonly ["-100% 0", "100% 0"];
133
151
  /** Hovered interactive cards gain a soft drop shadow; the rest value has the same shape, invisible. */
134
152
  export declare const MOTION_CARD_SHADOW_LIFT = "0px 4px 16px 0px rgba(0, 0, 0, 0.08)";
135
153
  export declare const MOTION_CARD_SHADOW_REST = "0px 0px 0px 0px rgba(0, 0, 0, 0)";
154
+ /** Arc's dialog panel scale on entrance; floating surfaces start from it too. */
136
155
  export declare const MOTION_SURFACE_SCALE = 0.96;
156
+ /** Scale a dialog or tooltip shrinks to while it leaves (Arc). */
157
+ export declare const MOTION_EXIT_SCALE = 0.98;
137
158
  /** Fraction of the surface kept hidden by the clip at the start of its reveal, in percent. */
138
159
  export declare const MOTION_SURFACE_CLIP_PERCENT = 92;
139
160
  /** Room left outside a revealing surface for its ring and drop shadow (`shadow-lg` spans ~22px). */
140
161
  export declare const MOTION_SURFACE_CLIP_BLEED_PX = 24;
141
- export declare const MOTION_TOOLTIP_DISTANCE = 8;
142
- export declare const MOTION_DIALOG_DISTANCE = 20;
143
- /** Inset kept on every side of a center-morph dialog while folded, in percent: a sliver at its center. */
162
+ /** Arc's tooltip rise from its trigger, in pixels. */
163
+ export declare const MOTION_TOOLTIP_DISTANCE = 3;
164
+ /** Arc's tooltip scale on entrance. */
165
+ export declare const MOTION_TOOLTIP_SCALE = 0.97;
166
+ /** Arc's dialog rise on entrance, in pixels. */
167
+ export declare const MOTION_DIALOG_DISTANCE = 8;
168
+ /** Arc's dialog drop while it leaves, in pixels. */
169
+ export declare const MOTION_DIALOG_EXIT_DISTANCE = 4;
170
+ /**
171
+ * Inset kept on every side of a center-morph dialog while folded, in percent: a sliver at its center.
172
+ * @deprecated Dialogs no longer unfold from their center. Kept for consumers that animate their own surfaces with it.
173
+ */
144
174
  export declare const MOTION_CENTER_MORPH_FOLDED_PERCENT = 48;
145
175
  export declare const MOTION_CONTENT_DISTANCE = 4;
176
+ /**
177
+ * @deprecated Invalid fields no longer shake: the error is told by color and message. Kept for consumers that
178
+ * animate their own fields.
179
+ */
146
180
  export declare const MOTION_INVALID_SHAKE_PX: readonly [0, -6, 6, -4, 4, -2, 0];
147
181
  /** Time after an item loses its active state during which a new active item still glides from it. */
148
182
  export declare const GLIDE_CONTINUITY_MS = 300;
149
- export type MotionKind = "press" | "subtle-press" | "toggle" | "fade" | "content" | "message" | "card" | "row" | "field" | "thumb" | "check" | "selection" | "item-check" | "rotate" | "list-item" | "icon-swap" | "delayed-pop" | "skeleton" | "reveal" | "link" | "icon" | "tooltip" | "surface" | "dialog" | "center-morph" | "sheet" | "overlay";
183
+ export type MotionKind = "press" | "subtle-press" | "toggle" | "fade" | "content" | "message" | "card" | "row" | "field" | "thumb" | "check" | "selection" | "item-check" | "rotate" | "list-item" | "icon-swap" | "delayed-pop" | "skeleton" | "reveal" | "link" | "icon" | "tooltip" | "surface" | "dialog"
184
+ /** @deprecated Library dialogs use `dialog`. */
185
+ | "center-morph" | "sheet" | "overlay";