@carbon/motion 11.48.0 → 11.49.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.
package/src/index.ts CHANGED
@@ -5,69 +5,5 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- export const fast01 = '70ms';
9
- export const fast02 = '110ms';
10
- export const moderate01 = '150ms';
11
- export const moderate02 = '240ms';
12
- export const slow01 = '400ms';
13
- export const slow02 = '700ms';
14
- // V11 Tokens
15
- export const durationFast01 = fast01;
16
- export const durationFast02 = fast02;
17
- export const durationModerate01 = moderate01;
18
- export const durationModerate02 = moderate02;
19
- export const durationSlow01 = slow01;
20
- export const durationSlow02 = slow02;
21
-
22
- export const unstable_tokens = [
23
- 'fast01',
24
- 'fast02',
25
- 'moderate01',
26
- 'moderate02',
27
- 'slow01',
28
- 'slow02',
29
- 'durationFast01',
30
- 'durationFast02',
31
- 'durationModerate01',
32
- 'durationModerate02',
33
- 'durationSlow01',
34
- 'durationSlow02',
35
- ] as const;
36
-
37
- export type EasingName = 'standard' | 'entrance' | 'exit';
38
- export type EasingMode = 'productive' | 'expressive';
39
- export type EasingMap = Record<EasingName, Record<EasingMode, string>>;
40
-
41
- export const easings: EasingMap = {
42
- standard: {
43
- productive: 'cubic-bezier(0.2, 0, 0.38, 0.9)',
44
- expressive: 'cubic-bezier(0.4, 0.14, 0.3, 1)',
45
- },
46
- entrance: {
47
- productive: 'cubic-bezier(0, 0, 0.38, 0.9)',
48
- expressive: 'cubic-bezier(0, 0, 0.3, 1)',
49
- },
50
- exit: {
51
- productive: 'cubic-bezier(0.2, 0, 1, 0.9)',
52
- expressive: 'cubic-bezier(0.4, 0.14, 1, 1)',
53
- },
54
- };
55
-
56
- export const motion = (name: EasingName, mode: EasingMode) => {
57
- if (!easings[name]) {
58
- throw new Error(
59
- `Unable to find easing \`${name}\` in our supported easings. Expected ` +
60
- `one of: ${Object.keys(easings).join(', ')}`
61
- );
62
- }
63
-
64
- const easing = easings[name];
65
- if (!easing[mode]) {
66
- throw new Error(
67
- `Unable to find a mode for the easing \`${name}\` called: \`${mode}\`. ` +
68
- `Expected one of: ${Object.keys(easing).join(', ')}`
69
- );
70
- }
71
-
72
- return easing[mode];
73
- };
8
+ export * from './tokens';
9
+ export * from './surfaces';
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Copyright IBM Corp. 2026
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import type { DurationName, EasingMode, EasingName } from './tokens';
9
+
10
+ type MotionEasing = readonly [EasingName, EasingMode];
11
+
12
+ /**
13
+ * from/to styles for reveal surface - plain CSS property/value pairs
14
+ * keep values engine-neutral so CSS, WAAPI, and Motion can all consume
15
+ * them
16
+ */
17
+ type RevealKeyframe = Record<string, string | number>;
18
+
19
+ interface MotionSurfaceBase {
20
+ duration: DurationName;
21
+ enterEasing: MotionEasing;
22
+ exitEasing: MotionEasing;
23
+ }
24
+
25
+ // reveal surfaces animate a single element between from/to styles
26
+ // works on every engine, including plain CSS
27
+ interface RevealSurface extends MotionSurfaceBase {
28
+ kind: 'reveal';
29
+ enter: RevealKeyframe;
30
+ exit: RevealKeyframe;
31
+ }
32
+
33
+ // shared-element surfaces morph one element into another
34
+ // the adapter picks mechanism (Motion layout projection, View Transitions, FLIP)
35
+ // optional enter/exit keyframes document the CSS-replicable layer (opacity /
36
+ // scale) for Sass consumers and future View Transitions fallbacks
37
+ interface SharedElementSurface extends MotionSurfaceBase {
38
+ kind: 'shared-element';
39
+ enter?: RevealKeyframe;
40
+ exit?: RevealKeyframe;
41
+ // when set to `trigger`, morph starts from the invoking element
42
+ origin?: 'trigger';
43
+ }
44
+
45
+ export type MotionSurfaceDefinition = SharedElementSurface | RevealSurface;
46
+
47
+ /**
48
+ * Named motion intents. These definitions are engine and framework agnostic.
49
+ *
50
+ * `prefers-reduced-motion` is intentionally not represented here: surfaces
51
+ * never animate when the users request reduced motion. Framework adapters
52
+ * bail before running, and the Sass output is wrapped in a
53
+ * `prefers-reduced-motion: no-preference` media query
54
+ */
55
+ export const surfaces = {
56
+ // Accordion, table-row expand - reveal in place
57
+ disclosure: {
58
+ kind: 'reveal',
59
+ duration: 'moderate-01',
60
+ enter: { blockSize: 'auto', opacity: 1 },
61
+ exit: { blockSize: 0, opacity: 0 },
62
+ enterEasing: ['entrance', 'productive'],
63
+ exitEasing: ['exit', 'productive'],
64
+ },
65
+ // Icon > tooltip/popover
66
+ contextual: {
67
+ kind: 'reveal',
68
+ duration: 'fast-02',
69
+ enter: { opacity: 1, transform: 'scale(1)' },
70
+ exit: { opacity: 0, transform: 'scale(0.96)' },
71
+ enterEasing: ['entrance', 'expressive'],
72
+ exitEasing: ['exit', 'expressive'],
73
+ },
74
+ // Component reveal, "stretching" from vertical axis
75
+ stretch: {
76
+ kind: 'reveal',
77
+ duration: 'slow-01',
78
+ enter: { opacity: 1, clipPath: 'inset(0 0 0 0)' },
79
+ exit: { opacity: 0, clipPath: 'inset(50% 0 50% 0)' },
80
+ enterEasing: ['entrance', 'expressive'],
81
+ exitEasing: ['exit', 'expressive'],
82
+ },
83
+ // Card/tile > side-panel/tearsheet
84
+ expand: {
85
+ kind: 'shared-element',
86
+ duration: 'moderate-02',
87
+ enter: { opacity: 1, transform: 'scale(1)' },
88
+ exit: { opacity: 0, transform: 'scale(0.96)' },
89
+ enterEasing: ['standard', 'productive'],
90
+ exitEasing: ['standard', 'productive'],
91
+ },
92
+ // Button > modal/menu/popover - morphs from the trigger
93
+ invoke: {
94
+ kind: 'shared-element',
95
+ origin: 'trigger',
96
+ duration: 'moderate-02',
97
+ enterEasing: ['standard', 'expressive'],
98
+ exitEasing: ['standard', 'expressive'],
99
+ },
100
+ } as const satisfies Record<string, MotionSurfaceDefinition>;
101
+
102
+ export type MotionSurfaceName = keyof typeof surfaces;
103
+
104
+ // Give JavaScript consumers a clear error for an unknown surface name.
105
+ export const getMotionSurface = (name: MotionSurfaceName) => {
106
+ const surface = surfaces[name];
107
+
108
+ if (!surface) {
109
+ throw new Error(
110
+ `Unable to find motion surface \`${name}\`. Expected one of: ` +
111
+ Object.keys(surfaces).join(', ')
112
+ );
113
+ }
114
+
115
+ return surface;
116
+ };
package/src/tokens.ts ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Copyright IBM Corp. 2018, 2026
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ export const fast01 = '70ms';
9
+ export const fast02 = '110ms';
10
+ export const moderate01 = '150ms';
11
+ export const moderate02 = '240ms';
12
+ export const slow01 = '400ms';
13
+ export const slow02 = '700ms';
14
+
15
+ // V11 Tokens
16
+ export const durationFast01 = fast01;
17
+ export const durationFast02 = fast02;
18
+ export const durationModerate01 = moderate01;
19
+ export const durationModerate02 = moderate02;
20
+ export const durationSlow01 = slow01;
21
+ export const durationSlow02 = slow02;
22
+
23
+ export const unstable_tokens = [
24
+ 'fast01',
25
+ 'fast02',
26
+ 'moderate01',
27
+ 'moderate02',
28
+ 'slow01',
29
+ 'slow02',
30
+ 'durationFast01',
31
+ 'durationFast02',
32
+ 'durationModerate01',
33
+ 'durationModerate02',
34
+ 'durationSlow01',
35
+ 'durationSlow02',
36
+ ] as const;
37
+
38
+ export type DurationName =
39
+ | 'fast-01'
40
+ | 'fast-02'
41
+ | 'moderate-01'
42
+ | 'moderate-02'
43
+ | 'slow-01'
44
+ | 'slow-02';
45
+
46
+ // Map the surface names to the existing Carbon duration tokens.
47
+ const durations: Record<DurationName, string> = {
48
+ 'fast-01': durationFast01,
49
+ 'fast-02': durationFast02,
50
+ 'moderate-01': durationModerate01,
51
+ 'moderate-02': durationModerate02,
52
+ 'slow-01': durationSlow01,
53
+ 'slow-02': durationSlow02,
54
+ };
55
+
56
+ export type EasingName = 'standard' | 'entrance' | 'exit';
57
+ export type EasingMode = 'productive' | 'expressive';
58
+ export type CubicBezier = readonly [number, number, number, number];
59
+ export type EasingMap = Record<EasingName, Record<EasingMode, string>>;
60
+ type EasingCurveMap = Record<EasingName, Record<EasingMode, CubicBezier>>;
61
+
62
+ // Keep one numeric source for every Carbon easing curve.
63
+ const easingCurves: EasingCurveMap = {
64
+ standard: {
65
+ productive: [0.2, 0, 0.38, 0.9],
66
+ expressive: [0.4, 0.14, 0.3, 1],
67
+ },
68
+ entrance: {
69
+ productive: [0, 0, 0.38, 0.9],
70
+ expressive: [0, 0, 0.3, 1],
71
+ },
72
+ exit: {
73
+ productive: [0.2, 0, 1, 0.9],
74
+ expressive: [0.4, 0.14, 1, 1],
75
+ },
76
+ };
77
+
78
+ const formatEasing = (curve: CubicBezier) =>
79
+ `cubic-bezier(${curve.join(', ')})`;
80
+
81
+ export const easings: EasingMap = {
82
+ standard: {
83
+ productive: formatEasing(easingCurves.standard.productive),
84
+ expressive: formatEasing(easingCurves.standard.expressive),
85
+ },
86
+ entrance: {
87
+ productive: formatEasing(easingCurves.entrance.productive),
88
+ expressive: formatEasing(easingCurves.entrance.expressive),
89
+ },
90
+ exit: {
91
+ productive: formatEasing(easingCurves.exit.productive),
92
+ expressive: formatEasing(easingCurves.exit.expressive),
93
+ },
94
+ };
95
+
96
+ export const motion = (name: EasingName, mode: EasingMode) => {
97
+ if (!easings[name]) {
98
+ throw new Error(
99
+ `Unable to find easing \`${name}\` in our supported easings. Expected ` +
100
+ `one of: ${Object.keys(easings).join(', ')}`
101
+ );
102
+ }
103
+
104
+ const easing = easings[name];
105
+ if (!easing[mode]) {
106
+ throw new Error(
107
+ `Unable to find a mode for the easing \`${name}\` called: \`${mode}\`. ` +
108
+ `Expected one of: ${Object.keys(easing).join(', ')}`
109
+ );
110
+ }
111
+
112
+ return easing[mode];
113
+ };
114
+
115
+ // Return the numeric curve required by JavaScript animation engines.
116
+ export const resolveEasing = (name: EasingName, mode: EasingMode) => {
117
+ if (!easingCurves[name]) {
118
+ throw new Error(
119
+ `Unable to find easing \`${name}\` in our supported easings. Expected ` +
120
+ `one of: ${Object.keys(easingCurves).join(', ')}`
121
+ );
122
+ }
123
+
124
+ const easing = easingCurves[name];
125
+ if (!easing[mode]) {
126
+ throw new Error(
127
+ `Unable to find a mode for the easing \`${name}\` called: \`${mode}\`. ` +
128
+ `Expected one of: ${Object.keys(easing).join(', ')}`
129
+ );
130
+ }
131
+
132
+ return easing[mode];
133
+ };
134
+
135
+ // Return the existing Carbon duration for a named surface token.
136
+ export const resolveDuration = (name: DurationName) => {
137
+ const duration = durations[name];
138
+
139
+ if (!duration) {
140
+ throw new Error(
141
+ `Unable to find duration \`${name}\` in our supported durations. ` +
142
+ `Expected one of: ${Object.keys(durations).join(', ')}`
143
+ );
144
+ }
145
+
146
+ return duration;
147
+ };
package/umd/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.CarbonMotion = {}));
3
3
  })(this, function(exports) {
4
4
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
5
- //#region src/index.ts
5
+ //#region src/tokens.ts
6
6
  /**
7
7
  * Copyright IBM Corp. 2018, 2026
8
8
  *
@@ -35,18 +35,71 @@
35
35
  "durationSlow01",
36
36
  "durationSlow02"
37
37
  ];
38
+ const durations = {
39
+ "fast-01": durationFast01,
40
+ "fast-02": durationFast02,
41
+ "moderate-01": durationModerate01,
42
+ "moderate-02": durationModerate02,
43
+ "slow-01": durationSlow01,
44
+ "slow-02": durationSlow02
45
+ };
46
+ const easingCurves = {
47
+ standard: {
48
+ productive: [
49
+ .2,
50
+ 0,
51
+ .38,
52
+ .9
53
+ ],
54
+ expressive: [
55
+ .4,
56
+ .14,
57
+ .3,
58
+ 1
59
+ ]
60
+ },
61
+ entrance: {
62
+ productive: [
63
+ 0,
64
+ 0,
65
+ .38,
66
+ .9
67
+ ],
68
+ expressive: [
69
+ 0,
70
+ 0,
71
+ .3,
72
+ 1
73
+ ]
74
+ },
75
+ exit: {
76
+ productive: [
77
+ .2,
78
+ 0,
79
+ 1,
80
+ .9
81
+ ],
82
+ expressive: [
83
+ .4,
84
+ .14,
85
+ 1,
86
+ 1
87
+ ]
88
+ }
89
+ };
90
+ const formatEasing = (curve) => `cubic-bezier(${curve.join(", ")})`;
38
91
  const easings = {
39
92
  standard: {
40
- productive: "cubic-bezier(0.2, 0, 0.38, 0.9)",
41
- expressive: "cubic-bezier(0.4, 0.14, 0.3, 1)"
93
+ productive: formatEasing(easingCurves.standard.productive),
94
+ expressive: formatEasing(easingCurves.standard.expressive)
42
95
  },
43
96
  entrance: {
44
- productive: "cubic-bezier(0, 0, 0.38, 0.9)",
45
- expressive: "cubic-bezier(0, 0, 0.3, 1)"
97
+ productive: formatEasing(easingCurves.entrance.productive),
98
+ expressive: formatEasing(easingCurves.entrance.expressive)
46
99
  },
47
100
  exit: {
48
- productive: "cubic-bezier(0.2, 0, 1, 0.9)",
49
- expressive: "cubic-bezier(0.4, 0.14, 1, 1)"
101
+ productive: formatEasing(easingCurves.exit.productive),
102
+ expressive: formatEasing(easingCurves.exit.expressive)
50
103
  }
51
104
  };
52
105
  const motion = (name, mode) => {
@@ -55,6 +108,97 @@
55
108
  if (!easing[mode]) throw new Error(`Unable to find a mode for the easing \`${name}\` called: \`${mode}\`. Expected one of: ${Object.keys(easing).join(", ")}`);
56
109
  return easing[mode];
57
110
  };
111
+ const resolveEasing = (name, mode) => {
112
+ if (!easingCurves[name]) throw new Error(`Unable to find easing \`${name}\` in our supported easings. Expected one of: ${Object.keys(easingCurves).join(", ")}`);
113
+ const easing = easingCurves[name];
114
+ if (!easing[mode]) throw new Error(`Unable to find a mode for the easing \`${name}\` called: \`${mode}\`. Expected one of: ${Object.keys(easing).join(", ")}`);
115
+ return easing[mode];
116
+ };
117
+ const resolveDuration = (name) => {
118
+ const duration = durations[name];
119
+ if (!duration) throw new Error(`Unable to find duration \`${name}\` in our supported durations. Expected one of: ${Object.keys(durations).join(", ")}`);
120
+ return duration;
121
+ };
122
+ //#endregion
123
+ //#region src/surfaces.ts
124
+ /**
125
+ * Named motion intents. These definitions are engine and framework agnostic.
126
+ *
127
+ * `prefers-reduced-motion` is intentionally not represented here: surfaces
128
+ * never animate when the users request reduced motion. Framework adapters
129
+ * bail before running, and the Sass output is wrapped in a
130
+ * `prefers-reduced-motion: no-preference` media query
131
+ */
132
+ const surfaces = {
133
+ disclosure: {
134
+ kind: "reveal",
135
+ duration: "moderate-01",
136
+ enter: {
137
+ blockSize: "auto",
138
+ opacity: 1
139
+ },
140
+ exit: {
141
+ blockSize: 0,
142
+ opacity: 0
143
+ },
144
+ enterEasing: ["entrance", "productive"],
145
+ exitEasing: ["exit", "productive"]
146
+ },
147
+ contextual: {
148
+ kind: "reveal",
149
+ duration: "fast-02",
150
+ enter: {
151
+ opacity: 1,
152
+ transform: "scale(1)"
153
+ },
154
+ exit: {
155
+ opacity: 0,
156
+ transform: "scale(0.96)"
157
+ },
158
+ enterEasing: ["entrance", "expressive"],
159
+ exitEasing: ["exit", "expressive"]
160
+ },
161
+ stretch: {
162
+ kind: "reveal",
163
+ duration: "slow-01",
164
+ enter: {
165
+ opacity: 1,
166
+ clipPath: "inset(0 0 0 0)"
167
+ },
168
+ exit: {
169
+ opacity: 0,
170
+ clipPath: "inset(50% 0 50% 0)"
171
+ },
172
+ enterEasing: ["entrance", "expressive"],
173
+ exitEasing: ["exit", "expressive"]
174
+ },
175
+ expand: {
176
+ kind: "shared-element",
177
+ duration: "moderate-02",
178
+ enter: {
179
+ opacity: 1,
180
+ transform: "scale(1)"
181
+ },
182
+ exit: {
183
+ opacity: 0,
184
+ transform: "scale(0.96)"
185
+ },
186
+ enterEasing: ["standard", "productive"],
187
+ exitEasing: ["standard", "productive"]
188
+ },
189
+ invoke: {
190
+ kind: "shared-element",
191
+ origin: "trigger",
192
+ duration: "moderate-02",
193
+ enterEasing: ["standard", "expressive"],
194
+ exitEasing: ["standard", "expressive"]
195
+ }
196
+ };
197
+ const getMotionSurface = (name) => {
198
+ const surface = surfaces[name];
199
+ if (!surface) throw new Error(`Unable to find motion surface \`${name}\`. Expected one of: ` + Object.keys(surfaces).join(", "));
200
+ return surface;
201
+ };
58
202
  //#endregion
59
203
  exports.durationFast01 = durationFast01;
60
204
  exports.durationFast02 = durationFast02;
@@ -65,10 +209,14 @@
65
209
  exports.easings = easings;
66
210
  exports.fast01 = fast01;
67
211
  exports.fast02 = fast02;
212
+ exports.getMotionSurface = getMotionSurface;
68
213
  exports.moderate01 = moderate01;
69
214
  exports.moderate02 = moderate02;
70
215
  exports.motion = motion;
216
+ exports.resolveDuration = resolveDuration;
217
+ exports.resolveEasing = resolveEasing;
71
218
  exports.slow01 = slow01;
72
219
  exports.slow02 = slow02;
220
+ exports.surfaces = surfaces;
73
221
  exports.unstable_tokens = unstable_tokens;
74
222
  });