@idealyst/theme 1.2.29 → 1.2.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/theme",
3
- "version": "1.2.29",
3
+ "version": "1.2.30",
4
4
  "description": "Theming system for Idealyst Framework",
5
5
  "readme": "README.md",
6
6
  "main": "src/index.ts",
@@ -50,6 +50,12 @@
50
50
  "import": "./src/darkTheme.ts",
51
51
  "require": "./src/darkTheme.ts",
52
52
  "types": "./src/darkTheme.ts"
53
+ },
54
+ "./animation": {
55
+ "native": "./src/animation/index.native.ts",
56
+ "import": "./src/animation/index.ts",
57
+ "require": "./src/animation/index.ts",
58
+ "types": "./src/animation/index.ts"
53
59
  }
54
60
  },
55
61
  "scripts": {
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Animation module for @idealyst/theme (Native)
3
+ *
4
+ * This module provides animation tokens and utilities for creating
5
+ * consistent animations across web and native platforms.
6
+ *
7
+ * Native-specific exports include Reanimated-compatible utilities.
8
+ *
9
+ * @example
10
+ * // Import tokens
11
+ * import { durations, easings, presets } from '@idealyst/theme/animation';
12
+ *
13
+ * // Import native utilities
14
+ * import { timingConfig, springConfig, animationConfig } from '@idealyst/theme/animation';
15
+ *
16
+ * // Use with Reanimated
17
+ * import { withTiming, withSpring, Easing } from 'react-native-reanimated';
18
+ *
19
+ * const config = timingConfig('normal', 'easeOut');
20
+ * value.value = withTiming(target, {
21
+ * duration: config.duration,
22
+ * easing: Easing.bezier(...config.easing),
23
+ * });
24
+ */
25
+
26
+ // Export tokens
27
+ export { durations, easings, presets } from './tokens';
28
+ export type { DurationKey, EasingKey, PresetKey } from './tokens';
29
+
30
+ // Export types
31
+ export type {
32
+ Duration,
33
+ Easing,
34
+ BezierEasing,
35
+ SpringConfig,
36
+ CSSEasing,
37
+ AnimationConfig,
38
+ TransitionConfig,
39
+ Keyframes,
40
+ KeyframeAnimationConfig,
41
+ AnimationIterations,
42
+ AnimationDirection,
43
+ AnimationFillMode,
44
+ SpringType,
45
+ TimingAnimationConfig,
46
+ PlatformAnimationOptions,
47
+ GradientAnimation,
48
+ GradientBorderConfig,
49
+ } from './types';
50
+
51
+ // Export native utilities
52
+ export {
53
+ resolveDuration,
54
+ resolveEasingBezier,
55
+ isSpringEasing,
56
+ timingConfig,
57
+ springConfig,
58
+ animationConfig,
59
+ presetConfig,
60
+ delayMs,
61
+ sequenceSteps,
62
+ } from './transitions.native';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Animation module for @idealyst/theme
3
+ *
4
+ * This module provides animation tokens and utilities for creating
5
+ * consistent animations across web and native platforms.
6
+ *
7
+ * @example
8
+ * // Import tokens
9
+ * import { durations, easings, presets } from '@idealyst/theme/animation';
10
+ *
11
+ * // Import web utilities
12
+ * import { cssTransition, cssPreset, cssKeyframes } from '@idealyst/theme/animation';
13
+ *
14
+ * // Import native utilities (in .native.ts files)
15
+ * import { timingConfig, springConfig } from '@idealyst/theme/animation';
16
+ */
17
+
18
+ // Export tokens
19
+ export { durations, easings, presets } from './tokens';
20
+ export type { DurationKey, EasingKey, PresetKey } from './tokens';
21
+
22
+ // Export types
23
+ export type {
24
+ Duration,
25
+ Easing,
26
+ BezierEasing,
27
+ SpringConfig,
28
+ CSSEasing,
29
+ AnimationConfig,
30
+ TransitionConfig,
31
+ Keyframes,
32
+ KeyframeAnimationConfig,
33
+ AnimationIterations,
34
+ AnimationDirection,
35
+ AnimationFillMode,
36
+ SpringType,
37
+ TimingAnimationConfig,
38
+ PlatformAnimationOptions,
39
+ GradientAnimation,
40
+ GradientBorderConfig,
41
+ } from './types';
42
+
43
+ // Export web utilities
44
+ export {
45
+ resolveDuration,
46
+ resolveEasing,
47
+ cssTransition,
48
+ cssPreset,
49
+ cssKeyframes,
50
+ cssAnimation,
51
+ gradientPropertyCSS,
52
+ injectGradientCSS,
53
+ conicSpinnerStyle,
54
+ pulseGradientStyle,
55
+ } from './transitions';
56
+
57
+ // Export native utilities (these are also exported from index.native.ts)
58
+ // Including here allows type-checking in monorepo without platform resolution
59
+ export {
60
+ resolveEasingBezier,
61
+ isSpringEasing,
62
+ timingConfig,
63
+ springConfig,
64
+ animationConfig,
65
+ presetConfig,
66
+ delayMs,
67
+ sequenceSteps,
68
+ } from './transitions.native';
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Animation tokens for @idealyst/theme
3
+ *
4
+ * These tokens provide consistent animation timing across the framework.
5
+ * Use these instead of hardcoded values for consistency and maintainability.
6
+ */
7
+
8
+ /**
9
+ * Duration tokens in milliseconds
10
+ *
11
+ * @example
12
+ * import { durations } from '@idealyst/theme/animation';
13
+ *
14
+ * // Use in CSS transition
15
+ * transition: `opacity ${durations.normal}ms ease`
16
+ *
17
+ * // Use with Reanimated
18
+ * withTiming(value, { duration: durations.fast })
19
+ */
20
+ export const durations = {
21
+ /** 0ms - No animation, instant change */
22
+ instant: 0,
23
+ /** 100ms - Micro-interactions: hover, focus, press feedback */
24
+ fast: 100,
25
+ /** 200ms - Standard state transitions: toggle, selection changes */
26
+ normal: 200,
27
+ /** 300ms - Complex transitions: expand/collapse, progress bars */
28
+ slow: 300,
29
+ /** 500ms - Entrance animations: modals, toasts appearing */
30
+ slower: 500,
31
+ /** 750ms - Extended animations: skeleton pulse, loading states */
32
+ slowest: 750,
33
+ /** 1000ms - Loop animations: spinners, continuous indicators */
34
+ loop: 1000,
35
+ } as const;
36
+
37
+ /**
38
+ * Easing tokens with both CSS string and bezier values
39
+ *
40
+ * CSS easings include both a CSS string for web transitions
41
+ * and bezier curve values for Reanimated's Easing.bezier()
42
+ *
43
+ * Spring easings are native-only and include damping/stiffness/mass
44
+ *
45
+ * @example
46
+ * import { easings } from '@idealyst/theme/animation';
47
+ *
48
+ * // Web: use CSS string
49
+ * transition: `opacity 200ms ${easings.easeOut.css}`
50
+ *
51
+ * // Native: use bezier values
52
+ * Easing.bezier(...easings.easeOut.bezier)
53
+ *
54
+ * // Native spring
55
+ * withSpring(value, easings.spring)
56
+ */
57
+ export const easings = {
58
+ // Standard CSS easings
59
+ /** Linear - constant speed, no acceleration */
60
+ linear: { css: 'linear', bezier: [0, 0, 1, 1] as const },
61
+ /** Ease - slight acceleration at start, deceleration at end */
62
+ ease: { css: 'ease', bezier: [0.25, 0.1, 0.25, 1] as const },
63
+ /** Ease-in - starts slow, accelerates */
64
+ easeIn: { css: 'ease-in', bezier: [0.42, 0, 1, 1] as const },
65
+ /** Ease-out - starts fast, decelerates (most common for UI) */
66
+ easeOut: { css: 'ease-out', bezier: [0, 0, 0.58, 1] as const },
67
+ /** Ease-in-out - symmetric acceleration and deceleration */
68
+ easeInOut: { css: 'ease-in-out', bezier: [0.42, 0, 0.58, 1] as const },
69
+
70
+ // Material Design easings
71
+ /** Standard - Material Design standard easing, balanced feel */
72
+ standard: { css: 'cubic-bezier(0.4, 0, 0.2, 1)', bezier: [0.4, 0, 0.2, 1] as const },
73
+ /** Decelerate - Material Design deceleration, for entering elements */
74
+ decelerate: { css: 'cubic-bezier(0, 0, 0.2, 1)', bezier: [0, 0, 0.2, 1] as const },
75
+ /** Accelerate - Material Design acceleration, for exiting elements */
76
+ accelerate: { css: 'cubic-bezier(0.4, 0, 1, 1)', bezier: [0.4, 0, 1, 1] as const },
77
+
78
+ // Spring configurations (native only - used with withSpring)
79
+ /** Spring - balanced spring, good for toggles and state changes */
80
+ spring: { damping: 15, stiffness: 200, mass: 1 } as const,
81
+ /** Spring stiff - less bounce, snappier feel, good for switches */
82
+ springStiff: { damping: 40, stiffness: 200, mass: 1 } as const,
83
+ /** Spring bouncy - more bounce, playful feel, good for buttons */
84
+ springBouncy: { damping: 10, stiffness: 180, mass: 1 } as const,
85
+ } as const;
86
+
87
+ /**
88
+ * Animation presets for common use cases
89
+ *
90
+ * These combine duration and easing for specific interaction patterns.
91
+ *
92
+ * @example
93
+ * import { presets } from '@idealyst/theme/animation';
94
+ *
95
+ * // Use for hover effects
96
+ * _web: { transition: cssPreset(presets.hover) }
97
+ */
98
+ export const presets = {
99
+ // Micro-interactions
100
+ /** Hover effect - fast, subtle */
101
+ hover: { duration: 'fast' as const, easing: 'ease' as const },
102
+ /** Press/active feedback - fast, ease-out */
103
+ press: { duration: 'fast' as const, easing: 'easeOut' as const },
104
+ /** Focus ring - normal speed */
105
+ focus: { duration: 'normal' as const, easing: 'ease' as const },
106
+
107
+ // State changes
108
+ /** Toggle/switch - normal with spring on native */
109
+ toggle: { duration: 'normal' as const, easing: 'spring' as const },
110
+ /** Selection change - normal, standard easing */
111
+ select: { duration: 'normal' as const, easing: 'standard' as const },
112
+
113
+ // Expand/collapse
114
+ /** Expand content - slow, standard easing */
115
+ expand: { duration: 'slow' as const, easing: 'standard' as const },
116
+ /** Collapse content - slightly faster than expand */
117
+ collapse: { duration: 'normal' as const, easing: 'accelerate' as const },
118
+
119
+ // Entrance/exit
120
+ /** Fade in - normal, decelerate */
121
+ fadeIn: { duration: 'normal' as const, easing: 'decelerate' as const },
122
+ /** Fade out - fast, accelerate */
123
+ fadeOut: { duration: 'fast' as const, easing: 'accelerate' as const },
124
+ /** Scale in (modal, dialog) - slower, decelerate */
125
+ scaleIn: { duration: 'slower' as const, easing: 'decelerate' as const },
126
+ /** Scale out - normal, accelerate */
127
+ scaleOut: { duration: 'normal' as const, easing: 'accelerate' as const },
128
+ /** Slide in - slow, standard */
129
+ slideIn: { duration: 'slow' as const, easing: 'standard' as const },
130
+ /** Slide out - normal, accelerate */
131
+ slideOut: { duration: 'normal' as const, easing: 'accelerate' as const },
132
+
133
+ // Loading/continuous
134
+ /** Spinner rotation - 1s linear loop */
135
+ spin: { duration: 'loop' as const, easing: 'linear' as const, iterations: 'infinite' as const },
136
+ /** Pulse effect - slowest, ease-in-out loop */
137
+ pulse: { duration: 'slowest' as const, easing: 'easeInOut' as const, iterations: 'infinite' as const },
138
+ /** Skeleton wave - slower than pulse */
139
+ wave: { duration: 1500 as const, easing: 'easeInOut' as const, iterations: 'infinite' as const },
140
+ } as const;
141
+
142
+ // Type exports for convenience
143
+ export type DurationKey = keyof typeof durations;
144
+ export type EasingKey = keyof typeof easings;
145
+ export type PresetKey = keyof typeof presets;
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Native-specific animation utilities
3
+ *
4
+ * These utilities generate Reanimated-compatible configurations
5
+ * from animation tokens for use in React Native animations.
6
+ */
7
+
8
+ import { durations, easings } from './tokens';
9
+ import type { DurationKey, EasingKey, PresetKey } from './tokens';
10
+ import type { Duration, SpringType, BezierEasing } from './types';
11
+
12
+ /**
13
+ * Resolve a duration value to milliseconds
14
+ */
15
+ export function resolveDuration(duration: Duration): number {
16
+ if (typeof duration === 'number') {
17
+ return duration;
18
+ }
19
+ return durations[duration];
20
+ }
21
+
22
+ /**
23
+ * Resolve an easing key to bezier curve values
24
+ * Returns bezier array for use with Easing.bezier()
25
+ *
26
+ * Note: Spring easings return a fallback bezier curve.
27
+ * For actual spring behavior, use springConfig() instead.
28
+ */
29
+ export function resolveEasingBezier(easing: EasingKey): BezierEasing {
30
+ const easingConfig = easings[easing];
31
+ // Spring configs don't have bezier equivalent, fall back to ease-out
32
+ if ('damping' in easingConfig) {
33
+ return easings.easeOut.bezier;
34
+ }
35
+ return easingConfig.bezier;
36
+ }
37
+
38
+ /**
39
+ * Check if an easing is a spring configuration
40
+ */
41
+ export function isSpringEasing(easing: EasingKey): easing is SpringType {
42
+ return easing === 'spring' || easing === 'springStiff' || easing === 'springBouncy';
43
+ }
44
+
45
+ /**
46
+ * Get Reanimated withTiming configuration from tokens
47
+ *
48
+ * Use with Reanimated's withTiming() function.
49
+ * The returned config includes duration and a bezier easing function creator.
50
+ *
51
+ * @param duration - Duration token key or milliseconds
52
+ * @param easing - Easing token key (spring easings fall back to easeOut)
53
+ * @returns Configuration object with duration and easing bezier values
54
+ *
55
+ * @example
56
+ * import { timingConfig } from '@idealyst/theme/animation';
57
+ * import { withTiming, Easing } from 'react-native-reanimated';
58
+ *
59
+ * const config = timingConfig('normal', 'easeOut');
60
+ * // => { duration: 200, easing: [0, 0, 0.58, 1] }
61
+ *
62
+ * // Use with Reanimated
63
+ * animatedValue.value = withTiming(targetValue, {
64
+ * duration: config.duration,
65
+ * easing: Easing.bezier(...config.easing),
66
+ * });
67
+ */
68
+ export function timingConfig(
69
+ duration: Duration = 'normal',
70
+ easing: EasingKey = 'easeOut'
71
+ ): { duration: number; easing: BezierEasing } {
72
+ return {
73
+ duration: resolveDuration(duration),
74
+ easing: resolveEasingBezier(easing),
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Get Reanimated withSpring configuration from tokens
80
+ *
81
+ * Use with Reanimated's withSpring() function.
82
+ *
83
+ * @param type - Spring type: 'spring', 'springStiff', or 'springBouncy'
84
+ * @returns Spring configuration object
85
+ *
86
+ * @example
87
+ * import { springConfig } from '@idealyst/theme/animation';
88
+ * import { withSpring } from 'react-native-reanimated';
89
+ *
90
+ * const config = springConfig('springStiff');
91
+ * // => { damping: 40, stiffness: 200, mass: 1 }
92
+ *
93
+ * // Use with Reanimated
94
+ * animatedValue.value = withSpring(targetValue, config);
95
+ */
96
+ export function springConfig(
97
+ type: SpringType = 'spring'
98
+ ): { damping: number; stiffness: number; mass: number } {
99
+ const config = easings[type];
100
+ return {
101
+ damping: config.damping,
102
+ stiffness: config.stiffness,
103
+ mass: config.mass,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Get animation configuration based on easing type
109
+ *
110
+ * Automatically returns spring config for spring easings,
111
+ * or timing config for bezier easings.
112
+ *
113
+ * @param duration - Duration token key or milliseconds
114
+ * @param easing - Easing token key
115
+ * @returns Either spring config or timing config
116
+ *
117
+ * @example
118
+ * import { animationConfig } from '@idealyst/theme/animation';
119
+ *
120
+ * // Returns spring config
121
+ * animationConfig('normal', 'spring')
122
+ * // => { type: 'spring', config: { damping: 15, stiffness: 200, mass: 1 } }
123
+ *
124
+ * // Returns timing config
125
+ * animationConfig('normal', 'easeOut')
126
+ * // => { type: 'timing', config: { duration: 200, easing: [0, 0, 0.58, 1] } }
127
+ */
128
+ export function animationConfig(
129
+ duration: Duration = 'normal',
130
+ easing: EasingKey = 'easeOut'
131
+ ):
132
+ | { type: 'spring'; config: ReturnType<typeof springConfig> }
133
+ | { type: 'timing'; config: ReturnType<typeof timingConfig> } {
134
+ if (isSpringEasing(easing)) {
135
+ return {
136
+ type: 'spring',
137
+ config: springConfig(easing),
138
+ };
139
+ }
140
+ return {
141
+ type: 'timing',
142
+ config: timingConfig(duration, easing),
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Get preset animation configuration for native
148
+ *
149
+ * @param presetName - Preset key
150
+ * @returns Animation configuration for the preset
151
+ */
152
+ export function presetConfig(presetName: keyof typeof import('./tokens').presets) {
153
+ const { presets } = require('./tokens');
154
+ const preset = presets[presetName];
155
+ return animationConfig(preset.duration, preset.easing);
156
+ }
157
+
158
+ /**
159
+ * Helper to create a delay configuration
160
+ *
161
+ * Use with Reanimated's withDelay() function.
162
+ *
163
+ * @param delay - Delay in milliseconds or duration token
164
+ * @returns Delay in milliseconds
165
+ *
166
+ * @example
167
+ * import { delayMs } from '@idealyst/theme/animation';
168
+ * import { withDelay, withTiming } from 'react-native-reanimated';
169
+ *
170
+ * // Using duration token
171
+ * animatedValue.value = withDelay(
172
+ * delayMs('normal'),
173
+ * withTiming(targetValue, timingConfig('fast', 'easeOut'))
174
+ * );
175
+ */
176
+ export function delayMs(delay: Duration): number {
177
+ return resolveDuration(delay);
178
+ }
179
+
180
+ /**
181
+ * Create a sequence of timing animations
182
+ *
183
+ * Helper for creating withSequence() compatible values.
184
+ *
185
+ * @param steps - Array of { value, duration?, easing? } objects
186
+ * @returns Array of animation configurations
187
+ *
188
+ * @example
189
+ * import { sequenceSteps } from '@idealyst/theme/animation';
190
+ *
191
+ * const steps = sequenceSteps([
192
+ * { value: 0 },
193
+ * { value: 1, duration: 'normal', easing: 'easeOut' },
194
+ * { value: 0.5, duration: 'fast' },
195
+ * ]);
196
+ *
197
+ * // Use with Reanimated
198
+ * animatedValue.value = withSequence(
199
+ * ...steps.map(s => withTiming(s.value, {
200
+ * duration: s.config.duration,
201
+ * easing: Easing.bezier(...s.config.easing)
202
+ * }))
203
+ * );
204
+ */
205
+ export function sequenceSteps(
206
+ steps: Array<{
207
+ value: number;
208
+ duration?: Duration;
209
+ easing?: EasingKey;
210
+ }>
211
+ ): Array<{
212
+ value: number;
213
+ config: ReturnType<typeof timingConfig>;
214
+ }> {
215
+ return steps.map((step) => ({
216
+ value: step.value,
217
+ config: timingConfig(step.duration ?? 'normal', step.easing ?? 'easeOut'),
218
+ }));
219
+ }
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Web-specific animation utilities
3
+ *
4
+ * These utilities generate CSS transition and animation strings
5
+ * from animation tokens for use in web stylesheets.
6
+ */
7
+
8
+ import { durations, easings, presets } from './tokens';
9
+ import type { DurationKey, EasingKey, PresetKey } from './tokens';
10
+ import type {
11
+ Duration,
12
+ Keyframes,
13
+ KeyframeAnimationConfig,
14
+ AnimationIterations,
15
+ AnimationDirection,
16
+ AnimationFillMode,
17
+ } from './types';
18
+
19
+ /**
20
+ * Resolve a duration value to milliseconds
21
+ */
22
+ export function resolveDuration(duration: Duration): number {
23
+ if (typeof duration === 'number') {
24
+ return duration;
25
+ }
26
+ return durations[duration];
27
+ }
28
+
29
+ /**
30
+ * Resolve an easing key to CSS string
31
+ */
32
+ export function resolveEasing(easing: EasingKey): string {
33
+ const easingConfig = easings[easing];
34
+ // Spring configs don't have CSS equivalent, fall back to ease-out
35
+ if ('damping' in easingConfig) {
36
+ return 'ease-out';
37
+ }
38
+ return easingConfig.css;
39
+ }
40
+
41
+ /**
42
+ * Generate a CSS transition string from tokens
43
+ *
44
+ * @param properties - CSS property or array of properties to transition
45
+ * @param duration - Duration token key or milliseconds
46
+ * @param easing - Easing token key
47
+ * @returns CSS transition string
48
+ *
49
+ * @example
50
+ * import { cssTransition } from '@idealyst/theme/animation';
51
+ *
52
+ * // Single property
53
+ * cssTransition('opacity', 'normal', 'easeOut')
54
+ * // => "opacity 200ms ease-out"
55
+ *
56
+ * // Multiple properties
57
+ * cssTransition(['opacity', 'transform'], 'fast', 'standard')
58
+ * // => "opacity 100ms cubic-bezier(0.4, 0, 0.2, 1), transform 100ms cubic-bezier(0.4, 0, 0.2, 1)"
59
+ *
60
+ * // With numeric duration
61
+ * cssTransition('background-color', 150, 'ease')
62
+ * // => "background-color 150ms ease"
63
+ */
64
+ export function cssTransition(
65
+ properties: string | string[],
66
+ duration: Duration = 'normal',
67
+ easing: EasingKey = 'ease'
68
+ ): string {
69
+ const props = Array.isArray(properties) ? properties : [properties];
70
+ const durationMs = resolveDuration(duration);
71
+ const easingValue = resolveEasing(easing);
72
+
73
+ return props.map((prop) => `${prop} ${durationMs}ms ${easingValue}`).join(', ');
74
+ }
75
+
76
+ /**
77
+ * Generate a CSS transition string from a preset
78
+ *
79
+ * @param presetName - Preset key from presets
80
+ * @param properties - Optional CSS properties (defaults to 'all')
81
+ * @returns CSS transition string
82
+ *
83
+ * @example
84
+ * import { cssPreset } from '@idealyst/theme/animation';
85
+ *
86
+ * cssPreset('hover')
87
+ * // => "all 100ms ease"
88
+ *
89
+ * cssPreset('fadeIn', 'opacity')
90
+ * // => "opacity 200ms cubic-bezier(0, 0, 0.2, 1)"
91
+ */
92
+ export function cssPreset(presetName: PresetKey, properties: string | string[] = 'all'): string {
93
+ const preset = presets[presetName];
94
+ return cssTransition(properties, preset.duration, preset.easing);
95
+ }
96
+
97
+ /**
98
+ * Generate CSS @keyframes string
99
+ *
100
+ * @param name - Unique name for the keyframes
101
+ * @param frames - Keyframe definitions
102
+ * @returns CSS @keyframes string
103
+ *
104
+ * @example
105
+ * import { cssKeyframes } from '@idealyst/theme/animation';
106
+ *
107
+ * const fadeIn = cssKeyframes('fadeIn', {
108
+ * '0%': { opacity: 0 },
109
+ * '100%': { opacity: 1 },
110
+ * });
111
+ * // => "@keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }"
112
+ */
113
+ export function cssKeyframes(name: string, frames: Keyframes): string {
114
+ const frameStrings = Object.entries(frames)
115
+ .map(([key, styles]) => {
116
+ const styleString = Object.entries(styles || {})
117
+ .map(([prop, value]) => {
118
+ // Convert camelCase to kebab-case
119
+ const kebabProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
120
+ return `${kebabProp}: ${value}`;
121
+ })
122
+ .join('; ');
123
+ return `${key} { ${styleString}; }`;
124
+ })
125
+ .join(' ');
126
+
127
+ return `@keyframes ${name} { ${frameStrings} }`;
128
+ }
129
+
130
+ /**
131
+ * Generate CSS animation shorthand string
132
+ *
133
+ * @param name - Animation name (must match @keyframes name)
134
+ * @param config - Animation configuration
135
+ * @returns CSS animation shorthand string
136
+ *
137
+ * @example
138
+ * import { cssAnimation } from '@idealyst/theme/animation';
139
+ *
140
+ * cssAnimation('fadeIn', { duration: 'normal', easing: 'easeOut' })
141
+ * // => "fadeIn 200ms ease-out"
142
+ *
143
+ * cssAnimation('spin', { duration: 'loop', easing: 'linear', iterations: 'infinite' })
144
+ * // => "spin 1000ms linear infinite"
145
+ */
146
+ export function cssAnimation(name: string, config: KeyframeAnimationConfig = {}): string {
147
+ const {
148
+ duration = 'normal',
149
+ easing = 'ease',
150
+ delay = 0,
151
+ iterations = 1,
152
+ direction = 'normal',
153
+ fillMode = 'none',
154
+ } = config;
155
+
156
+ const durationMs = resolveDuration(duration);
157
+ const easingValue = resolveEasing(easing);
158
+
159
+ const parts = [
160
+ name,
161
+ `${durationMs}ms`,
162
+ easingValue,
163
+ delay > 0 ? `${delay}ms` : null,
164
+ iterations !== 1 ? iterations : null,
165
+ direction !== 'normal' ? direction : null,
166
+ fillMode !== 'none' ? fillMode : null,
167
+ ].filter(Boolean);
168
+
169
+ return parts.join(' ');
170
+ }
171
+
172
+ /**
173
+ * CSS @property registration for animatable custom properties
174
+ *
175
+ * This CSS should be injected once into the document head to enable
176
+ * smooth gradient animations using CSS custom properties.
177
+ *
178
+ * @example
179
+ * // Inject in your app's entry point
180
+ * import { gradientPropertyCSS, injectGradientCSS } from '@idealyst/theme/animation';
181
+ *
182
+ * // Option 1: Manual injection
183
+ * const style = document.createElement('style');
184
+ * style.textContent = gradientPropertyCSS;
185
+ * document.head.appendChild(style);
186
+ *
187
+ * // Option 2: Use helper
188
+ * injectGradientCSS();
189
+ */
190
+ export const gradientPropertyCSS = `
191
+ @property --gradient-angle {
192
+ syntax: '<angle>';
193
+ initial-value: 0deg;
194
+ inherits: false;
195
+ }
196
+
197
+ @property --gradient-position {
198
+ syntax: '<percentage>';
199
+ initial-value: 0%;
200
+ inherits: false;
201
+ }
202
+
203
+ @keyframes spin-gradient {
204
+ from { --gradient-angle: 0deg; }
205
+ to { --gradient-angle: 360deg; }
206
+ }
207
+
208
+ @keyframes pulse-gradient {
209
+ 0%, 100% { opacity: 1; }
210
+ 50% { opacity: 0.5; }
211
+ }
212
+
213
+ @keyframes wave-gradient {
214
+ from { --gradient-position: -100%; }
215
+ to { --gradient-position: 200%; }
216
+ }
217
+ `;
218
+
219
+ let gradientCSSInjected = false;
220
+
221
+ /**
222
+ * Inject gradient CSS into document head (idempotent)
223
+ *
224
+ * Call this once in your app to enable gradient border animations.
225
+ * Safe to call multiple times - will only inject once.
226
+ */
227
+ export function injectGradientCSS(): void {
228
+ if (typeof document === 'undefined' || gradientCSSInjected) {
229
+ return;
230
+ }
231
+
232
+ const style = document.createElement('style');
233
+ style.id = 'idealyst-gradient-animations';
234
+ style.textContent = gradientPropertyCSS;
235
+ document.head.appendChild(style);
236
+ gradientCSSInjected = true;
237
+ }
238
+
239
+ /**
240
+ * Generate conic gradient border spinner styles
241
+ *
242
+ * @param colors - Array of gradient colors
243
+ * @param borderWidth - Border thickness in pixels
244
+ * @param duration - Animation duration
245
+ * @returns Style object for animated gradient border
246
+ *
247
+ * @example
248
+ * import { conicSpinnerStyle } from '@idealyst/theme/animation';
249
+ *
250
+ * const spinnerStyle = conicSpinnerStyle(
251
+ * ['#3b82f6', '#8b5cf6', '#ec4899'],
252
+ * 2,
253
+ * 2000
254
+ * );
255
+ */
256
+ export function conicSpinnerStyle(
257
+ colors: string[],
258
+ borderWidth: number = 2,
259
+ duration: Duration = 2000
260
+ ): Record<string, string | number> {
261
+ const durationMs = resolveDuration(duration);
262
+ const colorString = colors.join(', ');
263
+
264
+ return {
265
+ background: `conic-gradient(from var(--gradient-angle), ${colorString})`,
266
+ animation: `spin-gradient ${durationMs}ms linear infinite`,
267
+ // Mask technique for border-only effect
268
+ WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
269
+ WebkitMaskComposite: 'xor',
270
+ maskComposite: 'exclude',
271
+ padding: borderWidth,
272
+ };
273
+ }
274
+
275
+ /**
276
+ * Generate pulse gradient styles
277
+ *
278
+ * @param colors - Array of gradient colors
279
+ * @param duration - Animation duration
280
+ * @returns Style object for pulsing gradient
281
+ */
282
+ export function pulseGradientStyle(
283
+ colors: string[],
284
+ duration: Duration = 'slowest'
285
+ ): Record<string, string | number> {
286
+ const durationMs = resolveDuration(duration);
287
+ const colorString = colors.join(', ');
288
+
289
+ return {
290
+ background: `linear-gradient(135deg, ${colorString})`,
291
+ animation: `pulse-gradient ${durationMs}ms ease-in-out infinite`,
292
+ };
293
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Animation type definitions for @idealyst/theme
3
+ */
4
+
5
+ import type { durations, easings } from './tokens';
6
+
7
+ // Duration types
8
+ export type DurationKey = keyof typeof durations;
9
+ export type Duration = DurationKey | number;
10
+
11
+ // Easing types
12
+ export type EasingKey = keyof typeof easings;
13
+ export type BezierEasing = readonly [number, number, number, number];
14
+ export type SpringConfig = {
15
+ readonly damping: number;
16
+ readonly stiffness: number;
17
+ readonly mass: number;
18
+ };
19
+
20
+ export type CSSEasing = {
21
+ readonly css: string;
22
+ readonly bezier: BezierEasing;
23
+ };
24
+
25
+ export type Easing = CSSEasing | SpringConfig;
26
+
27
+ // Animation configuration
28
+ export interface AnimationConfig {
29
+ duration?: Duration;
30
+ easing?: EasingKey;
31
+ delay?: number;
32
+ }
33
+
34
+ // Transition configuration for CSS
35
+ export interface TransitionConfig extends AnimationConfig {
36
+ properties?: string | string[];
37
+ }
38
+
39
+ // Keyframe types
40
+ export type KeyframeStyles = Record<string, React.CSSProperties>;
41
+ export type KeyframePercentage = `${number}%` | 'from' | 'to';
42
+ export type Keyframes = Partial<Record<KeyframePercentage, React.CSSProperties>>;
43
+
44
+ // Animation iteration
45
+ export type AnimationIterations = number | 'infinite';
46
+ export type AnimationDirection = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
47
+ export type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both';
48
+
49
+ // Full animation configuration
50
+ export interface KeyframeAnimationConfig extends AnimationConfig {
51
+ iterations?: AnimationIterations;
52
+ direction?: AnimationDirection;
53
+ fillMode?: AnimationFillMode;
54
+ }
55
+
56
+ // Spring types for native
57
+ export type SpringType = 'spring' | 'springStiff' | 'springBouncy';
58
+
59
+ // Timing config for Reanimated
60
+ export interface TimingAnimationConfig {
61
+ duration: number;
62
+ easing: BezierEasing;
63
+ }
64
+
65
+ // Platform-specific options
66
+ export interface PlatformAnimationOptions {
67
+ web?: Partial<TransitionConfig> & { transition?: string };
68
+ native?: Partial<AnimationConfig> & Partial<SpringConfig>;
69
+ }
70
+
71
+ // Gradient animation types
72
+ export type GradientAnimation = 'spin' | 'pulse' | 'wave';
73
+
74
+ export interface GradientBorderConfig {
75
+ colors: string[];
76
+ borderWidth?: number;
77
+ borderRadius?: number;
78
+ duration?: Duration;
79
+ animation?: GradientAnimation;
80
+ }
package/src/builder.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  ProgressSizeValue,
21
21
  AccordionSizeValue,
22
22
  ActivityIndicatorSizeValue,
23
+ AlertSizeValue,
23
24
  BreadcrumbSizeValue,
24
25
  ListSizeValue,
25
26
  MenuSizeValue,
@@ -68,6 +69,7 @@ export type BuiltTheme<
68
69
  progress: Record<TSize, ProgressSizeValue>;
69
70
  accordion: Record<TSize, AccordionSizeValue>;
70
71
  activityIndicator: Record<TSize, ActivityIndicatorSizeValue>;
72
+ alert: Record<TSize, AlertSizeValue>;
71
73
  breadcrumb: Record<TSize, BreadcrumbSizeValue>;
72
74
  list: Record<TSize, ListSizeValue>;
73
75
  menu: Record<TSize, MenuSizeValue>;
@@ -259,6 +261,7 @@ export class ThemeBuilder<
259
261
  progress: Record<S, ProgressSizeValue>;
260
262
  accordion: Record<S, AccordionSizeValue>;
261
263
  activityIndicator: Record<S, ActivityIndicatorSizeValue>;
264
+ alert: Record<S, AlertSizeValue>;
262
265
  breadcrumb: Record<S, BreadcrumbSizeValue>;
263
266
  list: Record<S, ListSizeValue>;
264
267
  menu: Record<S, MenuSizeValue>;
@@ -141,7 +141,7 @@ function createSizeVariants(theme: Theme) {
141
141
 
142
142
  #### Colors
143
143
  - **Intents**: `theme.intents[intent].primary`, `.contrast`, `.light`, `.dark`
144
- - Available intents: `primary`, `success`, `error`, `warning`, `neutral`, `info`
144
+ - Available intents: `primary`, `success`, `danger`, `warning`, `neutral`, `info`
145
145
  - **Surface**: `theme.colors.surface.primary`, `.secondary`, `.tertiary`, `.inverse`, etc.
146
146
  - **Text**: `theme.colors.text.primary`, `.secondary`, `.tertiary`, `.inverse`, etc.
147
147
  - **Border**: `theme.colors.border.primary`, `.secondary`, `.tertiary`, `.disabled`
@@ -636,7 +636,7 @@ function generateInputStyles(theme: Theme, extensions?: ComponentExtensions): Re
636
636
  },
637
637
  error: {
638
638
  true: {
639
- borderColor: themeRef('intents.error.primary'),
639
+ borderColor: themeRef('intents.danger.primary'),
640
640
  },
641
641
  false: {},
642
642
  },
@@ -666,7 +666,7 @@ function generateInputStyles(theme: Theme, extensions?: ComponentExtensions): Re
666
666
  fontSize: 12,
667
667
  variants: {
668
668
  error: {
669
- true: { color: themeRef('intents.error.primary') },
669
+ true: { color: themeRef('intents.danger.primary') },
670
670
  false: {},
671
671
  },
672
672
  },
package/src/darkTheme.ts CHANGED
@@ -20,7 +20,7 @@ export const darkTheme = createTheme()
20
20
  light: '#6ee7b7',
21
21
  dark: '#047857',
22
22
  })
23
- .addIntent('error', {
23
+ .addIntent('danger', {
24
24
  primary: '#f87171',
25
25
  contrast: '#0f172a',
26
26
  light: '#fca5a5',
package/src/index.ts CHANGED
@@ -23,4 +23,8 @@ export * from './componentStyles';
23
23
  // Responsive utilities
24
24
  export * from './responsive';
25
25
  export * from './breakpoints';
26
- export * from './useResponsiveStyle';
26
+ export * from './useResponsiveStyle';
27
+
28
+ // Animation tokens and utilities
29
+ // Note: Use '@idealyst/theme/animation' for full animation API
30
+ export { durations, easings, presets } from './animation/tokens';
package/src/lightTheme.ts CHANGED
@@ -19,7 +19,7 @@ export const lightTheme = createTheme()
19
19
  light: '#a7f3d0',
20
20
  dark: '#165e29',
21
21
  })
22
- .addIntent('error', {
22
+ .addIntent('danger', {
23
23
  primary: '#ef4444',
24
24
  contrast: '#ffffff',
25
25
  light: '#fca5a1',
@@ -211,6 +211,13 @@ export const lightTheme = createTheme()
211
211
  lg: { size: 48, borderWidth: 4 },
212
212
  xl: { size: 64, borderWidth: 5 },
213
213
  },
214
+ alert: {
215
+ xs: { padding: 8, gap: 6, borderRadius: 4, titleFontSize: 12, titleLineHeight: 16, messageFontSize: 11, messageLineHeight: 14, iconSize: 16, closeIconSize: 12 },
216
+ sm: { padding: 12, gap: 8, borderRadius: 6, titleFontSize: 14, titleLineHeight: 20, messageFontSize: 12, messageLineHeight: 16, iconSize: 20, closeIconSize: 14 },
217
+ md: { padding: 16, gap: 10, borderRadius: 8, titleFontSize: 16, titleLineHeight: 24, messageFontSize: 14, messageLineHeight: 20, iconSize: 24, closeIconSize: 16 },
218
+ lg: { padding: 20, gap: 12, borderRadius: 10, titleFontSize: 18, titleLineHeight: 28, messageFontSize: 16, messageLineHeight: 24, iconSize: 28, closeIconSize: 18 },
219
+ xl: { padding: 24, gap: 14, borderRadius: 12, titleFontSize: 20, titleLineHeight: 32, messageFontSize: 18, messageLineHeight: 28, iconSize: 32, closeIconSize: 20 },
220
+ },
214
221
  breadcrumb: {
215
222
  xs: { fontSize: 10, lineHeight: 14, iconSize: 12 },
216
223
  sm: { fontSize: 12, lineHeight: 16, iconSize: 14 },
@@ -54,6 +54,7 @@ export type ComponentName =
54
54
  | 'Table'
55
55
  | 'Text'
56
56
  | 'TextArea'
57
+ | 'TextInput'
57
58
  | 'TimePicker'
58
59
  | 'Tooltip'
59
60
  | 'Video'
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-object-type */
2
2
 
3
- import type { IntentValue, ShadowValue, Shade, ColorValue, InteractionConfig, ButtonSizeValue, ChipSizeValue, BadgeSizeValue, IconSizeValue, InputSizeValue, RadioButtonSizeValue, SelectSizeValue, SliderSizeValue, SwitchSizeValue, TextAreaSizeValue, AvatarSizeValue, ProgressSizeValue, AccordionSizeValue, ActivityIndicatorSizeValue, BreadcrumbSizeValue, ListSizeValue, MenuSizeValue, TextSizeValue, TabBarSizeValue, TableSizeValue, TooltipSizeValue, ViewSizeValue, Typography, TypographyValue } from './structures';
3
+ import type { IntentValue, ShadowValue, Shade, ColorValue, InteractionConfig, ButtonSizeValue, ChipSizeValue, BadgeSizeValue, IconSizeValue, InputSizeValue, RadioButtonSizeValue, SelectSizeValue, SliderSizeValue, SwitchSizeValue, TextAreaSizeValue, AvatarSizeValue, ProgressSizeValue, AccordionSizeValue, ActivityIndicatorSizeValue, AlertSizeValue, BreadcrumbSizeValue, ListSizeValue, MenuSizeValue, TextSizeValue, TabBarSizeValue, TableSizeValue, TooltipSizeValue, ViewSizeValue, Typography, TypographyValue } from './structures';
4
4
 
5
5
  /**
6
6
  * Default fallback theme structure.
@@ -32,6 +32,7 @@ export interface DefaultTheme {
32
32
  progress: Record<string, ProgressSizeValue>;
33
33
  accordion: Record<string, AccordionSizeValue>;
34
34
  activityIndicator: Record<string, ActivityIndicatorSizeValue>;
35
+ alert: Record<string, AlertSizeValue>;
35
36
  breadcrumb: Record<string, BreadcrumbSizeValue>;
36
37
  list: Record<string, ListSizeValue>;
37
38
  menu: Record<string, MenuSizeValue>;
@@ -194,6 +194,18 @@ export type ActivityIndicatorSizeValue = {
194
194
  borderWidth: SizeValue;
195
195
  };
196
196
 
197
+ export type AlertSizeValue = {
198
+ padding: SizeValue;
199
+ gap: SizeValue;
200
+ borderRadius: SizeValue;
201
+ titleFontSize: SizeValue;
202
+ titleLineHeight: SizeValue;
203
+ messageFontSize: SizeValue;
204
+ messageLineHeight: SizeValue;
205
+ iconSize: SizeValue;
206
+ closeIconSize: SizeValue;
207
+ };
208
+
197
209
  export type BreadcrumbSizeValue = {
198
210
  fontSize: SizeValue;
199
211
  lineHeight: SizeValue;