stoop 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +125 -0
  2. package/dist/api/core-api.d.ts +34 -0
  3. package/dist/api/styled.d.ts +0 -1
  4. package/dist/api/theme-provider.d.ts +41 -0
  5. package/dist/constants.d.ts +11 -1
  6. package/dist/core/compiler.d.ts +11 -0
  7. package/dist/core/theme-manager.d.ts +34 -3
  8. package/dist/create-stoop-internal.d.ts +30 -0
  9. package/dist/create-stoop-ssr.d.ts +10 -0
  10. package/dist/create-stoop-ssr.js +16 -0
  11. package/dist/create-stoop.d.ts +32 -1
  12. package/dist/create-stoop.js +16 -147
  13. package/dist/inject.d.ts +113 -0
  14. package/dist/types/index.d.ts +147 -12
  15. package/dist/types/react-polymorphic-types.d.ts +13 -2
  16. package/dist/utils/auto-preload.d.ts +45 -0
  17. package/dist/utils/helpers.d.ts +64 -0
  18. package/dist/utils/storage.d.ts +148 -0
  19. package/dist/utils/{string.d.ts → theme-utils.d.ts} +20 -3
  20. package/dist/utils/theme.d.ts +14 -2
  21. package/package.json +44 -17
  22. package/dist/api/create-theme.d.ts +0 -13
  23. package/dist/api/create-theme.js +0 -43
  24. package/dist/api/css.d.ts +0 -16
  25. package/dist/api/css.js +0 -20
  26. package/dist/api/global-css.js +0 -89
  27. package/dist/api/keyframes.d.ts +0 -16
  28. package/dist/api/keyframes.js +0 -95
  29. package/dist/api/provider.d.ts +0 -19
  30. package/dist/api/provider.js +0 -109
  31. package/dist/api/styled.js +0 -170
  32. package/dist/api/use-theme.d.ts +0 -13
  33. package/dist/api/use-theme.js +0 -21
  34. package/dist/constants.js +0 -144
  35. package/dist/core/cache.js +0 -68
  36. package/dist/core/compiler.js +0 -198
  37. package/dist/core/theme-manager.js +0 -97
  38. package/dist/core/variants.js +0 -32
  39. package/dist/create-stoop-server.d.ts +0 -33
  40. package/dist/create-stoop-server.js +0 -130
  41. package/dist/index.d.ts +0 -6
  42. package/dist/index.js +0 -5
  43. package/dist/inject/browser.d.ts +0 -58
  44. package/dist/inject/browser.js +0 -149
  45. package/dist/inject/dedup.d.ts +0 -29
  46. package/dist/inject/dedup.js +0 -38
  47. package/dist/inject/index.d.ts +0 -40
  48. package/dist/inject/index.js +0 -75
  49. package/dist/inject/ssr.d.ts +0 -27
  50. package/dist/inject/ssr.js +0 -46
  51. package/dist/ssr.d.ts +0 -21
  52. package/dist/ssr.js +0 -19
  53. package/dist/types/index.js +0 -5
  54. package/dist/utils/environment.d.ts +0 -6
  55. package/dist/utils/environment.js +0 -12
  56. package/dist/utils/string.js +0 -253
  57. package/dist/utils/theme-map.d.ts +0 -22
  58. package/dist/utils/theme-map.js +0 -97
  59. package/dist/utils/theme-validation.d.ts +0 -13
  60. package/dist/utils/theme-validation.js +0 -36
  61. package/dist/utils/theme.js +0 -279
  62. package/dist/utils/type-guards.d.ts +0 -26
  63. package/dist/utils/type-guards.js +0 -38
  64. package/dist/utils/utilities.d.ts +0 -14
  65. package/dist/utils/utilities.js +0 -43
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Helper utilities for Stoop.
3
+ * Consolidates environment detection, type guards, theme validation, and utility function application.
4
+ */
5
+ import type { CSS, DefaultTheme, StyledComponentRef, Theme, UtilityFunction } from "../types";
6
+ /**
7
+ * Checks if code is running in a browser environment.
8
+ *
9
+ * @returns True if running in browser, false if in SSR/Node environment
10
+ */
11
+ export declare function isBrowser(): boolean;
12
+ /**
13
+ * Checks if running in production mode.
14
+ * Extracted to helper function for consistency.
15
+ *
16
+ * @returns True if running in production mode
17
+ */
18
+ export declare function isProduction(): boolean;
19
+ /**
20
+ * Type guard for CSS objects.
21
+ *
22
+ * @param value - Value to check
23
+ * @returns True if value is a CSS object
24
+ */
25
+ export declare function isCSSObject(value: unknown): value is CSS;
26
+ /**
27
+ * Checks if a value is a styled component reference.
28
+ * Consolidated function used across the codebase.
29
+ *
30
+ * @param value - Value to check
31
+ * @returns True if value is a styled component reference
32
+ */
33
+ export declare function isStyledComponentRef(value: unknown): value is StyledComponentRef;
34
+ /**
35
+ * Type guard for valid CSS objects (excludes styled component references).
36
+ *
37
+ * @param value - Value to check
38
+ * @returns True if value is a valid CSS object
39
+ */
40
+ export declare function isValidCSSObject(value: unknown): value is CSS;
41
+ /**
42
+ * Type guard for theme objects.
43
+ *
44
+ * @param value - Value to check
45
+ * @returns True if value is a theme object
46
+ */
47
+ export declare function isThemeObject(value: unknown): value is Theme;
48
+ /**
49
+ * Validates that a theme object only contains approved scales.
50
+ * In production, skips all validation for performance.
51
+ *
52
+ * @param theme - Theme object to validate
53
+ * @returns Validated theme as DefaultTheme
54
+ * @throws Error if theme contains invalid scales (development only)
55
+ */
56
+ export declare function validateTheme(theme: unknown): DefaultTheme;
57
+ /**
58
+ * Applies utility functions to transform shorthand properties into CSS.
59
+ *
60
+ * @param styles - CSS object to process
61
+ * @param utils - Optional utility functions object
62
+ * @returns CSS object with utilities applied
63
+ */
64
+ export declare function applyUtilities(styles: CSS, utils?: Record<string, UtilityFunction>): CSS;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Storage and theme detection utilities.
3
+ * Provides simplified localStorage and cookie management, plus automatic theme selection.
4
+ * Supports SSR compatibility and error handling.
5
+ */
6
+ import type { ThemeDetectionOptions, ThemeDetectionResult, StorageOptions, StorageResult } from "../types";
7
+ /**
8
+ * Safely gets a value from localStorage.
9
+ *
10
+ * @param key - Storage key
11
+ * @returns Storage result
12
+ */
13
+ export declare function getFromStorage(key: string): StorageResult<string | null>;
14
+ /**
15
+ * Safely sets a value in localStorage.
16
+ *
17
+ * @param key - Storage key
18
+ * @param value - Value to store
19
+ * @returns Storage result
20
+ */
21
+ export declare function setInStorage(key: string, value: string): StorageResult<void>;
22
+ /**
23
+ * Safely removes a value from localStorage.
24
+ *
25
+ * @param key - Storage key
26
+ * @returns Storage result
27
+ */
28
+ export declare function removeFromStorage(key: string): StorageResult<void>;
29
+ /**
30
+ * Gets a cookie value.
31
+ *
32
+ * @param name - Cookie name
33
+ * @returns Cookie value or null if not found
34
+ */
35
+ export declare function getCookie(name: string): string | null;
36
+ /**
37
+ * Sets a cookie value.
38
+ *
39
+ * @param name - Cookie name
40
+ * @param value - Cookie value
41
+ * @param options - Cookie options
42
+ * @returns Success status
43
+ */
44
+ export declare function setCookie(name: string, value: string, options?: Omit<StorageOptions, "type"> & {
45
+ maxAge?: number;
46
+ path?: string;
47
+ secure?: boolean;
48
+ }): boolean;
49
+ /**
50
+ * Removes a cookie by setting it to expire.
51
+ *
52
+ * @param name - Cookie name
53
+ * @param path - Cookie path
54
+ * @returns Success status
55
+ */
56
+ export declare function removeCookie(name: string, path?: string): boolean;
57
+ /**
58
+ * Unified storage API that works with both localStorage and cookies.
59
+ *
60
+ * @param key - Storage key
61
+ * @param options - Storage options
62
+ * @returns Storage result
63
+ */
64
+ export declare function getStorage(key: string, options?: StorageOptions): StorageResult<string | null>;
65
+ /**
66
+ * Unified storage API that works with both localStorage and cookies.
67
+ *
68
+ * @param key - Storage key
69
+ * @param value - Value to store
70
+ * @param options - Storage options
71
+ * @returns Storage result
72
+ */
73
+ export declare function setStorage(key: string, value: string, options?: StorageOptions): StorageResult<void>;
74
+ /**
75
+ * Unified storage API that works with both localStorage and cookies.
76
+ *
77
+ * @param key - Storage key
78
+ * @param options - Storage options
79
+ * @returns Storage result
80
+ */
81
+ export declare function removeStorage(key: string, options?: StorageOptions): StorageResult<void>;
82
+ /**
83
+ * Gets a JSON value from storage with automatic parsing.
84
+ *
85
+ * @param key - Storage key
86
+ * @param fallback - Fallback value if parsing fails or key not found
87
+ * @param options - Storage options
88
+ * @returns Parsed JSON value or fallback
89
+ */
90
+ export declare function getJsonFromStorage<T>(key: string, fallback: T, options?: StorageOptions): StorageResult<T>;
91
+ /**
92
+ * Sets a JSON value in storage with automatic serialization.
93
+ *
94
+ * @param key - Storage key
95
+ * @param value - Value to serialize and store
96
+ * @param options - Storage options
97
+ * @returns Storage result
98
+ */
99
+ export declare function setJsonInStorage<T>(key: string, value: T, options?: StorageOptions): StorageResult<void>;
100
+ /**
101
+ * Creates a typed storage interface for a specific key.
102
+ *
103
+ * @param key - Storage key
104
+ * @param options - Storage options
105
+ * @returns Typed storage interface
106
+ */
107
+ export declare function createStorage<T = string>(key: string, options?: StorageOptions): {
108
+ get: () => StorageResult<string | null>;
109
+ getJson: (fallback: T) => StorageResult<T>;
110
+ remove: () => StorageResult<void>;
111
+ set: (value: string) => StorageResult<void>;
112
+ setJson: (value: T) => StorageResult<void>;
113
+ };
114
+ /**
115
+ * Detects the best theme to use based on multiple sources with priority.
116
+ *
117
+ * Priority order (highest to lowest):
118
+ * 1. Explicit localStorage preference
119
+ * 2. Cookie preference (for SSR compatibility)
120
+ * 3. System color scheme preference
121
+ * 4. Default theme
122
+ *
123
+ * @param options - Theme detection options
124
+ * @returns Theme detection result
125
+ */
126
+ export declare function detectTheme(options?: ThemeDetectionOptions): ThemeDetectionResult;
127
+ /**
128
+ * Creates a theme detector function with pre-configured options.
129
+ *
130
+ * @param options - Theme detection options
131
+ * @returns Theme detection function
132
+ */
133
+ export declare function createThemeDetector(options: ThemeDetectionOptions): () => ThemeDetectionResult;
134
+ /**
135
+ * Auto-detects theme for SSR contexts (server-side or during hydration).
136
+ * Uses only cookie and default sources since localStorage isn't available.
137
+ *
138
+ * @param options - Theme detection options
139
+ * @returns Theme name
140
+ */
141
+ export declare function detectThemeForSSR(options?: ThemeDetectionOptions): string;
142
+ /**
143
+ * Listens for system theme changes and calls callback when changed.
144
+ *
145
+ * @param callback - Function to call when system theme changes
146
+ * @returns Cleanup function
147
+ */
148
+ export declare function onSystemThemeChange(callback: (theme: "dark" | "light") => void): () => void;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * String utility functions.
3
- * Provides hashing for class name generation, camelCase to kebab-case conversion,
4
- * and CSS sanitization utilities for security.
2
+ * Theme-related string utilities and property mapping.
3
+ * Provides hashing, CSS sanitization, and theme scale mapping for property-aware token resolution.
5
4
  */
5
+ import type { ThemeScale } from "../types";
6
6
  /**
7
7
  * Generates a hash string from an input string using FNV-1a algorithm.
8
8
  * Includes string length to reduce collision probability.
@@ -107,3 +107,20 @@ export declare function validateKeyframeKey(key: string): boolean;
107
107
  * @returns RegExp for matching :root selector blocks
108
108
  */
109
109
  export declare function getRootRegex(prefix?: string): RegExp;
110
+ /**
111
+ * Auto-detects theme scale from CSS property name using pattern matching.
112
+ * Used as fallback when property is not in DEFAULT_THEME_MAP.
113
+ *
114
+ * @param property - CSS property name
115
+ * @returns Theme scale name or undefined if no pattern matches
116
+ */
117
+ export declare function autoDetectScale(property: string): ThemeScale | undefined;
118
+ /**
119
+ * Gets the theme scale for a CSS property.
120
+ * Checks user themeMap first, then default themeMap, then pattern matching.
121
+ *
122
+ * @param property - CSS property name
123
+ * @param userThemeMap - Optional user-provided themeMap override
124
+ * @returns Theme scale name or undefined if no mapping found
125
+ */
126
+ export declare function getScaleForProperty(property: string, userThemeMap?: Record<string, ThemeScale>): ThemeScale | undefined;
@@ -28,9 +28,21 @@ export declare function tokenToCSSVar(token: string, theme?: Theme, property?: s
28
28
  *
29
29
  * @param theme - Theme object to convert to CSS variables
30
30
  * @param prefix - Optional prefix for CSS variable names
31
- * @returns CSS string with :root selector and CSS variables
31
+ * @param attributeSelector - Optional attribute selector (e.g., '[data-theme="light"]'). Defaults to ':root'
32
+ * @returns CSS string with selector and CSS variables
32
33
  */
33
- export declare function generateCSSVariables(theme: Theme, prefix?: string): string;
34
+ export declare function generateCSSVariables(theme: Theme, prefix?: string, attributeSelector?: string): string;
35
+ /**
36
+ * Generates CSS custom properties for all themes using attribute selectors.
37
+ * This allows all themes to be available simultaneously, with theme switching
38
+ * handled by changing the data-theme attribute.
39
+ *
40
+ * @param themes - Map of theme names to theme objects
41
+ * @param prefix - Optional prefix for CSS variable names
42
+ * @param attribute - Attribute name for theme selection (defaults to 'data-theme')
43
+ * @returns CSS string with all theme CSS variables
44
+ */
45
+ export declare function generateAllThemeVariables(themes: Record<string, Theme>, prefix?: string, attribute?: string): string;
34
46
  /**
35
47
  * Recursively replaces theme tokens with CSS variable references in a CSS object.
36
48
  *
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "stoop",
3
3
  "description": "CSS-in-JS library with type inference, theme creation, and variants support.",
4
- "version": "0.3.0",
4
+ "version": "0.4.1",
5
5
  "author": "Jackson Dolman <mail@dolmios.com>",
6
- "main": "./dist/index.js",
6
+ "main": "./dist/create-stoop.js",
7
+ "types": "./dist/create-stoop.d.ts",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "https://github.com/dolmios/stoop.git"
@@ -16,24 +17,48 @@
16
17
  "react-polymorphic-types": "^2.0.0"
17
18
  },
18
19
  "devDependencies": {
19
- "@types/node": "^22.10.5",
20
- "@types/react": "^19.0.6",
21
- "@types/react-dom": "^19.0.2",
22
- "bun-types": "^1.2.6",
23
- "typescript": "^5.7.2"
20
+ "@types/node": "^25.0.3",
21
+ "@types/react": "^19.2.7",
22
+ "@types/react-dom": "^19.2.3",
23
+ "bun-types": "^1.3.5",
24
+ "typescript": "^5.9.3"
24
25
  },
25
26
  "exports": {
26
27
  ".": {
27
- "types": "./dist/index.d.ts",
28
- "import": "./dist/index.js",
29
- "require": "./dist/index.js",
30
- "default": "./dist/index.js"
28
+ "types": "./dist/create-stoop.d.ts",
29
+ "import": "./dist/create-stoop.js",
30
+ "require": "./dist/create-stoop.js",
31
+ "default": "./dist/create-stoop.js"
32
+ },
33
+ "./utils/storage": {
34
+ "types": "./dist/utils/storage.d.ts",
35
+ "import": "./dist/utils/storage.js",
36
+ "require": "./dist/utils/storage.js",
37
+ "default": "./dist/utils/storage.js"
38
+ },
39
+ "./utils/auto-preload": {
40
+ "types": "./dist/utils/auto-preload.d.ts",
41
+ "import": "./dist/utils/auto-preload.js",
42
+ "require": "./dist/utils/auto-preload.js",
43
+ "default": "./dist/utils/auto-preload.js"
44
+ },
45
+ "./types": {
46
+ "types": "./dist/types/index.d.ts",
47
+ "import": "./dist/types/index.js",
48
+ "require": "./dist/types/index.js",
49
+ "default": "./dist/types/index.js"
50
+ },
51
+ "./inject": {
52
+ "types": "./dist/inject.d.ts",
53
+ "import": "./dist/inject.js",
54
+ "require": "./dist/inject.js",
55
+ "default": "./dist/inject.js"
31
56
  },
32
57
  "./ssr": {
33
- "types": "./dist/ssr.d.ts",
34
- "import": "./dist/ssr.js",
35
- "require": "./dist/ssr.js",
36
- "default": "./dist/ssr.js"
58
+ "types": "./dist/create-stoop-ssr.d.ts",
59
+ "import": "./dist/create-stoop-ssr.js",
60
+ "require": "./dist/create-stoop-ssr.js",
61
+ "default": "./dist/create-stoop-ssr.js"
37
62
  }
38
63
  },
39
64
  "files": [
@@ -72,11 +97,13 @@
72
97
  "react-dom": ">=16.8.0"
73
98
  },
74
99
  "scripts": {
75
- "build": "bun run build:clean && bun run build:types",
100
+ "build": "bun run build:clean && bun run build:js && bun run build:js:ssr && bun run build:types",
76
101
  "build:clean": "rm -rf dist",
77
102
  "build:copy-declarations": "mkdir -p dist/types && cp src/types/react-polymorphic-types.d.ts dist/types/react-polymorphic-types.d.ts 2>/dev/null || :",
78
- "build:js": "NODE_ENV=production bun build ./src/index.ts --outdir ./dist --target node --minify --jsx=automatic --splitting --external react --external react-dom --external react/jsx-runtime --define process.env.NODE_ENV='\"production\"'",
103
+ "build:js": "NODE_ENV=production bun build ./src/create-stoop.ts --outfile ./dist/create-stoop.js --target browser --format esm --minify --jsx=automatic --external react --external react-dom --external react/jsx-runtime --define process.env.NODE_ENV='\"production\"'",
104
+ "build:js:ssr": "NODE_ENV=production bun build ./src/create-stoop-ssr.ts --outfile ./dist/create-stoop-ssr.js --target browser --format esm --minify --jsx=automatic --external react --external react-dom --external react/jsx-runtime --define process.env.NODE_ENV='\"production\"'",
79
105
  "build:types": "bunx tsc --project tsconfig.build.json && bun run build:copy-declarations",
106
+ "lint": "eslint src --fix",
80
107
  "prepare": "bun run build",
81
108
  "test": "bun test",
82
109
  "test:coverage": "bun test --coverage",
@@ -1,13 +0,0 @@
1
- /**
2
- * Theme extension API.
3
- * Creates a function that deep merges theme overrides with a base theme.
4
- */
5
- import type { Theme } from "../types";
6
- /**
7
- * Creates a function that extends a base theme with overrides.
8
- * The returned function deep merges theme overrides with the base theme.
9
- *
10
- * @param baseTheme - Base theme to extend
11
- * @returns Function that accepts theme overrides and returns a merged theme
12
- */
13
- export declare function createTheme(baseTheme: Theme): (themeOverrides: Partial<Theme>) => Theme;
@@ -1,43 +0,0 @@
1
- /**
2
- * Theme extension API.
3
- * Creates a function that deep merges theme overrides with a base theme.
4
- */
5
- import { validateTheme } from "../utils/theme-validation";
6
- import { isThemeObject } from "../utils/type-guards";
7
- /**
8
- * Creates a function that extends a base theme with overrides.
9
- * The returned function deep merges theme overrides with the base theme.
10
- *
11
- * @param baseTheme - Base theme to extend
12
- * @returns Function that accepts theme overrides and returns a merged theme
13
- */
14
- export function createTheme(baseTheme) {
15
- return function createTheme(themeOverrides) {
16
- const validatedOverrides = validateTheme(themeOverrides);
17
- function deepMerge(target, source) {
18
- const result = { ...target };
19
- const sourceKeys = Object.keys(source);
20
- for (const key of sourceKeys) {
21
- const sourceValue = source[key];
22
- const targetValue = target[key];
23
- if (isThemeObject(sourceValue) && isThemeObject(targetValue)) {
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- result[key] = { ...targetValue, ...sourceValue };
26
- }
27
- else if (sourceValue !== undefined) {
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- result[key] = sourceValue;
30
- }
31
- }
32
- const targetKeys = Object.keys(target);
33
- for (const key of targetKeys) {
34
- if (!(key in result)) {
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- result[key] = target[key];
37
- }
38
- }
39
- return result;
40
- }
41
- return deepMerge(baseTheme, validatedOverrides);
42
- };
43
- }
package/dist/api/css.d.ts DELETED
@@ -1,16 +0,0 @@
1
- /**
2
- * CSS class generation API.
3
- * Creates a function that compiles CSS objects into class names.
4
- */
5
- import type { CSS, Theme, ThemeScale, UtilityFunction } from "../types";
6
- /**
7
- * Creates a CSS function that compiles CSS objects into class names.
8
- *
9
- * @param defaultTheme - Default theme for token resolution
10
- * @param prefix - Optional prefix for generated class names
11
- * @param media - Optional media query breakpoints
12
- * @param utils - Optional utility functions
13
- * @param themeMap - Optional theme scale mappings
14
- * @returns Function that accepts CSS objects and returns class names
15
- */
16
- export declare function createCSSFunction(defaultTheme: Theme, prefix?: string, media?: Record<string, string>, utils?: Record<string, UtilityFunction>, themeMap?: Record<string, ThemeScale>): (styles: CSS) => string;
package/dist/api/css.js DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * CSS class generation API.
3
- * Creates a function that compiles CSS objects into class names.
4
- */
5
- import { compileCSS } from "../core/compiler";
6
- /**
7
- * Creates a CSS function that compiles CSS objects into class names.
8
- *
9
- * @param defaultTheme - Default theme for token resolution
10
- * @param prefix - Optional prefix for generated class names
11
- * @param media - Optional media query breakpoints
12
- * @param utils - Optional utility functions
13
- * @param themeMap - Optional theme scale mappings
14
- * @returns Function that accepts CSS objects and returns class names
15
- */
16
- export function createCSSFunction(defaultTheme, prefix = "stoop", media, utils, themeMap) {
17
- return function css(styles) {
18
- return compileCSS(styles, defaultTheme, prefix, media, utils, themeMap);
19
- };
20
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * Global CSS injection API.
3
- * Creates a function that injects global styles into the document.
4
- * Supports media queries, nested selectors, and theme tokens.
5
- */
6
- import { MAX_CSS_NESTING_DEPTH } from "../constants";
7
- import { injectCSS } from "../inject";
8
- import { escapeCSSValue, hashObject, sanitizeCSSPropertyName, sanitizeCSSSelector, sanitizeMediaQuery, sanitizePrefix, } from "../utils/string";
9
- import { replaceThemeTokensWithVars } from "../utils/theme";
10
- import { isCSSObject } from "../utils/type-guards";
11
- import { applyUtilities } from "../utils/utilities";
12
- /**
13
- * Creates a global CSS injection function.
14
- * Injects styles directly into the document with deduplication support.
15
- *
16
- * @param defaultTheme - Default theme for token resolution
17
- * @param prefix - Optional prefix for CSS rules
18
- * @param media - Optional media query breakpoints
19
- * @param utils - Optional utility functions
20
- * @param themeMap - Optional theme scale mappings
21
- * @returns Function that accepts CSS objects and returns a cleanup function
22
- */
23
- const globalInjectedStyles = new WeakMap();
24
- function getInjectedSet(theme) {
25
- let set = globalInjectedStyles.get(theme);
26
- if (!set) {
27
- set = new Set();
28
- globalInjectedStyles.set(theme, set);
29
- }
30
- return set;
31
- }
32
- export function createGlobalCSSFunction(defaultTheme, prefix = "stoop", media, utils, themeMap) {
33
- return function globalCss(styles) {
34
- const injected = getInjectedSet(defaultTheme);
35
- const cssKey = hashObject(styles);
36
- if (injected.has(cssKey)) {
37
- return () => { };
38
- }
39
- injected.add(cssKey);
40
- function generateGlobalCSS(obj, depth = 0) {
41
- if (depth > MAX_CSS_NESTING_DEPTH) {
42
- return "";
43
- }
44
- let result = "";
45
- const sortedEntries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b));
46
- sortedEntries.forEach(([key, value]) => {
47
- if (isCSSObject(value)) {
48
- if (media && key in media) {
49
- const mediaQuery = sanitizeMediaQuery(media[key]);
50
- if (mediaQuery) {
51
- const nestedCss = generateGlobalCSS(value, depth + 1);
52
- result += `${mediaQuery} { ${nestedCss} }`;
53
- }
54
- }
55
- else if (key.startsWith("@")) {
56
- const sanitizedKey = sanitizeCSSSelector(key);
57
- if (sanitizedKey) {
58
- const nestedCss = generateGlobalCSS(value, depth + 1);
59
- result += `${sanitizedKey} { ${nestedCss} }`;
60
- }
61
- }
62
- else {
63
- const sanitizedKey = sanitizeCSSSelector(key);
64
- if (sanitizedKey) {
65
- const nestedCss = generateGlobalCSS(value, depth + 1);
66
- result += `${sanitizedKey} { ${nestedCss} }`;
67
- }
68
- }
69
- }
70
- else if (value !== undefined) {
71
- const property = sanitizeCSSPropertyName(key);
72
- if (property && (typeof value === "string" || typeof value === "number")) {
73
- const escapedValue = escapeCSSValue(value);
74
- result += `${property}: ${escapedValue}; `;
75
- }
76
- }
77
- });
78
- return result;
79
- }
80
- const sanitizedPrefix = sanitizePrefix(prefix);
81
- const stylesWithUtils = applyUtilities(styles, utils);
82
- const themedStyles = replaceThemeTokensWithVars(stylesWithUtils, defaultTheme, themeMap);
83
- const cssText = generateGlobalCSS(themedStyles);
84
- injectCSS(cssText, sanitizedPrefix, `__global_${cssKey}`);
85
- return () => {
86
- injected.delete(cssKey);
87
- };
88
- };
89
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * CSS keyframes animation API.
3
- * Creates a function that generates and injects @keyframes rules.
4
- * Caches animations by content hash to prevent duplicates.
5
- */
6
- import type { CSS, Theme, ThemeScale } from "../types";
7
- /**
8
- * Creates a keyframes animation function.
9
- * Generates and injects @keyframes rules with caching to prevent duplicates.
10
- *
11
- * @param prefix - Optional prefix for animation names
12
- * @param theme - Optional theme for token resolution
13
- * @param themeMap - Optional theme scale mappings
14
- * @returns Function that accepts keyframes objects and returns animation names
15
- */
16
- export declare function createKeyframesFunction(prefix?: string, theme?: Theme, themeMap?: Record<string, ThemeScale>): (keyframes: Record<string, CSS>) => string;
@@ -1,95 +0,0 @@
1
- /**
2
- * CSS keyframes animation API.
3
- * Creates a function that generates and injects @keyframes rules.
4
- * Caches animations by content hash to prevent duplicates.
5
- */
6
- import { LRUCache } from "../core/cache";
7
- import { injectCSS } from "../inject";
8
- import { hashObject, sanitizeCSSPropertyName, sanitizePrefix, validateKeyframeKey, } from "../utils/string";
9
- import { replaceThemeTokensWithVars } from "../utils/theme";
10
- /**
11
- * Converts a keyframes object to a CSS @keyframes rule string.
12
- *
13
- * @param keyframesObj - Keyframes object with percentage/from/to keys
14
- * @param animationName - Name for the animation
15
- * @param theme - Optional theme for token resolution
16
- * @param themeMap - Optional theme scale mappings
17
- * @returns CSS @keyframes rule string
18
- */
19
- function keyframesToCSS(keyframesObj, animationName, theme, themeMap) {
20
- let css = `@keyframes ${animationName} {`;
21
- const sortedKeys = Object.keys(keyframesObj).sort((a, b) => {
22
- const aNum = parseFloat(a.replace("%", ""));
23
- const bNum = parseFloat(b.replace("%", ""));
24
- if (a === "from") {
25
- return -1;
26
- }
27
- if (b === "from") {
28
- return 1;
29
- }
30
- if (a === "to") {
31
- return 1;
32
- }
33
- if (b === "to") {
34
- return -1;
35
- }
36
- return aNum - bNum;
37
- });
38
- for (const key of sortedKeys) {
39
- if (!validateKeyframeKey(key)) {
40
- continue;
41
- }
42
- const styles = keyframesObj[key];
43
- if (!styles || typeof styles !== "object") {
44
- continue;
45
- }
46
- css += ` ${key} {`;
47
- const themedStyles = replaceThemeTokensWithVars(styles, theme, themeMap);
48
- // Sort properties for deterministic CSS generation
49
- const sortedProps = Object.keys(themedStyles).sort();
50
- for (const prop of sortedProps) {
51
- const value = themedStyles[prop];
52
- if (value !== undefined && (typeof value === "string" || typeof value === "number")) {
53
- const sanitizedProp = sanitizeCSSPropertyName(prop);
54
- if (sanitizedProp) {
55
- // Don't escape keyframe values - escaping breaks complex CSS functions
56
- const cssValue = String(value);
57
- css += ` ${sanitizedProp}: ${cssValue};`;
58
- }
59
- }
60
- }
61
- css += " }";
62
- }
63
- css += " }";
64
- return css;
65
- }
66
- const KEYFRAME_CACHE_LIMIT = 500;
67
- /**
68
- * Creates a keyframes animation function.
69
- * Generates and injects @keyframes rules with caching to prevent duplicates.
70
- *
71
- * @param prefix - Optional prefix for animation names
72
- * @param theme - Optional theme for token resolution
73
- * @param themeMap - Optional theme scale mappings
74
- * @returns Function that accepts keyframes objects and returns animation names
75
- */
76
- export function createKeyframesFunction(prefix = "stoop", theme, themeMap) {
77
- const sanitizedPrefix = sanitizePrefix(prefix);
78
- const animationCache = new LRUCache(KEYFRAME_CACHE_LIMIT);
79
- return function keyframes(keyframesObj) {
80
- const keyframesKey = hashObject(keyframesObj);
81
- const cachedName = animationCache.get(keyframesKey);
82
- if (cachedName) {
83
- return cachedName;
84
- }
85
- const hashValue = keyframesKey.slice(0, 8);
86
- const animationName = sanitizedPrefix
87
- ? `${sanitizedPrefix}-${hashValue}`
88
- : `stoop-${hashValue}`;
89
- const css = keyframesToCSS(keyframesObj, animationName, theme, themeMap);
90
- const ruleKey = `__keyframes_${animationName}`;
91
- injectCSS(css, sanitizedPrefix, ruleKey);
92
- animationCache.set(keyframesKey, animationName);
93
- return animationName;
94
- };
95
- }