@castui/cast-ui 4.8.0 → 4.10.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.
@@ -44,6 +44,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
44
44
  const react_1 = require("react");
45
45
  const themes_1 = require("./themes");
46
46
  const colors_1 = require("../tokens/colors");
47
+ const motion_1 = require("../tokens/motion");
47
48
  // ---------------------------------------------------------------------------
48
49
  // Deep merge utility (for partial colour overrides)
49
50
  // ---------------------------------------------------------------------------
@@ -77,9 +78,10 @@ const defaultTheme = {
77
78
  scheme: colors_1.colorSchemes.light,
78
79
  colors: colors_1.colorSchemes.light.intents,
79
80
  disabledColors: colors_1.colorSchemes.light.disabled,
81
+ motion: motion_1.motionTokens,
80
82
  };
81
83
  const ThemeContext = (0, react_1.createContext)(defaultTheme);
82
- function ThemeProvider({ density = 'default', colorMode = 'light', colors, scheme: schemeOverride, children, }) {
84
+ function ThemeProvider({ density = 'default', colorMode = 'light', colors, scheme: schemeOverride, motion: motionOverrides, children, }) {
83
85
  const theme = (0, react_1.useMemo)(() => {
84
86
  const baseScheme = colors_1.colorSchemes[colorMode];
85
87
  const resolvedIntents = colors
@@ -98,8 +100,9 @@ function ThemeProvider({ density = 'default', colorMode = 'light', colors, schem
98
100
  scheme,
99
101
  colors: scheme.intents,
100
102
  disabledColors: scheme.disabled,
103
+ motion: (0, motion_1.resolveMotion)(motionOverrides),
101
104
  };
102
- }, [density, colorMode, colors, schemeOverride]);
105
+ }, [density, colorMode, colors, schemeOverride, motionOverrides]);
103
106
  return ((0, jsx_runtime_1.jsx)(ThemeContext.Provider, { value: theme, children: children }));
104
107
  }
105
108
  // ---------------------------------------------------------------------------
@@ -42,7 +42,7 @@ export type CastThemeFile = {
42
42
  name?: string;
43
43
  description?: string;
44
44
  generatedAt?: string;
45
- /** Theme-file format version emitted by the plugin (currently 3). */
45
+ /** Theme-file format version emitted by the plugin (version 4 adds motion). */
46
46
  version?: number;
47
47
  /** Optional explicit schema version for consumer validation. */
48
48
  schemaVersion?: number;
@@ -61,10 +61,40 @@ export type CastThemeFile = {
61
61
  }>>;
62
62
  typography?: Record<string, unknown>;
63
63
  shadows?: Record<string, unknown>;
64
+ /**
65
+ * Motion block exported from the kit's `motion` variable collection
66
+ * (cast-theme version 4+). `easing` carries cubic-bezier control points
67
+ * as [x1, y1, x2, y2]. Motion is mode-independent, so this block is not
68
+ * keyed by colour mode.
69
+ */
70
+ motion?: {
71
+ duration?: Record<string, number>;
72
+ cycle?: Record<string, number>;
73
+ easing?: Record<string, readonly number[]>;
74
+ spring?: Record<string, {
75
+ damping?: number;
76
+ stiffness?: number;
77
+ mass?: number;
78
+ }>;
79
+ feedback?: {
80
+ press?: {
81
+ scale?: number;
82
+ };
83
+ shake?: {
84
+ amplitude?: number;
85
+ };
86
+ };
87
+ loop?: {
88
+ pulse?: {
89
+ from?: number;
90
+ to?: number;
91
+ };
92
+ };
93
+ };
64
94
  [key: string]: unknown;
65
95
  };
66
96
  /** The subset of ThemeProvider props this helper produces. */
67
- export type CastThemeProps = Pick<ThemeProviderProps, 'colorMode' | 'colors' | 'scheme'>;
97
+ export type CastThemeProps = Pick<ThemeProviderProps, 'colorMode' | 'colors' | 'scheme' | 'motion'>;
68
98
  /**
69
99
  * Build ThemeProvider props from a cast-theme file for a given colour mode.
70
100
  *
@@ -33,6 +33,77 @@
33
33
  */
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.applyCastTheme = applyCastTheme;
36
+ const EASING_NAMES = ['standard', 'entrance', 'exit', 'emphasized', 'linear'];
37
+ const DURATION_KEYS = ['instant', 'fast', 'base', 'slow'];
38
+ const CYCLE_KEYS = ['pulse', 'spin', 'sweep'];
39
+ function pickNumbers(source, keys) {
40
+ if (!source)
41
+ return undefined;
42
+ const out = {};
43
+ for (const key of keys) {
44
+ const value = source[key];
45
+ if (typeof value === 'number' && Number.isFinite(value))
46
+ out[key] = value;
47
+ }
48
+ return Object.keys(out).length > 0 ? out : undefined;
49
+ }
50
+ /** Map the file's `motion` block onto ThemeProvider motion overrides. */
51
+ function mapMotion(fileMotion) {
52
+ if (!fileMotion || typeof fileMotion !== 'object')
53
+ return undefined;
54
+ const out = {};
55
+ const durations = pickNumbers(fileMotion.duration, DURATION_KEYS);
56
+ if (durations)
57
+ out.duration = durations;
58
+ const cycles = pickNumbers(fileMotion.cycle, CYCLE_KEYS);
59
+ if (cycles)
60
+ out.cycle = cycles;
61
+ if (fileMotion.easing) {
62
+ const beziers = {};
63
+ for (const name of EASING_NAMES) {
64
+ const pts = fileMotion.easing[name];
65
+ if (Array.isArray(pts) &&
66
+ pts.length === 4 &&
67
+ pts.every((n) => typeof n === 'number' && Number.isFinite(n))) {
68
+ beziers[name] = [pts[0], pts[1], pts[2], pts[3]];
69
+ }
70
+ }
71
+ if (Object.keys(beziers).length > 0)
72
+ out.easingBezier = beziers;
73
+ }
74
+ const overlay = fileMotion.spring?.overlay;
75
+ if (overlay && typeof overlay === 'object') {
76
+ const springOut = {};
77
+ if (typeof overlay.damping === 'number')
78
+ springOut.damping = overlay.damping;
79
+ if (typeof overlay.stiffness === 'number')
80
+ springOut.stiffness = overlay.stiffness;
81
+ if (typeof overlay.mass === 'number')
82
+ springOut.mass = overlay.mass;
83
+ if (Object.keys(springOut).length > 0)
84
+ out.spring = { overlay: springOut };
85
+ }
86
+ const press = fileMotion.feedback?.press;
87
+ const shake = fileMotion.feedback?.shake;
88
+ const fb = {};
89
+ if (press && typeof press.scale === 'number')
90
+ fb.press = { scale: press.scale };
91
+ if (shake && typeof shake.amplitude === 'number')
92
+ fb.shake = { amplitude: shake.amplitude };
93
+ if (Object.keys(fb).length > 0)
94
+ out.feedback = fb;
95
+ const pulse = fileMotion.loop?.pulse;
96
+ if (pulse && typeof pulse === 'object') {
97
+ const pulseOut = {};
98
+ if (typeof pulse.from === 'number')
99
+ pulseOut.from = pulse.from;
100
+ if (typeof pulse.to === 'number')
101
+ pulseOut.to = pulse.to;
102
+ if (Object.keys(pulseOut).length > 0)
103
+ out.loop = { pulse: pulseOut };
104
+ }
105
+ return Object.keys(out).length > 0 ? out : undefined;
106
+ }
36
107
  /** Map the file's `text` block onto the scheme's `text` slots (matching keys only). */
37
108
  function mapText(fileText) {
38
109
  if (!fileText)
@@ -91,5 +162,6 @@ function applyCastTheme(theme, mode = 'light') {
91
162
  colorMode: mode,
92
163
  colors: intents,
93
164
  scheme: Object.keys(schemeOverride).length > 0 ? schemeOverride : undefined,
165
+ motion: mapMotion(theme?.motion),
94
166
  };
95
167
  }
@@ -1,4 +1,5 @@
1
1
  export { ThemeProvider, useTheme, type ThemeProviderProps, type Theme } from './ThemeContext';
2
2
  export { themes } from './themes';
3
+ export { useMotion, type Motion } from './useMotion';
3
4
  export { applyCastTheme, type CastThemeFile, type CastThemeProps, } from './applyCastTheme';
4
5
  export type { DensityTheme, ComponentTokens, AccordionSizeTokens, AccordionThemeTokens, ButtonSizeTokens, ButtonThemeTokens, DialogSizeTokens, DialogThemeTokens, InputSizeTokens, InputThemeTokens, SelectContentTokens, SelectOptionTokens, SelectGroupTokens, SelectSeparatorTokens, SelectThemeTokens, ListItemTokens, ListSubheaderTokens, ListThemeTokens, CheckboxSizeTokens, CheckboxThemeTokens, AlertSizeTokens, AlertThemeTokens, ToggleSizeTokens, ToggleThemeTokens, CardSizeTokens, CardThemeTokens, BadgeSizeTokens, BadgeThemeTokens, RadioSizeTokens, RadioThemeTokens, ToastSizeTokens, ToastThemeTokens, ChipSizeTokens, ChipThemeTokens, AvatarSizeTokens, AvatarThemeTokens, PopoverSizeTokens, PopoverThemeTokens, TooltipSizeTokens, TooltipThemeTokens, ProgressSizeTokens, ProgressThemeTokens, TabsSizeTokens, TabsThemeTokens, SpinnerSizeTokens, SpinnerThemeTokens, BottomSheetThemeTokens, LinkSizeTokens, LinkThemeTokens, BreadcrumbsSizeTokens, BreadcrumbsThemeTokens, CodeBlockSizeTokens, CodeBlockThemeTokens, DrawerThemeTokens, MenuItemTokens, MenuGroupTokens, MenuThemeTokens, ToggleButtonGroupSizeTokens, ToggleButtonGroupThemeTokens, AppBarSizeTokens, AppBarThemeTokens, SliderSizeTokens, SliderThemeTokens, SpeedDialSizeTokens, SpeedDialThemeTokens, TableSizeTokens, TableThemeTokens, DeepPartial, } from './types';
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.applyCastTheme = exports.themes = exports.useTheme = exports.ThemeProvider = void 0;
3
+ exports.applyCastTheme = exports.useMotion = exports.themes = exports.useTheme = exports.ThemeProvider = void 0;
4
4
  var ThemeContext_1 = require("./ThemeContext");
5
5
  Object.defineProperty(exports, "ThemeProvider", { enumerable: true, get: function () { return ThemeContext_1.ThemeProvider; } });
6
6
  Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return ThemeContext_1.useTheme; } });
7
7
  var themes_1 = require("./themes");
8
8
  Object.defineProperty(exports, "themes", { enumerable: true, get: function () { return themes_1.themes; } });
9
+ var useMotion_1 = require("./useMotion");
10
+ Object.defineProperty(exports, "useMotion", { enumerable: true, get: function () { return useMotion_1.useMotion; } });
9
11
  var applyCastTheme_1 = require("./applyCastTheme");
10
12
  Object.defineProperty(exports, "applyCastTheme", { enumerable: true, get: function () { return applyCastTheme_1.applyCastTheme; } });
@@ -0,0 +1,32 @@
1
+ /**
2
+ * useMotion — the single access point for animation in Cast UI.
3
+ *
4
+ * Returns the theme's motion tokens plus three runtime helpers:
5
+ * reduceMotion true when the OS "reduce motion" setting is on.
6
+ * useNativeDriver the Animated.timing native-driver flag (false on web).
7
+ * scale(ms) a duration in ms, collapsed to 0 when reduceMotion is on.
8
+ *
9
+ * Components read a semantic role for the value and wrap durations in scale(),
10
+ * so the whole library honours reduce-motion from one place:
11
+ *
12
+ * const motion = useMotion();
13
+ * Animated.timing(v, {
14
+ * toValue: 1,
15
+ * duration: motion.scale(motion.transition.standard.duration),
16
+ * easing: motion.transition.standard.easing,
17
+ * useNativeDriver: motion.useNativeDriver,
18
+ * });
19
+ *
20
+ * For continuous loops (spinner, skeleton, progress), check reduceMotion and
21
+ * skip starting the loop.
22
+ */
23
+ import type { MotionTokens } from '../tokens/motion';
24
+ export type Motion = MotionTokens & {
25
+ /** True when the OS "reduce motion" accessibility setting is enabled. */
26
+ reduceMotion: boolean;
27
+ /** Animated useNativeDriver value. False on web (the web driver can't animate layout). */
28
+ useNativeDriver: boolean;
29
+ /** Returns the given duration, or 0 when reduce-motion is on. */
30
+ scale: (ms: number) => number;
31
+ };
32
+ export declare function useMotion(): Motion;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * useMotion — the single access point for animation in Cast UI.
4
+ *
5
+ * Returns the theme's motion tokens plus three runtime helpers:
6
+ * reduceMotion true when the OS "reduce motion" setting is on.
7
+ * useNativeDriver the Animated.timing native-driver flag (false on web).
8
+ * scale(ms) a duration in ms, collapsed to 0 when reduceMotion is on.
9
+ *
10
+ * Components read a semantic role for the value and wrap durations in scale(),
11
+ * so the whole library honours reduce-motion from one place:
12
+ *
13
+ * const motion = useMotion();
14
+ * Animated.timing(v, {
15
+ * toValue: 1,
16
+ * duration: motion.scale(motion.transition.standard.duration),
17
+ * easing: motion.transition.standard.easing,
18
+ * useNativeDriver: motion.useNativeDriver,
19
+ * });
20
+ *
21
+ * For continuous loops (spinner, skeleton, progress), check reduceMotion and
22
+ * skip starting the loop.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.useMotion = useMotion;
26
+ const react_1 = require("react");
27
+ const react_native_1 = require("react-native");
28
+ const ThemeContext_1 = require("./ThemeContext");
29
+ function useMotion() {
30
+ const { motion } = (0, ThemeContext_1.useTheme)();
31
+ const [reduceMotion, setReduceMotion] = (0, react_1.useState)(false);
32
+ (0, react_1.useEffect)(() => {
33
+ let active = true;
34
+ const read = react_native_1.AccessibilityInfo.isReduceMotionEnabled;
35
+ if (typeof read === 'function') {
36
+ read()
37
+ .then((value) => {
38
+ if (active)
39
+ setReduceMotion(!!value);
40
+ })
41
+ .catch(() => { });
42
+ }
43
+ const sub = react_native_1.AccessibilityInfo.addEventListener?.('reduceMotionChanged', (value) => setReduceMotion(!!value));
44
+ return () => {
45
+ active = false;
46
+ sub?.remove?.();
47
+ };
48
+ }, []);
49
+ return {
50
+ ...motion,
51
+ reduceMotion,
52
+ useNativeDriver: react_native_1.Platform.OS !== 'web',
53
+ scale: (ms) => (reduceMotion ? 0 : ms),
54
+ };
55
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Breakpoint scale — named min-width thresholds in density-independent pixels.
3
+ *
4
+ * Mirrors the Figma `breakpoint/{sm,md,lg,xl}` primitive variables. This is a
5
+ * foundation reference scale, not a runtime theme value. The numbers do not
6
+ * change with density, they are not part of the ThemeProvider theme, and
7
+ * cast-sync does not export them. Read them with the useBreakpoint /
8
+ * useResponsiveValue hooks, which watch the live window width.
9
+ *
10
+ * Values follow Material 3 window size classes, which are derived from real
11
+ * device-width data across the whole spectrum. The scale is mobile-first: a
12
+ * width is "at" a breakpoint when it is greater than or equal to that
13
+ * breakpoint's value, and anything below `sm` is the implicit `base` tier.
14
+ *
15
+ * Device mapping (width in dp):
16
+ *
17
+ * base < 600 Watches, and every phone in portrait. iPhone SE 320,
18
+ * iPhone 15 390, Pro Max 430, Pixel 7 412, Apple Watch
19
+ * 136-205. This is your default, design-here-first layout.
20
+ * sm >= 600 Large phones in landscape, foldables unfolded, small
21
+ * tablets in portrait.
22
+ * md >= 840 Tablets. iPad portrait ~768-834 (sits at the top of sm),
23
+ * iPad landscape and larger tablets land here and up.
24
+ * lg >= 1200 Laptops and desktops.
25
+ * xl >= 1600 Large desktops and TVs.
26
+ *
27
+ * React Native has no CSS media queries, so a breakpoint here is a width
28
+ * threshold you compare the window against at runtime. It does not restyle
29
+ * anything on its own. Small-vs-large phone differences belong to fluid layout
30
+ * (flex, percentages, maxWidth), not breakpoints. A watch is its own surface,
31
+ * not a tier: it falls into `base` and shares the default layout, which is the
32
+ * safe behaviour for a kit that does not target watchOS.
33
+ */
34
+ /** The named breakpoint tiers. */
35
+ export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl';
36
+ /** A breakpoint tier including the implicit below-`sm` base tier. */
37
+ export type BreakpointKey = 'base' | Breakpoint;
38
+ /** Min-width thresholds in dp (Material 3 window size classes). */
39
+ export declare const breakpoints: Record<Breakpoint, number>;
40
+ /** Tiers ordered smallest to largest, base first. */
41
+ export declare const breakpointOrder: readonly BreakpointKey[];
42
+ /**
43
+ * Resolve a raw window width (dp) to its active breakpoint tier. Mobile-first:
44
+ * returns the largest tier whose threshold the width has reached, or `base`
45
+ * below `sm`.
46
+ */
47
+ export declare function resolveBreakpoint(width: number): BreakpointKey;
48
+ /**
49
+ * Pick a value for a tier, mobile-first. Supply values for any subset of tiers.
50
+ * The chosen value is the one at the current tier or, if that tier has none,
51
+ * the nearest defined tier below it. If nothing is defined at or below the
52
+ * current tier, the smallest defined tier above is used. Returns undefined when
53
+ * `values` is empty.
54
+ *
55
+ * resolveResponsiveValue({ base: 1, md: 2, xl: 4 }, 'lg') // => 2
56
+ */
57
+ export declare function resolveResponsiveValue<T>(values: Partial<Record<BreakpointKey, T>>, current: BreakpointKey): T | undefined;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Breakpoint scale — named min-width thresholds in density-independent pixels.
4
+ *
5
+ * Mirrors the Figma `breakpoint/{sm,md,lg,xl}` primitive variables. This is a
6
+ * foundation reference scale, not a runtime theme value. The numbers do not
7
+ * change with density, they are not part of the ThemeProvider theme, and
8
+ * cast-sync does not export them. Read them with the useBreakpoint /
9
+ * useResponsiveValue hooks, which watch the live window width.
10
+ *
11
+ * Values follow Material 3 window size classes, which are derived from real
12
+ * device-width data across the whole spectrum. The scale is mobile-first: a
13
+ * width is "at" a breakpoint when it is greater than or equal to that
14
+ * breakpoint's value, and anything below `sm` is the implicit `base` tier.
15
+ *
16
+ * Device mapping (width in dp):
17
+ *
18
+ * base < 600 Watches, and every phone in portrait. iPhone SE 320,
19
+ * iPhone 15 390, Pro Max 430, Pixel 7 412, Apple Watch
20
+ * 136-205. This is your default, design-here-first layout.
21
+ * sm >= 600 Large phones in landscape, foldables unfolded, small
22
+ * tablets in portrait.
23
+ * md >= 840 Tablets. iPad portrait ~768-834 (sits at the top of sm),
24
+ * iPad landscape and larger tablets land here and up.
25
+ * lg >= 1200 Laptops and desktops.
26
+ * xl >= 1600 Large desktops and TVs.
27
+ *
28
+ * React Native has no CSS media queries, so a breakpoint here is a width
29
+ * threshold you compare the window against at runtime. It does not restyle
30
+ * anything on its own. Small-vs-large phone differences belong to fluid layout
31
+ * (flex, percentages, maxWidth), not breakpoints. A watch is its own surface,
32
+ * not a tier: it falls into `base` and shares the default layout, which is the
33
+ * safe behaviour for a kit that does not target watchOS.
34
+ */
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.breakpointOrder = exports.breakpoints = void 0;
37
+ exports.resolveBreakpoint = resolveBreakpoint;
38
+ exports.resolveResponsiveValue = resolveResponsiveValue;
39
+ /** Min-width thresholds in dp (Material 3 window size classes). */
40
+ exports.breakpoints = {
41
+ sm: 600,
42
+ md: 840,
43
+ lg: 1200,
44
+ xl: 1600,
45
+ };
46
+ /** Tiers ordered smallest to largest, base first. */
47
+ exports.breakpointOrder = [
48
+ 'base',
49
+ 'sm',
50
+ 'md',
51
+ 'lg',
52
+ 'xl',
53
+ ];
54
+ /**
55
+ * Resolve a raw window width (dp) to its active breakpoint tier. Mobile-first:
56
+ * returns the largest tier whose threshold the width has reached, or `base`
57
+ * below `sm`.
58
+ */
59
+ function resolveBreakpoint(width) {
60
+ if (width >= exports.breakpoints.xl)
61
+ return 'xl';
62
+ if (width >= exports.breakpoints.lg)
63
+ return 'lg';
64
+ if (width >= exports.breakpoints.md)
65
+ return 'md';
66
+ if (width >= exports.breakpoints.sm)
67
+ return 'sm';
68
+ return 'base';
69
+ }
70
+ /**
71
+ * Pick a value for a tier, mobile-first. Supply values for any subset of tiers.
72
+ * The chosen value is the one at the current tier or, if that tier has none,
73
+ * the nearest defined tier below it. If nothing is defined at or below the
74
+ * current tier, the smallest defined tier above is used. Returns undefined when
75
+ * `values` is empty.
76
+ *
77
+ * resolveResponsiveValue({ base: 1, md: 2, xl: 4 }, 'lg') // => 2
78
+ */
79
+ function resolveResponsiveValue(values, current) {
80
+ const idx = exports.breakpointOrder.indexOf(current);
81
+ for (let i = idx; i >= 0; i--) {
82
+ const v = values[exports.breakpointOrder[i]];
83
+ if (v !== undefined)
84
+ return v;
85
+ }
86
+ for (let i = idx + 1; i < exports.breakpointOrder.length; i++) {
87
+ const v = values[exports.breakpointOrder[i]];
88
+ if (v !== undefined)
89
+ return v;
90
+ }
91
+ return undefined;
92
+ }
@@ -1,3 +1,5 @@
1
1
  export { lightColors, darkColors, colorSchemes, intentColors, disabledColors, controlTokens, surfaceTokens, textTokens, overlayTokens, selectColors, menuColors, tagTokens, errorTokens, listColors, checkboxColors, toggleColors, progressColors, tabsColors, radioColors, avatarColors, skeletonColors, sliderColors, tableColors, type IntentName, type ProminenceName, type StateName, type ColorMode, type ColorScheme, } from './colors';
2
2
  export { fontFamily, fontWeight, label, title, body, heading, display, caption, type LabelSize, } from './typography';
3
3
  export { iconSize, type IconSize } from './icon';
4
+ export { breakpoints, breakpointOrder, resolveBreakpoint, resolveResponsiveValue, type Breakpoint, type BreakpointKey, } from './breakpoints';
5
+ export { duration, cycle, easing, easingBezier, spring, transition, feedback, loop, motionTokens, resolveMotion, type MotionTokens, type MotionTransition, type MotionOverrides, type MotionDurations, type MotionCycles, type EasingName, type EasingBezierPoints, type SpringConfig, } from './motion';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.iconSize = exports.caption = exports.display = exports.heading = exports.body = exports.title = exports.label = exports.fontWeight = exports.fontFamily = exports.tableColors = exports.sliderColors = exports.skeletonColors = exports.avatarColors = exports.radioColors = exports.tabsColors = exports.progressColors = exports.toggleColors = exports.checkboxColors = exports.listColors = exports.errorTokens = exports.tagTokens = exports.menuColors = exports.selectColors = exports.overlayTokens = exports.textTokens = exports.surfaceTokens = exports.controlTokens = exports.disabledColors = exports.intentColors = exports.colorSchemes = exports.darkColors = exports.lightColors = void 0;
3
+ exports.resolveMotion = exports.motionTokens = exports.loop = exports.feedback = exports.transition = exports.spring = exports.easingBezier = exports.easing = exports.cycle = exports.duration = exports.resolveResponsiveValue = exports.resolveBreakpoint = exports.breakpointOrder = exports.breakpoints = exports.iconSize = exports.caption = exports.display = exports.heading = exports.body = exports.title = exports.label = exports.fontWeight = exports.fontFamily = exports.tableColors = exports.sliderColors = exports.skeletonColors = exports.avatarColors = exports.radioColors = exports.tabsColors = exports.progressColors = exports.toggleColors = exports.checkboxColors = exports.listColors = exports.errorTokens = exports.tagTokens = exports.menuColors = exports.selectColors = exports.overlayTokens = exports.textTokens = exports.surfaceTokens = exports.controlTokens = exports.disabledColors = exports.intentColors = exports.colorSchemes = exports.darkColors = exports.lightColors = void 0;
4
4
  var colors_1 = require("./colors");
5
5
  Object.defineProperty(exports, "lightColors", { enumerable: true, get: function () { return colors_1.lightColors; } });
6
6
  Object.defineProperty(exports, "darkColors", { enumerable: true, get: function () { return colors_1.darkColors; } });
@@ -36,3 +36,19 @@ Object.defineProperty(exports, "display", { enumerable: true, get: function () {
36
36
  Object.defineProperty(exports, "caption", { enumerable: true, get: function () { return typography_1.caption; } });
37
37
  var icon_1 = require("./icon");
38
38
  Object.defineProperty(exports, "iconSize", { enumerable: true, get: function () { return icon_1.iconSize; } });
39
+ var breakpoints_1 = require("./breakpoints");
40
+ Object.defineProperty(exports, "breakpoints", { enumerable: true, get: function () { return breakpoints_1.breakpoints; } });
41
+ Object.defineProperty(exports, "breakpointOrder", { enumerable: true, get: function () { return breakpoints_1.breakpointOrder; } });
42
+ Object.defineProperty(exports, "resolveBreakpoint", { enumerable: true, get: function () { return breakpoints_1.resolveBreakpoint; } });
43
+ Object.defineProperty(exports, "resolveResponsiveValue", { enumerable: true, get: function () { return breakpoints_1.resolveResponsiveValue; } });
44
+ var motion_1 = require("./motion");
45
+ Object.defineProperty(exports, "duration", { enumerable: true, get: function () { return motion_1.duration; } });
46
+ Object.defineProperty(exports, "cycle", { enumerable: true, get: function () { return motion_1.cycle; } });
47
+ Object.defineProperty(exports, "easing", { enumerable: true, get: function () { return motion_1.easing; } });
48
+ Object.defineProperty(exports, "easingBezier", { enumerable: true, get: function () { return motion_1.easingBezier; } });
49
+ Object.defineProperty(exports, "spring", { enumerable: true, get: function () { return motion_1.spring; } });
50
+ Object.defineProperty(exports, "transition", { enumerable: true, get: function () { return motion_1.transition; } });
51
+ Object.defineProperty(exports, "feedback", { enumerable: true, get: function () { return motion_1.feedback; } });
52
+ Object.defineProperty(exports, "loop", { enumerable: true, get: function () { return motion_1.loop; } });
53
+ Object.defineProperty(exports, "motionTokens", { enumerable: true, get: function () { return motion_1.motionTokens; } });
54
+ Object.defineProperty(exports, "resolveMotion", { enumerable: true, get: function () { return motion_1.resolveMotion; } });
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Motion tokens — the single source for animation timing across Cast UI.
3
+ *
4
+ * Two layers, mirroring the colour and spacing token system:
5
+ * Primitive raw durations, cubic-bezier easings, spring configs.
6
+ * Semantic named roles (transition / feedback / loop) that components read.
7
+ *
8
+ * Motion is constant across density and colour mode. Density changes spacing
9
+ * only. Easings are real cubic-beziers, so the same numbers map 1:1 to the
10
+ * `motion` variable collection in the Figma kit and to CSS.
11
+ *
12
+ * Components never read a raw number. They read a semantic role through
13
+ * useMotion(), for example motion.transition.standard or motion.loop.spin.
14
+ *
15
+ * Theming: a cast-theme.json can carry a `motion` block (exported by the
16
+ * cast-sync plugin from the kit's `motion` variable collection). ThemeProvider
17
+ * accepts those primitive-level overrides via its `motion` prop and
18
+ * `resolveMotion` rebuilds every semantic role from them, so a brand can
19
+ * re-tune timing with no code changes.
20
+ */
21
+ type EasingFn = (value: number) => number;
22
+ export type EasingName = 'standard' | 'entrance' | 'exit' | 'emphasized' | 'linear';
23
+ /** Cubic-bezier control points as [x1, y1, x2, y2]. */
24
+ export type EasingBezierPoints = readonly [number, number, number, number];
25
+ export type MotionDurations = {
26
+ /** Micro feedback, e.g. a press. */
27
+ instant: number;
28
+ /** Small enters/exits and expands. */
29
+ fast: number;
30
+ /** The general state-to-state duration. */
31
+ base: number;
32
+ /** Large surfaces or long distances. */
33
+ slow: number;
34
+ };
35
+ export type MotionCycles = {
36
+ /** Skeleton fade half-cycle. */
37
+ pulse: number;
38
+ /** Spinner full rotation. */
39
+ spin: number;
40
+ /** Progress indeterminate sweep. */
41
+ sweep: number;
42
+ };
43
+ export type SpringConfig = {
44
+ damping: number;
45
+ stiffness: number;
46
+ mass: number;
47
+ };
48
+ export type MotionTransition = {
49
+ duration: number;
50
+ easing: EasingFn;
51
+ };
52
+ export type MotionTokens = {
53
+ duration: MotionDurations;
54
+ cycle: MotionCycles;
55
+ easing: Record<EasingName, EasingFn>;
56
+ easingBezier: Record<EasingName, EasingBezierPoints>;
57
+ spring: {
58
+ overlay: SpringConfig;
59
+ };
60
+ transition: {
61
+ standard: MotionTransition;
62
+ enter: MotionTransition;
63
+ exit: MotionTransition;
64
+ expand: MotionTransition;
65
+ };
66
+ feedback: {
67
+ press: MotionTransition & {
68
+ scale: number;
69
+ };
70
+ shake: MotionTransition & {
71
+ amplitude: number;
72
+ };
73
+ pop: MotionTransition;
74
+ };
75
+ loop: {
76
+ spin: MotionTransition;
77
+ pulse: MotionTransition & {
78
+ from: number;
79
+ to: number;
80
+ };
81
+ indeterminate: MotionTransition;
82
+ };
83
+ };
84
+ /**
85
+ * Primitive-level motion overrides, the shape a cast-theme.json `motion`
86
+ * block maps onto. Only numbers and beziers — semantic roles are rebuilt
87
+ * from these by `resolveMotion`, never overridden directly.
88
+ */
89
+ export type MotionOverrides = {
90
+ duration?: Partial<MotionDurations>;
91
+ cycle?: Partial<MotionCycles>;
92
+ easingBezier?: Partial<Record<EasingName, EasingBezierPoints>>;
93
+ spring?: {
94
+ overlay?: Partial<SpringConfig>;
95
+ };
96
+ feedback?: {
97
+ press?: {
98
+ scale?: number;
99
+ };
100
+ shake?: {
101
+ amplitude?: number;
102
+ };
103
+ };
104
+ loop?: {
105
+ pulse?: {
106
+ from?: number;
107
+ to?: number;
108
+ };
109
+ };
110
+ };
111
+ /** Primitive: transition durations in ms (state to state). */
112
+ export declare const duration: MotionDurations;
113
+ /** Primitive: loop cycle lengths in ms (continuous, not state to state). */
114
+ export declare const cycle: MotionCycles;
115
+ /**
116
+ * Primitive: easing control points as cubic-bezier [x1, y1, x2, y2].
117
+ * This is the design-side value. The `motion` collection in the Figma kit
118
+ * carries these exact numbers (easing/{name}/{x1,y1,x2,y2}).
119
+ */
120
+ export declare const easingBezier: Record<EasingName, EasingBezierPoints>;
121
+ /** Primitive: easing as runtime functions (the code-side value). */
122
+ export declare const easing: Record<EasingName, EasingFn>;
123
+ /** Primitive: spring configs for Animated.spring. */
124
+ export declare const spring: {
125
+ overlay: SpringConfig;
126
+ };
127
+ export declare const transition: {
128
+ /** General state-to-state fade or slide. Backdrops, overlay fades. */
129
+ standard: {
130
+ duration: number;
131
+ easing: EasingFn;
132
+ };
133
+ /** Something appearing. Decelerates as it lands. */
134
+ enter: {
135
+ duration: number;
136
+ easing: EasingFn;
137
+ };
138
+ /** Something leaving. Accelerates as it goes. */
139
+ exit: {
140
+ duration: number;
141
+ easing: EasingFn;
142
+ };
143
+ /** Expand or collapse. Accordion chevron, height reveals. */
144
+ expand: {
145
+ duration: number;
146
+ easing: EasingFn;
147
+ };
148
+ };
149
+ export declare const feedback: {
150
+ /** Press-down scale on a pressable (reserved for future use). */
151
+ press: {
152
+ duration: number;
153
+ easing: EasingFn;
154
+ scale: number;
155
+ };
156
+ /** Error shake, e.g. input rejection. amplitude in px (future use). */
157
+ shake: {
158
+ duration: number;
159
+ easing: EasingFn;
160
+ amplitude: number;
161
+ };
162
+ /** Pop-in for a check, badge, or toast (future use). */
163
+ pop: {
164
+ duration: number;
165
+ easing: EasingFn;
166
+ };
167
+ };
168
+ export declare const loop: {
169
+ /** Spinner rotation. */
170
+ spin: {
171
+ duration: number;
172
+ easing: EasingFn;
173
+ };
174
+ /** Skeleton pulse. Fades opacity between from and to. */
175
+ pulse: {
176
+ duration: number;
177
+ easing: EasingFn;
178
+ from: number;
179
+ to: number;
180
+ };
181
+ /** Progress indeterminate sweep. */
182
+ indeterminate: {
183
+ duration: number;
184
+ easing: EasingFn;
185
+ };
186
+ };
187
+ /** The full default motion token set, exposed on the theme as `theme.motion`. */
188
+ export declare const motionTokens: MotionTokens;
189
+ /**
190
+ * Resolve a full motion token set from primitive-level overrides.
191
+ * With no overrides this returns the shared default set (no allocation).
192
+ * Semantic roles are always rebuilt from the merged primitives, so a
193
+ * duration override flows into every role that uses it.
194
+ */
195
+ export declare function resolveMotion(overrides?: MotionOverrides): MotionTokens;
196
+ export {};