@idealyst/theme 1.2.102 → 1.2.104
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 +2 -2
- package/src/babel/plugin.js +98 -30
- package/src/colorScheme.ts +99 -4
- package/src/componentStyles.ts +12 -28
- package/src/defaults.ts +33 -0
- package/src/index.ts +3 -0
- package/src/theme/extensions.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/theme",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.104",
|
|
4
4
|
"description": "Theming system for Idealyst Framework",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"publish:npm": "npm publish"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@idealyst/tooling": "^1.2.
|
|
66
|
+
"@idealyst/tooling": "^1.2.104"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"react-native-unistyles": ">=3.0.0"
|
package/src/babel/plugin.js
CHANGED
|
@@ -66,10 +66,18 @@ function getOrCreateEntry(componentName) {
|
|
|
66
66
|
// AST Deep Merge - Merges style object ASTs at build time
|
|
67
67
|
// ============================================================================
|
|
68
68
|
|
|
69
|
+
// Platform-specific keys used by Unistyles
|
|
70
|
+
const PLATFORM_KEYS = new Set(['_web', '_ios', '_android']);
|
|
71
|
+
|
|
69
72
|
/**
|
|
70
73
|
* Deep merge two ObjectExpression ASTs.
|
|
71
74
|
* Source properties override target properties.
|
|
72
75
|
* Nested objects are recursively merged.
|
|
76
|
+
*
|
|
77
|
+
* Also propagates extension properties into platform-specific blocks (_web, _ios, _android)
|
|
78
|
+
* when those blocks already contain the same key. This ensures that e.g. setting
|
|
79
|
+
* `fontFamily: 'MyFont'` in an extension properly overrides `_web: { fontFamily: 'inherit' }`
|
|
80
|
+
* in the base styles.
|
|
73
81
|
*/
|
|
74
82
|
function mergeObjectExpressions(t, target, source) {
|
|
75
83
|
if (!t.isObjectExpression(target) || !t.isObjectExpression(source)) {
|
|
@@ -90,6 +98,9 @@ function mergeObjectExpressions(t, target, source) {
|
|
|
90
98
|
|
|
91
99
|
const resultProps = [...target.properties];
|
|
92
100
|
|
|
101
|
+
// Collect non-platform source property keys and values for propagation
|
|
102
|
+
const sourceNonPlatformProps = new Map();
|
|
103
|
+
|
|
93
104
|
for (const prop of source.properties) {
|
|
94
105
|
if (!t.isObjectProperty(prop)) continue;
|
|
95
106
|
|
|
@@ -97,6 +108,10 @@ function mergeObjectExpressions(t, target, source) {
|
|
|
97
108
|
t.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
98
109
|
if (!key) continue;
|
|
99
110
|
|
|
111
|
+
if (!PLATFORM_KEYS.has(key)) {
|
|
112
|
+
sourceNonPlatformProps.set(key, prop);
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
const existingProp = targetProps.get(key);
|
|
101
116
|
|
|
102
117
|
if (existingProp) {
|
|
@@ -111,11 +126,42 @@ function mergeObjectExpressions(t, target, source) {
|
|
|
111
126
|
const idx = resultProps.indexOf(existingProp);
|
|
112
127
|
resultProps[idx] = t.objectProperty(existingProp.key, mergedValue);
|
|
113
128
|
}
|
|
114
|
-
// If
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
// If base is a dynamic function and extension is anything (plain object
|
|
130
|
+
// or arrow function), extract the extension's object and merge it into the
|
|
131
|
+
// base function's return body. Extensions should only provide plain objects,
|
|
132
|
+
// but if a function is given we unwrap its body to get the object.
|
|
133
|
+
else if (t.isArrowFunctionExpression(existingValue)) {
|
|
134
|
+
let extObj = newValue;
|
|
135
|
+
// If extension mistakenly provides a function, unwrap its return body
|
|
136
|
+
if (t.isArrowFunctionExpression(extObj)) {
|
|
137
|
+
extObj = extObj.body;
|
|
138
|
+
if (t.isParenthesizedExpression(extObj)) {
|
|
139
|
+
extObj = extObj.expression;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (t.isObjectExpression(extObj)) {
|
|
144
|
+
let baseBody = existingValue.body;
|
|
145
|
+
if (t.isParenthesizedExpression(baseBody)) {
|
|
146
|
+
baseBody = baseBody.expression;
|
|
147
|
+
}
|
|
148
|
+
if (t.isObjectExpression(baseBody)) {
|
|
149
|
+
const mergedBody = mergeObjectExpressions(t, baseBody, extObj);
|
|
150
|
+
const idx = resultProps.indexOf(existingProp);
|
|
151
|
+
resultProps[idx] = t.objectProperty(
|
|
152
|
+
existingProp.key,
|
|
153
|
+
t.arrowFunctionExpression(existingValue.params, mergedBody)
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
// Block body or other complex form — fall back to replacement
|
|
157
|
+
const idx = resultProps.indexOf(existingProp);
|
|
158
|
+
resultProps[idx] = prop;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
// Extension body isn't an object — fall back to replacement
|
|
162
|
+
const idx = resultProps.indexOf(existingProp);
|
|
163
|
+
resultProps[idx] = prop;
|
|
164
|
+
}
|
|
119
165
|
}
|
|
120
166
|
// Otherwise, source replaces target
|
|
121
167
|
else {
|
|
@@ -128,34 +174,49 @@ function mergeObjectExpressions(t, target, source) {
|
|
|
128
174
|
}
|
|
129
175
|
}
|
|
130
176
|
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Get the bodies (assuming they return ObjectExpressions)
|
|
140
|
-
let baseBody = baseFn.body;
|
|
141
|
-
let extBody = extFn.body;
|
|
177
|
+
// Propagate extension properties into platform-specific blocks.
|
|
178
|
+
// If the base has _web: { fontFamily: 'inherit' } and the extension sets
|
|
179
|
+
// fontFamily: 'MyFont' at the top level, we need to also override fontFamily
|
|
180
|
+
// inside the _web block so the platform-specific value doesn't shadow the extension.
|
|
181
|
+
if (sourceNonPlatformProps.size > 0) {
|
|
182
|
+
for (let i = 0; i < resultProps.length; i++) {
|
|
183
|
+
const prop = resultProps[i];
|
|
184
|
+
if (!t.isObjectProperty(prop)) continue;
|
|
142
185
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
186
|
+
const key = t.isIdentifier(prop.key) ? prop.key.name :
|
|
187
|
+
t.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
188
|
+
if (!key || !PLATFORM_KEYS.has(key)) continue;
|
|
189
|
+
if (!t.isObjectExpression(prop.value)) continue;
|
|
190
|
+
|
|
191
|
+
// Check if any source properties conflict with keys in this platform block
|
|
192
|
+
const platformProps = prop.value.properties;
|
|
193
|
+
let modified = false;
|
|
194
|
+
const newPlatformProps = [...platformProps];
|
|
195
|
+
|
|
196
|
+
for (let j = 0; j < newPlatformProps.length; j++) {
|
|
197
|
+
const platProp = newPlatformProps[j];
|
|
198
|
+
if (!t.isObjectProperty(platProp)) continue;
|
|
199
|
+
|
|
200
|
+
const platKey = t.isIdentifier(platProp.key) ? platProp.key.name :
|
|
201
|
+
t.isStringLiteral(platProp.key) ? platProp.key.value : null;
|
|
202
|
+
if (!platKey) continue;
|
|
203
|
+
|
|
204
|
+
const extProp = sourceNonPlatformProps.get(platKey);
|
|
205
|
+
if (extProp) {
|
|
206
|
+
// Extension has a property that conflicts with this platform block key.
|
|
207
|
+
// Override the platform block value with the extension value.
|
|
208
|
+
newPlatformProps[j] = t.objectProperty(platProp.key, t.cloneDeep(extProp.value));
|
|
209
|
+
modified = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
150
212
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
213
|
+
if (modified) {
|
|
214
|
+
resultProps[i] = t.objectProperty(prop.key, t.objectExpression(newPlatformProps));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
155
217
|
}
|
|
156
218
|
|
|
157
|
-
|
|
158
|
-
return extFn;
|
|
219
|
+
return t.objectExpression(resultProps);
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
/**
|
|
@@ -873,7 +934,10 @@ module.exports = function idealystStylesPlugin({ types: t }) {
|
|
|
873
934
|
opts.processAll ||
|
|
874
935
|
(opts.autoProcessPaths?.some(p => filename.includes(p)));
|
|
875
936
|
|
|
876
|
-
|
|
937
|
+
// extendStyle/overrideStyle must ALWAYS be processed regardless of
|
|
938
|
+
// shouldProcess, since they are called from user code (not just from
|
|
939
|
+
// @idealyst/* packages). Only defineStyle and StyleSheet.create
|
|
940
|
+
// $iterator expansion are gated by autoProcessPaths.
|
|
877
941
|
|
|
878
942
|
// ============================================================
|
|
879
943
|
// Handle extendStyle - Store extension AST for later merging
|
|
@@ -965,6 +1029,10 @@ module.exports = function idealystStylesPlugin({ types: t }) {
|
|
|
965
1029
|
return;
|
|
966
1030
|
}
|
|
967
1031
|
|
|
1032
|
+
// defineStyle and StyleSheet.create are only processed for files
|
|
1033
|
+
// matching autoProcessPaths (i.e., framework packages)
|
|
1034
|
+
if (!shouldProcess) return;
|
|
1035
|
+
|
|
968
1036
|
// ============================================================
|
|
969
1037
|
// Handle defineStyle - Merge with extensions and output StyleSheet.create
|
|
970
1038
|
// ============================================================
|
package/src/colorScheme.ts
CHANGED
|
@@ -1,10 +1,71 @@
|
|
|
1
|
-
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
1
|
+
import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles';
|
|
2
|
+
import type { UnistylesThemes } from 'react-native-unistyles';
|
|
3
|
+
import type { BuiltTheme } from './builder';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Color scheme preference type.
|
|
5
7
|
*/
|
|
6
8
|
export type ColorScheme = 'light' | 'dark';
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Any theme produced by the builder system (fromTheme().build() or createTheme().build()).
|
|
12
|
+
*/
|
|
13
|
+
type AnyBuiltTheme = BuiltTheme<string, string, string, string, string, string, string, string, string>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for configureThemes().
|
|
17
|
+
*/
|
|
18
|
+
export interface ConfigureThemesOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Theme instances keyed by name. Must include at least `light` and `dark`.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* { light: customLightTheme, dark: customDarkTheme }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
themes: { light: AnyBuiltTheme; dark: AnyBuiltTheme } & Record<string, AnyBuiltTheme>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Which theme to activate on startup (default: 'light').
|
|
31
|
+
*/
|
|
32
|
+
initialTheme?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Configure the theme system. Call this **once** at app startup,
|
|
37
|
+
* before any component renders.
|
|
38
|
+
*
|
|
39
|
+
* This replaces the need to import `StyleSheet` from `react-native-unistyles` directly.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import { configureThemes, lightTheme, darkTheme, fromTheme } from '@idealyst/theme';
|
|
44
|
+
*
|
|
45
|
+
* const light = fromTheme(lightTheme).build();
|
|
46
|
+
* const dark = fromTheme(darkTheme).build();
|
|
47
|
+
*
|
|
48
|
+
* configureThemes({ themes: { light, dark } });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // With a custom initial theme
|
|
54
|
+
* configureThemes({
|
|
55
|
+
* themes: { light, dark, midnight },
|
|
56
|
+
* initialTheme: 'midnight',
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function configureThemes(options: ConfigureThemesOptions): void {
|
|
61
|
+
StyleSheet.configure({
|
|
62
|
+
themes: options.themes as Record<string, object> as UnistylesThemes,
|
|
63
|
+
settings: {
|
|
64
|
+
initialTheme: (options.initialTheme ?? 'light') as keyof UnistylesThemes,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
8
69
|
/**
|
|
9
70
|
* Get the current system/device color scheme preference.
|
|
10
71
|
*
|
|
@@ -27,19 +88,21 @@ export function getColorScheme(): ColorScheme | null {
|
|
|
27
88
|
}
|
|
28
89
|
|
|
29
90
|
/**
|
|
30
|
-
* Theme settings controller
|
|
91
|
+
* Theme settings controller — wraps Unistyles runtime for theme management.
|
|
92
|
+
*
|
|
93
|
+
* Use this instead of importing `UnistylesRuntime` directly.
|
|
31
94
|
*/
|
|
32
95
|
export const ThemeSettings = {
|
|
33
96
|
/**
|
|
34
97
|
* Set the active theme by name with content color scheme.
|
|
35
98
|
*
|
|
36
|
-
* @param themeName - The theme name to activate
|
|
99
|
+
* @param themeName - The theme name to activate (e.g. 'light', 'dark')
|
|
37
100
|
* @param contentColor - The content color scheme ('light' or 'dark')
|
|
38
101
|
* @param animated - Whether to animate the status bar transition (default: false)
|
|
39
102
|
*
|
|
40
103
|
* @example
|
|
41
104
|
* ```typescript
|
|
42
|
-
* ThemeSettings.setTheme('
|
|
105
|
+
* ThemeSettings.setTheme('dark', 'dark');
|
|
43
106
|
* ThemeSettings.setTheme('light', 'light', true); // animated
|
|
44
107
|
* ```
|
|
45
108
|
*/
|
|
@@ -50,4 +113,36 @@ export const ThemeSettings = {
|
|
|
50
113
|
);
|
|
51
114
|
UnistylesRuntime.statusBar.setStyle((contentColor === 'dark' ? 'light' : 'dark') as any, animated);
|
|
52
115
|
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the name of the currently active theme.
|
|
119
|
+
*
|
|
120
|
+
* @returns The current theme name (e.g. 'light', 'dark')
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const current = ThemeSettings.getThemeName(); // 'dark'
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
getThemeName(): string {
|
|
128
|
+
return String(UnistylesRuntime.themeName);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Enable or disable adaptive (system-following) themes.
|
|
133
|
+
*
|
|
134
|
+
* When enabled, the theme automatically switches to match the device's
|
|
135
|
+
* light/dark mode setting.
|
|
136
|
+
*
|
|
137
|
+
* @param enabled - Whether to follow the system theme
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* ThemeSettings.setAdaptiveThemes(true); // follow system
|
|
142
|
+
* ThemeSettings.setAdaptiveThemes(false); // manual control
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
setAdaptiveThemes(enabled: boolean): void {
|
|
146
|
+
UnistylesRuntime.setAdaptiveThemes(enabled);
|
|
147
|
+
},
|
|
53
148
|
};
|
package/src/componentStyles.ts
CHANGED
|
@@ -22,10 +22,12 @@ import type { TextStyle, ViewStyle } from 'react-native';
|
|
|
22
22
|
/**
|
|
23
23
|
* Registry interface that components augment to register their style types.
|
|
24
24
|
* This enables type-safe extendStyle and overrideStyle calls.
|
|
25
|
+
*
|
|
26
|
+
* Style definitions must use plain style objects, not functions.
|
|
25
27
|
*/
|
|
26
28
|
export interface ComponentStyleRegistry {
|
|
27
29
|
// Components augment this interface to add their style types
|
|
28
|
-
// Example: Text: { text:
|
|
30
|
+
// Example: Text: { text: TextStyle & { variants?: { ... } } }
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -37,38 +39,25 @@ export type ComponentStyleDef<K extends string> = K extends keyof ComponentStyle
|
|
|
37
39
|
: Record<string, any>;
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
|
-
* Deep partial type
|
|
41
|
-
*
|
|
42
|
+
* Deep partial type for style objects.
|
|
43
|
+
* Recursively makes all properties optional.
|
|
44
|
+
* Extensions must be plain style objects — functions are not supported.
|
|
42
45
|
*/
|
|
43
|
-
export type DeepPartialStyle<T> = T extends
|
|
44
|
-
?
|
|
45
|
-
: T
|
|
46
|
-
? { [K in keyof T]?: DeepPartialStyle<T[K]> }
|
|
47
|
-
: T;
|
|
46
|
+
export type DeepPartialStyle<T> = T extends object
|
|
47
|
+
? { [K in keyof T]?: DeepPartialStyle<T[K]> }
|
|
48
|
+
: T;
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
* Style definition for extendStyle -
|
|
51
|
-
*
|
|
51
|
+
* Style definition for extendStyle - plain style objects only.
|
|
52
|
+
* Functions are not supported; the babel plugin merges plain objects into base styles.
|
|
52
53
|
*/
|
|
53
54
|
export type ExtendStyleDef<K extends string> = DeepPartialStyle<ComponentStyleDef<K>>;
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
|
-
* Style definition for overrideStyle - requires full implementation
|
|
57
|
+
* Style definition for overrideStyle - requires full style implementation.
|
|
57
58
|
*/
|
|
58
59
|
export type OverrideStyleDef<K extends string> = ComponentStyleDef<K>;
|
|
59
60
|
|
|
60
|
-
/**
|
|
61
|
-
* Helper to extract the params type from a dynamic style function.
|
|
62
|
-
* Use this to type your extension functions.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```typescript
|
|
66
|
-
* type TextParams = StyleParams<TextStyleDef['text']>;
|
|
67
|
-
* // TextParams = { color?: TextColorVariant }
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export type StyleParams<T> = T extends (params: infer P) => any ? P : never;
|
|
71
|
-
|
|
72
61
|
// =============================================================================
|
|
73
62
|
// Common Style Types
|
|
74
63
|
// =============================================================================
|
|
@@ -86,8 +75,3 @@ export interface StyleWithVariants<TVariants extends Record<string, any> = Recor
|
|
|
86
75
|
[K in keyof TVariants]?: TVariants[K];
|
|
87
76
|
} & { styles: ViewStyle | TextStyle }>;
|
|
88
77
|
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Dynamic style function type.
|
|
92
|
-
*/
|
|
93
|
-
export type DynamicStyleFn<TParams, TStyle> = (params: TParams) => TStyle;
|
package/src/defaults.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global component defaults.
|
|
3
|
+
*
|
|
4
|
+
* These values are used as fallbacks when neither the component prop
|
|
5
|
+
* nor a component-specific default is set.
|
|
6
|
+
* Call the setter once at app startup (e.g., in App.tsx).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { setDefaultMaxFontSizeMultiplier } from '@idealyst/theme';
|
|
11
|
+
*
|
|
12
|
+
* setDefaultMaxFontSizeMultiplier(1.5);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
let _defaultMaxFontSizeMultiplier: number | undefined = undefined;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set the global default `maxFontSizeMultiplier` for all text-rendering components.
|
|
20
|
+
* Any component without an explicit prop or component-level default will use this value.
|
|
21
|
+
* Pass `undefined` to clear (no limit).
|
|
22
|
+
*/
|
|
23
|
+
export function setDefaultMaxFontSizeMultiplier(value: number | undefined): void {
|
|
24
|
+
_defaultMaxFontSizeMultiplier = value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the current global default `maxFontSizeMultiplier`.
|
|
29
|
+
* Returns `undefined` if no default has been set.
|
|
30
|
+
*/
|
|
31
|
+
export function getDefaultMaxFontSizeMultiplier(): number | undefined {
|
|
32
|
+
return _defaultMaxFontSizeMultiplier;
|
|
33
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -41,6 +41,9 @@ export { useStyleProps, type StyleProps } from './useStyleProps';
|
|
|
41
41
|
// Shadow utility (platform-specific via .native.ts)
|
|
42
42
|
export { shadow, type ShadowOptions, type ShadowStyle } from './shadow';
|
|
43
43
|
|
|
44
|
+
// Component defaults
|
|
45
|
+
export { setDefaultMaxFontSizeMultiplier, getDefaultMaxFontSizeMultiplier } from './defaults';
|
|
46
|
+
|
|
44
47
|
// Animation tokens and utilities
|
|
45
48
|
// Note: Use '@idealyst/theme/animation' for full animation API
|
|
46
49
|
export { durations, easings, presets } from './animation/tokens';
|
package/src/theme/extensions.ts
CHANGED