stoop 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/dist/api/core-api.d.ts +34 -0
- package/dist/api/{keyframes.js → core-api.js} +45 -5
- package/dist/api/global-css.js +11 -58
- package/dist/api/styled.d.ts +0 -1
- package/dist/api/styled.js +265 -16
- package/dist/api/theme-provider.d.ts +41 -0
- package/dist/api/theme-provider.js +223 -0
- package/dist/constants.d.ts +11 -1
- package/dist/constants.js +11 -1
- package/dist/core/compiler.d.ts +11 -0
- package/dist/core/compiler.js +15 -7
- package/dist/core/theme-manager.d.ts +34 -3
- package/dist/core/theme-manager.js +55 -45
- package/dist/core/variants.js +9 -3
- package/dist/create-stoop-internal.d.ts +30 -0
- package/dist/create-stoop-internal.js +123 -0
- package/dist/create-stoop-ssr.d.ts +10 -0
- package/dist/create-stoop-ssr.js +26 -0
- package/dist/create-stoop.d.ts +32 -1
- package/dist/create-stoop.js +78 -69
- package/dist/inject.d.ts +113 -0
- package/dist/inject.js +308 -0
- package/dist/types/index.d.ts +147 -12
- package/dist/types/react-polymorphic-types.d.ts +13 -2
- package/dist/utils/auto-preload.d.ts +45 -0
- package/dist/utils/auto-preload.js +167 -0
- package/dist/utils/helpers.d.ts +64 -0
- package/dist/utils/helpers.js +150 -0
- package/dist/utils/storage.d.ts +148 -0
- package/dist/utils/storage.js +396 -0
- package/dist/utils/{string.d.ts → theme-utils.d.ts} +20 -3
- package/dist/utils/{string.js → theme-utils.js} +109 -9
- package/dist/utils/theme.d.ts +14 -2
- package/dist/utils/theme.js +41 -16
- package/package.json +48 -23
- package/dist/api/create-theme.d.ts +0 -13
- package/dist/api/create-theme.js +0 -43
- package/dist/api/css.d.ts +0 -16
- package/dist/api/css.js +0 -20
- package/dist/api/keyframes.d.ts +0 -16
- package/dist/api/provider.d.ts +0 -19
- package/dist/api/provider.js +0 -109
- package/dist/api/use-theme.d.ts +0 -13
- package/dist/api/use-theme.js +0 -21
- package/dist/create-stoop-server.d.ts +0 -33
- package/dist/create-stoop-server.js +0 -130
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -5
- package/dist/inject/browser.d.ts +0 -58
- package/dist/inject/browser.js +0 -149
- package/dist/inject/dedup.d.ts +0 -29
- package/dist/inject/dedup.js +0 -38
- package/dist/inject/index.d.ts +0 -40
- package/dist/inject/index.js +0 -75
- package/dist/inject/ssr.d.ts +0 -27
- package/dist/inject/ssr.js +0 -46
- package/dist/ssr.d.ts +0 -21
- package/dist/ssr.js +0 -19
- package/dist/utils/environment.d.ts +0 -6
- package/dist/utils/environment.js +0 -12
- package/dist/utils/theme-map.d.ts +0 -22
- package/dist/utils/theme-map.js +0 -97
- package/dist/utils/theme-validation.d.ts +0 -13
- package/dist/utils/theme-validation.js +0 -36
- package/dist/utils/type-guards.d.ts +0 -26
- package/dist/utils/type-guards.js +0 -38
- package/dist/utils/utilities.d.ts +0 -14
- package/dist/utils/utilities.js +0 -43
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# stoop
|
|
2
|
+
|
|
3
|
+
A lightweight, type-safe CSS-in-JS library for React with theme support, variants, and SSR capabilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install stoop
|
|
9
|
+
# or
|
|
10
|
+
bun add stoop
|
|
11
|
+
# or
|
|
12
|
+
yarn add stoop
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { createStoop } from "stoop";
|
|
19
|
+
|
|
20
|
+
const { styled, css, Provider, useTheme } = createStoop({
|
|
21
|
+
theme: {
|
|
22
|
+
colors: {
|
|
23
|
+
primary: "#0070f3",
|
|
24
|
+
background: "#ffffff",
|
|
25
|
+
text: "#000000",
|
|
26
|
+
},
|
|
27
|
+
space: {
|
|
28
|
+
small: "8px",
|
|
29
|
+
medium: "16px",
|
|
30
|
+
large: "24px",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
themes: {
|
|
34
|
+
light: {
|
|
35
|
+
/* ... */
|
|
36
|
+
},
|
|
37
|
+
dark: {
|
|
38
|
+
/* ... */
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const Button = styled("button", {
|
|
44
|
+
padding: "$medium",
|
|
45
|
+
backgroundColor: "$primary",
|
|
46
|
+
color: "$text",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
<Button>Click me</Button>;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Type-safe theming** with TypeScript inference
|
|
55
|
+
- **CSS variables** for instant theme switching
|
|
56
|
+
- **Variant system** for component variations
|
|
57
|
+
- **SSR support** via `getCssText()`
|
|
58
|
+
- **Multiple themes** with built-in Provider
|
|
59
|
+
- **Utility functions** for custom CSS transformations
|
|
60
|
+
- **Zero runtime overhead** for theme switching
|
|
61
|
+
|
|
62
|
+
## Documentation
|
|
63
|
+
|
|
64
|
+
- **[GUIDE.md](./docs/GUIDE.md)** - Step-by-step setup and usage guide
|
|
65
|
+
- **[API.md](./docs/API.md)** - Complete API reference
|
|
66
|
+
- **[ARCHITECTURE.md](./docs/ARCHITECTURE.md)** - Internal implementation details
|
|
67
|
+
- **[TESTING.md](./docs/TESTING.md)** - Testing guide and test suite documentation
|
|
68
|
+
|
|
69
|
+
## API Overview
|
|
70
|
+
|
|
71
|
+
### `createStoop(config)`
|
|
72
|
+
|
|
73
|
+
Creates a Stoop instance. Returns: `styled`, `css`, `createTheme`, `globalCss`, `keyframes`, `getCssText`, `warmCache`, `preloadTheme`, `theme`, `config`. If `themes` config is provided, also returns `Provider` and `useTheme`.
|
|
74
|
+
|
|
75
|
+
### Theme Tokens
|
|
76
|
+
|
|
77
|
+
Use `$` prefix for theme tokens. Shorthand `$token` uses property-aware resolution (preferred); explicit `$scale.token` specifies the scale.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
{
|
|
81
|
+
color: "$primary", // Shorthand (preferred, property-aware)
|
|
82
|
+
padding: "$medium", // Property-aware → space scale
|
|
83
|
+
fontSize: "$fontSizes.small", // Explicit scale
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Variants
|
|
88
|
+
|
|
89
|
+
Variants create component variations via props:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
const Button = styled(
|
|
93
|
+
"button",
|
|
94
|
+
{},
|
|
95
|
+
{
|
|
96
|
+
variant: {
|
|
97
|
+
primary: { backgroundColor: "$primary" },
|
|
98
|
+
secondary: { backgroundColor: "$secondary" },
|
|
99
|
+
},
|
|
100
|
+
size: {
|
|
101
|
+
small: { padding: "$small" },
|
|
102
|
+
large: { padding: "$large" },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
<Button variant="primary" size="small" />;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
# Build
|
|
114
|
+
bun run build
|
|
115
|
+
|
|
116
|
+
# Test
|
|
117
|
+
bun run test
|
|
118
|
+
|
|
119
|
+
# Watch mode
|
|
120
|
+
bun run test:watch
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core API functions.
|
|
3
|
+
* Consolidates theme creation, CSS class generation, and keyframes animation APIs.
|
|
4
|
+
*/
|
|
5
|
+
import type { CSS, Theme, ThemeScale, UtilityFunction } 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;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a CSS function that compiles CSS objects into class names.
|
|
16
|
+
*
|
|
17
|
+
* @param defaultTheme - Default theme for token resolution
|
|
18
|
+
* @param prefix - Optional prefix for generated class names
|
|
19
|
+
* @param media - Optional media query breakpoints
|
|
20
|
+
* @param utils - Optional utility functions
|
|
21
|
+
* @param themeMap - Optional theme scale mappings
|
|
22
|
+
* @returns Function that accepts CSS objects and returns class names
|
|
23
|
+
*/
|
|
24
|
+
export declare function createCSSFunction(defaultTheme: Theme, prefix?: string, media?: Record<string, string>, utils?: Record<string, UtilityFunction>, themeMap?: Record<string, ThemeScale>): (styles: CSS) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a keyframes animation function.
|
|
27
|
+
* Generates and injects @keyframes rules with caching to prevent duplicates.
|
|
28
|
+
*
|
|
29
|
+
* @param prefix - Optional prefix for animation names
|
|
30
|
+
* @param theme - Optional theme for token resolution
|
|
31
|
+
* @param themeMap - Optional theme scale mappings
|
|
32
|
+
* @returns Function that accepts keyframes objects and returns animation names
|
|
33
|
+
*/
|
|
34
|
+
export declare function createKeyframesFunction(prefix?: string, theme?: Theme, themeMap?: Record<string, ThemeScale>): (keyframes: Record<string, CSS>) => string;
|
|
@@ -1,12 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Caches animations by content hash to prevent duplicates.
|
|
2
|
+
* Core API functions.
|
|
3
|
+
* Consolidates theme creation, CSS class generation, and keyframes animation APIs.
|
|
5
4
|
*/
|
|
6
5
|
import { LRUCache } from "../core/cache";
|
|
6
|
+
import { compileCSS } from "../core/compiler";
|
|
7
|
+
import { mergeThemes } from "../core/theme-manager";
|
|
7
8
|
import { injectCSS } from "../inject";
|
|
8
|
-
import {
|
|
9
|
+
import { validateTheme } from "../utils/helpers";
|
|
9
10
|
import { replaceThemeTokensWithVars } from "../utils/theme";
|
|
11
|
+
import { hashObject, sanitizeCSSPropertyName, sanitizePrefix, validateKeyframeKey, } from "../utils/theme-utils";
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Theme Creation API
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Creates a function that extends a base theme with overrides.
|
|
17
|
+
* The returned function deep merges theme overrides with the base theme.
|
|
18
|
+
*
|
|
19
|
+
* @param baseTheme - Base theme to extend
|
|
20
|
+
* @returns Function that accepts theme overrides and returns a merged theme
|
|
21
|
+
*/
|
|
22
|
+
export function createTheme(baseTheme) {
|
|
23
|
+
return function createTheme(themeOverrides = {}) {
|
|
24
|
+
const validatedOverrides = validateTheme(themeOverrides);
|
|
25
|
+
// Use shared mergeThemes function instead of duplicate deepMerge
|
|
26
|
+
return mergeThemes(baseTheme, validatedOverrides);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// CSS Class Generation API
|
|
31
|
+
// ============================================================================
|
|
32
|
+
/**
|
|
33
|
+
* Creates a CSS function that compiles CSS objects into class names.
|
|
34
|
+
*
|
|
35
|
+
* @param defaultTheme - Default theme for token resolution
|
|
36
|
+
* @param prefix - Optional prefix for generated class names
|
|
37
|
+
* @param media - Optional media query breakpoints
|
|
38
|
+
* @param utils - Optional utility functions
|
|
39
|
+
* @param themeMap - Optional theme scale mappings
|
|
40
|
+
* @returns Function that accepts CSS objects and returns class names
|
|
41
|
+
*/
|
|
42
|
+
export function createCSSFunction(defaultTheme, prefix = "stoop", media, utils, themeMap) {
|
|
43
|
+
return function css(styles) {
|
|
44
|
+
return compileCSS(styles, defaultTheme, prefix, media, utils, themeMap);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Keyframes Animation API
|
|
49
|
+
// ============================================================================
|
|
10
50
|
/**
|
|
11
51
|
* Converts a keyframes object to a CSS @keyframes rule string.
|
|
12
52
|
*
|
|
@@ -63,7 +103,7 @@ function keyframesToCSS(keyframesObj, animationName, theme, themeMap) {
|
|
|
63
103
|
css += " }";
|
|
64
104
|
return css;
|
|
65
105
|
}
|
|
66
|
-
|
|
106
|
+
import { KEYFRAME_CACHE_LIMIT } from "../constants";
|
|
67
107
|
/**
|
|
68
108
|
* Creates a keyframes animation function.
|
|
69
109
|
* Generates and injects @keyframes rules with caching to prevent duplicates.
|
package/dist/api/global-css.js
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Creates a function that injects global styles into the document.
|
|
4
4
|
* Supports media queries, nested selectors, and theme tokens.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { cssObjectToString } from "../core/compiler";
|
|
7
7
|
import { injectCSS } from "../inject";
|
|
8
|
-
import {
|
|
8
|
+
import { applyUtilities } from "../utils/helpers";
|
|
9
9
|
import { replaceThemeTokensWithVars } from "../utils/theme";
|
|
10
|
-
import {
|
|
11
|
-
import { applyUtilities } from "../utils/utilities";
|
|
10
|
+
import { hashObject, sanitizePrefix } from "../utils/theme-utils";
|
|
12
11
|
/**
|
|
13
12
|
* Creates a global CSS injection function.
|
|
14
13
|
* Injects styles directly into the document with deduplication support.
|
|
@@ -20,70 +19,24 @@ import { applyUtilities } from "../utils/utilities";
|
|
|
20
19
|
* @param themeMap - Optional theme scale mappings
|
|
21
20
|
* @returns Function that accepts CSS objects and returns a cleanup function
|
|
22
21
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let set = globalInjectedStyles.get(theme);
|
|
26
|
-
if (!set) {
|
|
27
|
-
set = new Set();
|
|
28
|
-
globalInjectedStyles.set(theme, set);
|
|
29
|
-
}
|
|
30
|
-
return set;
|
|
31
|
-
}
|
|
22
|
+
// Use CSS hash as key instead of theme object reference for better deduplication
|
|
23
|
+
const globalInjectedStyles = new Set();
|
|
32
24
|
export function createGlobalCSSFunction(defaultTheme, prefix = "stoop", media, utils, themeMap) {
|
|
33
25
|
return function globalCss(styles) {
|
|
34
|
-
const injected = getInjectedSet(defaultTheme);
|
|
35
26
|
const cssKey = hashObject(styles);
|
|
36
|
-
if (
|
|
27
|
+
if (globalInjectedStyles.has(cssKey)) {
|
|
37
28
|
return () => { };
|
|
38
29
|
}
|
|
39
|
-
|
|
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
|
-
}
|
|
30
|
+
globalInjectedStyles.add(cssKey);
|
|
80
31
|
const sanitizedPrefix = sanitizePrefix(prefix);
|
|
81
32
|
const stylesWithUtils = applyUtilities(styles, utils);
|
|
82
33
|
const themedStyles = replaceThemeTokensWithVars(stylesWithUtils, defaultTheme, themeMap);
|
|
83
|
-
|
|
34
|
+
// Use cssObjectToString from compiler.ts instead of duplicate generateGlobalCSS
|
|
35
|
+
// Empty selector for global CSS (no base selector needed)
|
|
36
|
+
const cssText = cssObjectToString(themedStyles, "", 0, media);
|
|
84
37
|
injectCSS(cssText, sanitizedPrefix, `__global_${cssKey}`);
|
|
85
38
|
return () => {
|
|
86
|
-
|
|
39
|
+
globalInjectedStyles.delete(cssKey);
|
|
87
40
|
};
|
|
88
41
|
};
|
|
89
42
|
}
|
package/dist/api/styled.d.ts
CHANGED
package/dist/api/styled.js
CHANGED
|
@@ -8,7 +8,8 @@ import { useMemo, forwardRef, createElement, useContext, createContext, } from "
|
|
|
8
8
|
import { EMPTY_CSS, STOOP_COMPONENT_SYMBOL } from "../constants";
|
|
9
9
|
import { compileCSS } from "../core/compiler";
|
|
10
10
|
import { applyVariants } from "../core/variants";
|
|
11
|
-
import {
|
|
11
|
+
import { isStyledComponentRef } from "../utils/helpers";
|
|
12
|
+
import { hash, sanitizeClassName } from "../utils/theme-utils";
|
|
12
13
|
let defaultThemeContext = null;
|
|
13
14
|
function getDefaultThemeContext() {
|
|
14
15
|
if (!defaultThemeContext) {
|
|
@@ -33,40 +34,283 @@ export function createStyledComponentRef(className) {
|
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Type guard for styled component references.
|
|
37
|
+
* Uses shared isStyledComponentRef helper for consistency.
|
|
36
38
|
*
|
|
37
39
|
* @param value - Value to check
|
|
38
40
|
* @returns True if value is a styled component reference
|
|
39
41
|
*/
|
|
40
42
|
function isStyledComponent(value) {
|
|
41
|
-
return (
|
|
42
|
-
value !== null &&
|
|
43
|
-
"__isStoopStyled" in value &&
|
|
44
|
-
value.__isStoopStyled === true);
|
|
43
|
+
return isStyledComponentRef(value);
|
|
45
44
|
}
|
|
46
45
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* Set of common CSS properties that should not be passed to DOM elements.
|
|
47
|
+
* These are camelCase CSS properties that React doesn't recognize as valid DOM attributes.
|
|
48
|
+
*/
|
|
49
|
+
const CSS_PROPERTIES = new Set([
|
|
50
|
+
"alignContent",
|
|
51
|
+
"alignItems",
|
|
52
|
+
"alignSelf",
|
|
53
|
+
"animation",
|
|
54
|
+
"animationDelay",
|
|
55
|
+
"animationDirection",
|
|
56
|
+
"animationDuration",
|
|
57
|
+
"animationFillMode",
|
|
58
|
+
"animationIterationCount",
|
|
59
|
+
"animationName",
|
|
60
|
+
"animationPlayState",
|
|
61
|
+
"animationTimingFunction",
|
|
62
|
+
"aspectRatio",
|
|
63
|
+
"backdropFilter",
|
|
64
|
+
"backfaceVisibility",
|
|
65
|
+
"background",
|
|
66
|
+
"backgroundAttachment",
|
|
67
|
+
"backgroundBlendMode",
|
|
68
|
+
"backgroundClip",
|
|
69
|
+
"backgroundColor",
|
|
70
|
+
"backgroundImage",
|
|
71
|
+
"backgroundOrigin",
|
|
72
|
+
"backgroundPosition",
|
|
73
|
+
"backgroundRepeat",
|
|
74
|
+
"backgroundSize",
|
|
75
|
+
"border",
|
|
76
|
+
"borderBottom",
|
|
77
|
+
"borderBottomColor",
|
|
78
|
+
"borderBottomLeftRadius",
|
|
79
|
+
"borderBottomRightRadius",
|
|
80
|
+
"borderBottomStyle",
|
|
81
|
+
"borderBottomWidth",
|
|
82
|
+
"borderCollapse",
|
|
83
|
+
"borderColor",
|
|
84
|
+
"borderImage",
|
|
85
|
+
"borderImageOutset",
|
|
86
|
+
"borderImageRepeat",
|
|
87
|
+
"borderImageSlice",
|
|
88
|
+
"borderImageSource",
|
|
89
|
+
"borderImageWidth",
|
|
90
|
+
"borderLeft",
|
|
91
|
+
"borderLeftColor",
|
|
92
|
+
"borderLeftStyle",
|
|
93
|
+
"borderLeftWidth",
|
|
94
|
+
"borderRadius",
|
|
95
|
+
"borderRight",
|
|
96
|
+
"borderRightColor",
|
|
97
|
+
"borderRightStyle",
|
|
98
|
+
"borderRightWidth",
|
|
99
|
+
"borderSpacing",
|
|
100
|
+
"borderStyle",
|
|
101
|
+
"borderTop",
|
|
102
|
+
"borderTopColor",
|
|
103
|
+
"borderTopLeftRadius",
|
|
104
|
+
"borderTopRightRadius",
|
|
105
|
+
"borderTopStyle",
|
|
106
|
+
"borderTopWidth",
|
|
107
|
+
"borderWidth",
|
|
108
|
+
"bottom",
|
|
109
|
+
"boxShadow",
|
|
110
|
+
"boxSizing",
|
|
111
|
+
"captionSide",
|
|
112
|
+
"caretColor",
|
|
113
|
+
"clear",
|
|
114
|
+
"clip",
|
|
115
|
+
"clipPath",
|
|
116
|
+
"color",
|
|
117
|
+
"columnCount",
|
|
118
|
+
"columnFill",
|
|
119
|
+
"columnGap",
|
|
120
|
+
"columnRule",
|
|
121
|
+
"columnRuleColor",
|
|
122
|
+
"columnRuleStyle",
|
|
123
|
+
"columnRuleWidth",
|
|
124
|
+
"columnSpan",
|
|
125
|
+
"columnWidth",
|
|
126
|
+
"columns",
|
|
127
|
+
"content",
|
|
128
|
+
"counterIncrement",
|
|
129
|
+
"counterReset",
|
|
130
|
+
"cursor",
|
|
131
|
+
"direction",
|
|
132
|
+
"display",
|
|
133
|
+
"emptyCells",
|
|
134
|
+
"filter",
|
|
135
|
+
"flex",
|
|
136
|
+
"flexBasis",
|
|
137
|
+
"flexDirection",
|
|
138
|
+
"flexFlow",
|
|
139
|
+
"flexGrow",
|
|
140
|
+
"flexShrink",
|
|
141
|
+
"flexWrap",
|
|
142
|
+
"float",
|
|
143
|
+
"font",
|
|
144
|
+
"fontFamily",
|
|
145
|
+
"fontFeatureSettings",
|
|
146
|
+
"fontKerning",
|
|
147
|
+
"fontLanguageOverride",
|
|
148
|
+
"fontSize",
|
|
149
|
+
"fontSizeAdjust",
|
|
150
|
+
"fontStretch",
|
|
151
|
+
"fontStyle",
|
|
152
|
+
"fontSynthesis",
|
|
153
|
+
"fontVariant",
|
|
154
|
+
"fontVariantAlternates",
|
|
155
|
+
"fontVariantCaps",
|
|
156
|
+
"fontVariantEastAsian",
|
|
157
|
+
"fontVariantLigatures",
|
|
158
|
+
"fontVariantNumeric",
|
|
159
|
+
"fontVariantPosition",
|
|
160
|
+
"fontWeight",
|
|
161
|
+
"gap",
|
|
162
|
+
"grid",
|
|
163
|
+
"gridArea",
|
|
164
|
+
"gridAutoColumns",
|
|
165
|
+
"gridAutoFlow",
|
|
166
|
+
"gridAutoRows",
|
|
167
|
+
"gridColumn",
|
|
168
|
+
"gridColumnEnd",
|
|
169
|
+
"gridColumnGap",
|
|
170
|
+
"gridColumnStart",
|
|
171
|
+
"gridGap",
|
|
172
|
+
"gridRow",
|
|
173
|
+
"gridRowEnd",
|
|
174
|
+
"gridRowGap",
|
|
175
|
+
"gridRowStart",
|
|
176
|
+
"gridTemplate",
|
|
177
|
+
"gridTemplateAreas",
|
|
178
|
+
"gridTemplateColumns",
|
|
179
|
+
"gridTemplateRows",
|
|
180
|
+
"height",
|
|
181
|
+
"hyphens",
|
|
182
|
+
"imageOrientation",
|
|
183
|
+
"imageRendering",
|
|
184
|
+
"imageResolution",
|
|
185
|
+
"imeMode",
|
|
186
|
+
"inlineSize",
|
|
187
|
+
"isolation",
|
|
188
|
+
"justifyContent",
|
|
189
|
+
"justifyItems",
|
|
190
|
+
"justifySelf",
|
|
191
|
+
"left",
|
|
192
|
+
"letterSpacing",
|
|
193
|
+
"lineHeight",
|
|
194
|
+
"listStyle",
|
|
195
|
+
"listStyleImage",
|
|
196
|
+
"listStylePosition",
|
|
197
|
+
"listStyleType",
|
|
198
|
+
"margin",
|
|
199
|
+
"marginBottom",
|
|
200
|
+
"marginLeft",
|
|
201
|
+
"marginRight",
|
|
202
|
+
"marginTop",
|
|
203
|
+
"maxHeight",
|
|
204
|
+
"maxWidth",
|
|
205
|
+
"minHeight",
|
|
206
|
+
"minWidth",
|
|
207
|
+
"objectFit",
|
|
208
|
+
"objectPosition",
|
|
209
|
+
"opacity",
|
|
210
|
+
"order",
|
|
211
|
+
"orphans",
|
|
212
|
+
"outline",
|
|
213
|
+
"outlineColor",
|
|
214
|
+
"outlineOffset",
|
|
215
|
+
"outlineStyle",
|
|
216
|
+
"outlineWidth",
|
|
217
|
+
"overflow",
|
|
218
|
+
"overflowWrap",
|
|
219
|
+
"overflowX",
|
|
220
|
+
"overflowY",
|
|
221
|
+
"padding",
|
|
222
|
+
"paddingBottom",
|
|
223
|
+
"paddingLeft",
|
|
224
|
+
"paddingRight",
|
|
225
|
+
"paddingTop",
|
|
226
|
+
"pageBreakAfter",
|
|
227
|
+
"pageBreakBefore",
|
|
228
|
+
"pageBreakInside",
|
|
229
|
+
"perspective",
|
|
230
|
+
"perspectiveOrigin",
|
|
231
|
+
"placeContent",
|
|
232
|
+
"placeItems",
|
|
233
|
+
"placeSelf",
|
|
234
|
+
"pointerEvents",
|
|
235
|
+
"position",
|
|
236
|
+
"quotes",
|
|
237
|
+
"resize",
|
|
238
|
+
"right",
|
|
239
|
+
"rowGap",
|
|
240
|
+
"scrollBehavior",
|
|
241
|
+
"tabSize",
|
|
242
|
+
"tableLayout",
|
|
243
|
+
"textAlign",
|
|
244
|
+
"textAlignLast",
|
|
245
|
+
"textDecoration",
|
|
246
|
+
"textDecorationColor",
|
|
247
|
+
"textDecorationLine",
|
|
248
|
+
"textDecorationStyle",
|
|
249
|
+
"textIndent",
|
|
250
|
+
"textJustify",
|
|
251
|
+
"textOverflow",
|
|
252
|
+
"textShadow",
|
|
253
|
+
"textTransform",
|
|
254
|
+
"textUnderlinePosition",
|
|
255
|
+
"top",
|
|
256
|
+
"transform",
|
|
257
|
+
"transformOrigin",
|
|
258
|
+
"transformStyle",
|
|
259
|
+
"transition",
|
|
260
|
+
"transitionDelay",
|
|
261
|
+
"transitionDuration",
|
|
262
|
+
"transitionProperty",
|
|
263
|
+
"transitionTimingFunction",
|
|
264
|
+
"unicodeBidi",
|
|
265
|
+
"userSelect",
|
|
266
|
+
"verticalAlign",
|
|
267
|
+
"visibility",
|
|
268
|
+
"whiteSpace",
|
|
269
|
+
"width",
|
|
270
|
+
"wordBreak",
|
|
271
|
+
"wordSpacing",
|
|
272
|
+
"wordWrap",
|
|
273
|
+
"writingMode",
|
|
274
|
+
"zIndex",
|
|
275
|
+
]);
|
|
276
|
+
/**
|
|
277
|
+
* Checks if a prop name is a CSS property that should be converted to CSS styles.
|
|
278
|
+
*
|
|
279
|
+
* @param propName - The prop name to check
|
|
280
|
+
* @returns True if the prop is a CSS property
|
|
281
|
+
*/
|
|
282
|
+
function isCSSProperty(propName) {
|
|
283
|
+
return CSS_PROPERTIES.has(propName);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Separates component props into variant props, CSS props, and element props.
|
|
287
|
+
* Variant props are used for style variants.
|
|
288
|
+
* CSS props are converted to CSS styles.
|
|
289
|
+
* Element props are passed to the DOM element.
|
|
49
290
|
*
|
|
50
291
|
* @param props - All component props
|
|
51
292
|
* @param variants - Variant configuration
|
|
52
|
-
* @returns Object with separated elementProps and
|
|
293
|
+
* @returns Object with separated elementProps, variantProps, and cssProps
|
|
53
294
|
*/
|
|
54
295
|
function extractVariantProps(props, variants) {
|
|
55
|
-
|
|
56
|
-
return { elementProps: props, variantProps: {} };
|
|
57
|
-
}
|
|
58
|
-
const variantKeys = new Set(Object.keys(variants));
|
|
296
|
+
const variantKeys = variants ? new Set(Object.keys(variants)) : new Set();
|
|
59
297
|
const variantProps = {};
|
|
298
|
+
const cssProps = {};
|
|
60
299
|
const elementProps = {};
|
|
61
300
|
for (const key in props) {
|
|
62
301
|
if (variantKeys.has(key)) {
|
|
63
302
|
variantProps[key] = props[key];
|
|
64
303
|
}
|
|
304
|
+
else if (isCSSProperty(key)) {
|
|
305
|
+
// Convert CSS properties to CSS styles
|
|
306
|
+
cssProps[key] = props[key];
|
|
307
|
+
}
|
|
65
308
|
else {
|
|
309
|
+
// Only include non-CSS, non-variant properties in elementProps
|
|
66
310
|
elementProps[key] = props[key];
|
|
67
311
|
}
|
|
68
312
|
}
|
|
69
|
-
return { elementProps, variantProps };
|
|
313
|
+
return { cssProps, elementProps, variantProps };
|
|
70
314
|
}
|
|
71
315
|
/**
|
|
72
316
|
* Creates a styled component factory function.
|
|
@@ -88,7 +332,7 @@ export function createStyledFunction(defaultTheme, prefix = "stoop", media, util
|
|
|
88
332
|
"variants" in baseStylesOrVariants &&
|
|
89
333
|
typeof baseStylesOrVariants.variants === "object") {
|
|
90
334
|
actualVariants = baseStylesOrVariants.variants;
|
|
91
|
-
const {
|
|
335
|
+
const { variants: _, ...rest } = baseStylesOrVariants;
|
|
92
336
|
actualBaseStyles = rest;
|
|
93
337
|
}
|
|
94
338
|
let baseElementClassName;
|
|
@@ -101,7 +345,7 @@ export function createStyledFunction(defaultTheme, prefix = "stoop", media, util
|
|
|
101
345
|
const cssObject = useMemo(() => cssStyles && typeof cssStyles === "object" && cssStyles !== null
|
|
102
346
|
? cssStyles
|
|
103
347
|
: EMPTY_CSS, [cssStyles]);
|
|
104
|
-
const { elementProps, variantProps } = extractVariantProps(restProps, actualVariants);
|
|
348
|
+
const { cssProps, elementProps, variantProps } = extractVariantProps(restProps, actualVariants);
|
|
105
349
|
const contextValue = useContext(themeContext || getDefaultThemeContext());
|
|
106
350
|
const currentTheme = contextValue?.theme || defaultTheme;
|
|
107
351
|
const currentMedia = currentTheme.media ? { ...media, ...currentTheme.media } : media;
|
|
@@ -123,11 +367,16 @@ export function createStyledFunction(defaultTheme, prefix = "stoop", media, util
|
|
|
123
367
|
if (actualVariants && variantKey) {
|
|
124
368
|
componentStyles = applyVariants(actualVariants, variantProps, actualBaseStyles);
|
|
125
369
|
}
|
|
370
|
+
// Merge CSS props (CSS properties passed as props) into styles
|
|
371
|
+
if (Object.keys(cssProps).length > 0) {
|
|
372
|
+
componentStyles = Object.assign({}, componentStyles, cssProps);
|
|
373
|
+
}
|
|
374
|
+
// Merge explicit css prop into styles
|
|
126
375
|
if (cssObject !== EMPTY_CSS) {
|
|
127
376
|
componentStyles = Object.assign({}, componentStyles, cssObject);
|
|
128
377
|
}
|
|
129
378
|
return componentStyles;
|
|
130
|
-
}, [variantKey, cssObject, actualBaseStyles, actualVariants, variantProps]);
|
|
379
|
+
}, [variantKey, cssObject, cssProps, actualBaseStyles, actualVariants, variantProps]);
|
|
131
380
|
const finalClassName = useMemo(() => {
|
|
132
381
|
const classNames = [];
|
|
133
382
|
if (baseElementClassName) {
|