chaincss 1.12.8
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/LICENSE +22 -0
- package/README.md +148 -0
- package/browser/index.js +3 -0
- package/browser/react-hooks.js +138 -0
- package/browser/rtt.js +212 -0
- package/node/atomic-optimizer.js +391 -0
- package/node/btt.js +871 -0
- package/node/cache-manager.js +56 -0
- package/node/chaincss.js +484 -0
- package/node/index.js +2 -0
- package/node/loaders/chaincss-loader.js +62 -0
- package/node/plugins/next-plugin.js +29 -0
- package/node/plugins/vite-plugin.js +215 -0
- package/node/plugins/webpack-plugin.js +41 -0
- package/node/prefixer.js +237 -0
- package/node/strVal.js +43 -0
- package/package.json +133 -0
- package/shared/tokens.cjs +256 -0
- package/shared/tokens.mjs +256 -0
- package/types.d.ts +239 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rommel Caneos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
EOF
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# ChainCSS
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://www.npmjs.com/package/@melcanz85/chaincss)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
[](https://chaincss.dev)
|
|
8
|
+
|
|
9
|
+
> Chainable CSS-in-JS with build-time compilation, atomic CSS, and zero-runtime options
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Core Styling Features
|
|
14
|
+
|
|
15
|
+
| Feature | Description |
|
|
16
|
+
|---------|-------------|
|
|
17
|
+
| **Chainable API** | `$().color('red').padding('10px').block()` |
|
|
18
|
+
| **CSS Properties** | All standard CSS properties (camelCase) |
|
|
19
|
+
| **Multiple Selectors** | `.block('.btn', '.button', '[type="button"]')` |
|
|
20
|
+
| **Hover States** | `.hover().backgroundColor('blue').end()` |
|
|
21
|
+
| **Hover Exit** | `.end()` method to exit hover mode |
|
|
22
|
+
| **Nested Selectors** | `.select('.parent .child')` |
|
|
23
|
+
| **Token Resolution** | `$colors.primary` → actual color value |
|
|
24
|
+
| **CSS Property Validation** | Warns on invalid CSS properties |
|
|
25
|
+
|
|
26
|
+
### At-Rules (CSS Rules)
|
|
27
|
+
|
|
28
|
+
| At-Rule | Method | Example |
|
|
29
|
+
|---------|--------|---------|
|
|
30
|
+
| **Media Queries** | `.media()` | `.media('(max-width: 768px)', (css) => {...})` |
|
|
31
|
+
| **Keyframes** | `.keyframes()` | `.keyframes('slide', (kf) => {...})` |
|
|
32
|
+
| **Font Face** | `.fontFace()` | `.fontFace((css) => {...})` |
|
|
33
|
+
| **Supports** | `.supports()` | `.supports('display: grid', (css) => {...})` |
|
|
34
|
+
| **Container Queries** | `.container()` | `.container('(min-width: 400px)', (css) => {...})` |
|
|
35
|
+
| **CSS Layers** | `.layer()` | `.layer('components', (css) => {...})` |
|
|
36
|
+
| **Counter Styles** | `.counterStyle()` | `.counterStyle('circled', (css) => {...})` |
|
|
37
|
+
| **Custom Properties** | `.property()` | `.property('--my-color', (css) => {...})` |
|
|
38
|
+
|
|
39
|
+
### Design System
|
|
40
|
+
|
|
41
|
+
| Feature | Description |
|
|
42
|
+
|---------|-------------|
|
|
43
|
+
| **Design Tokens** | `createTokens()` with colors, spacing, typography |
|
|
44
|
+
| **Token Getter** | `tokens.get('colors.primary')` |
|
|
45
|
+
| **Token Resolution in Styles** | `$colors.primary` syntax |
|
|
46
|
+
| **Responsive Values** | `responsive({ base: '16px', sm: '14px', lg: '18px' })` |
|
|
47
|
+
| **Theme Switching** | Dynamic token updates at runtime |
|
|
48
|
+
| **CSS Variables Output** | `tokens.toCSSVariables()` |
|
|
49
|
+
|
|
50
|
+
### Component System
|
|
51
|
+
|
|
52
|
+
| Feature | Description |
|
|
53
|
+
|---------|-------------|
|
|
54
|
+
| **Framework Agnostic** | React, Next.js, Vite, Webpack |
|
|
55
|
+
| **Recipe System** | `recipe()` for variant-based components |
|
|
56
|
+
| **Base Styles** | `recipe({ base: $().padding('8px').block() })` |
|
|
57
|
+
| **Variants** | `variants: { color: { primary: $().bg('blue').block() } }` |
|
|
58
|
+
| **Default Variants** | `defaultVariants: { color: 'primary' }` |
|
|
59
|
+
| **Compound Variants** | `compoundVariants: [{ variants: { color: 'primary', size: 'lg' }, style: ... }]` |
|
|
60
|
+
| **Variant Combination** | `button({ color: 'primary', size: 'lg' })` |
|
|
61
|
+
| **Compile All Variants** | `button.compileAll()` |
|
|
62
|
+
|
|
63
|
+
### Build-Time Features (CLI)
|
|
64
|
+
|
|
65
|
+
| Feature | Command/Flag |
|
|
66
|
+
|---------|--------------|
|
|
67
|
+
| **CSS Compilation** | `chaincss input.jcss output/dir` |
|
|
68
|
+
| **Watch Mode** | `--watch` flag |
|
|
69
|
+
| **Atomic CSS Optimization** | `--atomic` flag |
|
|
70
|
+
| **Source Maps** | `--source-map` flag |
|
|
71
|
+
| **Minification** | Via CleanCSS (built-in) |
|
|
72
|
+
| **Autoprefixer** | `--prefixer-mode auto` |
|
|
73
|
+
| **Class Map Generation** | `.map.json` output |
|
|
74
|
+
| **JS Module Export** | `.classes.js` output |
|
|
75
|
+
| **TypeScript Definitions** | `.classes.d.ts` output |
|
|
76
|
+
| **Build Manifest** | `chaincss-manifest.json` |
|
|
77
|
+
| **CSS Property Cache** | Local cache for faster builds |
|
|
78
|
+
|
|
79
|
+
### React Runtime Features
|
|
80
|
+
|
|
81
|
+
| Feature | Description |
|
|
82
|
+
|---------|-------------|
|
|
83
|
+
| **useChainStyles** | Main React hook for styles |
|
|
84
|
+
| **useDynamicChainStyles** | For dynamic styles with dependencies |
|
|
85
|
+
| **useThemeChainStyles** | For theme-aware styles |
|
|
86
|
+
| **ChainCSSGlobal** | Global style injection component |
|
|
87
|
+
| **withChainStyles** | HOC for class components |
|
|
88
|
+
| **cx** | Class name utility (like clsx) |
|
|
89
|
+
|
|
90
|
+
### Plugin Ecosystem
|
|
91
|
+
|
|
92
|
+
| Plugin | Description |
|
|
93
|
+
|--------|-------------|
|
|
94
|
+
| **Vite Plugin** | `.jcss` support with HMR |
|
|
95
|
+
| **Webpack Plugin** | Build-time compilation |
|
|
96
|
+
| **Next.js Plugin** | SSR support |
|
|
97
|
+
| **Webpack Loader** | ChainCSS loader for webpack |
|
|
98
|
+
|
|
99
|
+
### Development Tools
|
|
100
|
+
|
|
101
|
+
| Feature | Description |
|
|
102
|
+
|---------|-------------|
|
|
103
|
+
| **TypeScript Support** | Full type definitions (`types.d.ts`) |
|
|
104
|
+
| **Configuration File** | `chaincss.config.cjs` for customization |
|
|
105
|
+
| **Cache Management** | Atomic optimizer cache |
|
|
106
|
+
| **Cache Cleanup** | Auto cleanup of old cache files |
|
|
107
|
+
| **Atomic Optimizer Stats** | `atomicOptimizer.getStats()` |
|
|
108
|
+
| **configureAtomic** | Programmatic atomic config |
|
|
109
|
+
|
|
110
|
+
### CSS Output Features
|
|
111
|
+
|
|
112
|
+
| Feature | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| **Pure CSS Output** | `global.css` generation |
|
|
115
|
+
| **Atomic CSS Output** | Reusable atomic classes |
|
|
116
|
+
| **Source Maps** | `.map` files for debugging |
|
|
117
|
+
| **Class Mapping** | Selector → atomic class map |
|
|
118
|
+
| **Minified Output** | Via CleanCSS |
|
|
119
|
+
| **Vendor Prefixing** | Via Autoprefixer |
|
|
120
|
+
|
|
121
|
+
### Performance Features
|
|
122
|
+
|
|
123
|
+
| Feature | Description |
|
|
124
|
+
|---------|-------------|
|
|
125
|
+
| **Zero Runtime Option** | Build mode = 0KB runtime |
|
|
126
|
+
| **Small Runtime** | Runtime mode = ~3.2KB |
|
|
127
|
+
| **Tree Shaking** | `sideEffects: false` |
|
|
128
|
+
| **Atomic CSS Optimization** | Eliminates duplicate styles |
|
|
129
|
+
| **Cache Strategy** | File + compiled function cache |
|
|
130
|
+
|
|
131
|
+
### Security & Compatibility
|
|
132
|
+
|
|
133
|
+
| Feature | Description |
|
|
134
|
+
|---------|-------------|
|
|
135
|
+
| **No eval()** | Uses `new Function()` with parameters |
|
|
136
|
+
| **No vm2** | Native Node.js module system |
|
|
137
|
+
| **Node.js 14+** | Minimum version requirement |
|
|
138
|
+
| **ESM/CJS Support** | Dual module format |
|
|
139
|
+
| **Browser Support** | Modern browsers via autoprefixer |
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
For complete guide, documentation, examples, and API reference,
|
|
144
|
+
just go to the docs section of [https://chaincss.dev](https://www.chaincss.dev)
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT © [Rommel Caneos](https://github.com/melcanz08)
|
package/browser/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { useMemo, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { $, compile, chain } from './rtt';
|
|
3
|
+
|
|
4
|
+
const styleCache = new Map();
|
|
5
|
+
let styleSheet = null;
|
|
6
|
+
|
|
7
|
+
const initStyleSheet = () => {
|
|
8
|
+
if (typeof document === 'undefined') return null;
|
|
9
|
+
if (!styleSheet) {
|
|
10
|
+
const existing = document.getElementById('chaincss-styles');
|
|
11
|
+
if (existing) {
|
|
12
|
+
styleSheet = existing;
|
|
13
|
+
return styleSheet;
|
|
14
|
+
}
|
|
15
|
+
const style = document.createElement('style');
|
|
16
|
+
style.id = 'chaincss-styles';
|
|
17
|
+
style.setAttribute('data-chaincss', 'true');
|
|
18
|
+
document.head.appendChild(style);
|
|
19
|
+
styleSheet = style;
|
|
20
|
+
}
|
|
21
|
+
return styleSheet;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const updateStyles = (css) => {
|
|
25
|
+
const sheet = initStyleSheet();
|
|
26
|
+
if (sheet) {
|
|
27
|
+
sheet.textContent = css;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function useChainStyles(styles, deps = [], options = {}) {
|
|
32
|
+
const {
|
|
33
|
+
cache = true,
|
|
34
|
+
namespace = 'chain',
|
|
35
|
+
watch = false
|
|
36
|
+
} = options;
|
|
37
|
+
|
|
38
|
+
const id = useRef(`chain-${Math.random().toString(36).substr(2, 9)}`);
|
|
39
|
+
const [classNames, setClassNames] = useState({});
|
|
40
|
+
|
|
41
|
+
const processed = useMemo(() => {
|
|
42
|
+
const resolvedStyles = typeof styles === 'function' ? styles() : styles;
|
|
43
|
+
if (!resolvedStyles || Object.keys(resolvedStyles).length === 0) {
|
|
44
|
+
return { classNames: {}, css: '' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const cacheKey = JSON.stringify(resolvedStyles);
|
|
48
|
+
if (cache && styleCache.has(cacheKey)) {
|
|
49
|
+
return styleCache.get(cacheKey);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const newClassNames = {};
|
|
53
|
+
const compiledStyles = {};
|
|
54
|
+
|
|
55
|
+
Object.entries(resolvedStyles).forEach(([key, styleDef]) => {
|
|
56
|
+
const className = `${namespace}-${key}-${id.current}`;
|
|
57
|
+
const styleObj = typeof styleDef === 'function'
|
|
58
|
+
? styleDef()
|
|
59
|
+
: styleDef;
|
|
60
|
+
newClassNames[key] = className;
|
|
61
|
+
compiledStyles[`${key}_${id.current}`] = {
|
|
62
|
+
selectors: [`.${className}`],
|
|
63
|
+
...styleObj
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
compile(compiledStyles);
|
|
68
|
+
const css = chain.cssOutput;
|
|
69
|
+
const result = { classNames: newClassNames, css };
|
|
70
|
+
|
|
71
|
+
if (cache) {
|
|
72
|
+
styleCache.set(cacheKey, result);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}, [styles, namespace, id.current, ...deps]);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (processed.css) {
|
|
79
|
+
if (!watch) {
|
|
80
|
+
const sheet = initStyleSheet();
|
|
81
|
+
if (sheet) {
|
|
82
|
+
const existingStyles = sheet.textContent || '';
|
|
83
|
+
const styleRegex = new RegExp(`\\.[\\w-]*${id.current}[\\s\\S]*?}`, 'g');
|
|
84
|
+
const cleanedStyles = existingStyles.replace(styleRegex, '');
|
|
85
|
+
sheet.textContent = cleanedStyles + processed.css;
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
updateStyles(processed.css);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return () => {
|
|
93
|
+
if (!watch && styleSheet) {
|
|
94
|
+
const existingStyles = styleSheet.textContent || '';
|
|
95
|
+
const styleRegex = new RegExp(`\\.[\\w-]*${id.current}[\\s\\S]*?}`, 'g');
|
|
96
|
+
styleSheet.textContent = existingStyles.replace(styleRegex, '');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}, [processed.css, watch]);
|
|
100
|
+
|
|
101
|
+
return processed.classNames;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function useDynamicChainStyles(styleFactory, deps = [], options = {}) {
|
|
105
|
+
const styles = useMemo(() => {
|
|
106
|
+
return styleFactory();
|
|
107
|
+
}, deps);
|
|
108
|
+
return useChainStyles(styles, options);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function useThemeChainStyles(styles, theme, options = {}) {
|
|
112
|
+
const themedStyles = useMemo(() => {
|
|
113
|
+
if (typeof styles === 'function') {
|
|
114
|
+
return styles(theme);
|
|
115
|
+
}
|
|
116
|
+
return styles;
|
|
117
|
+
}, [styles, theme]);
|
|
118
|
+
return useChainStyles(themedStyles, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function ChainCSSGlobal({ styles }) {
|
|
122
|
+
useChainStyles(styles, { watch: true });
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function withChainStyles(styles, options = {}) {
|
|
127
|
+
return function WrappedComponent(props) {
|
|
128
|
+
const classNames = useChainStyles(
|
|
129
|
+
typeof styles === 'function' ? styles(props) : styles,
|
|
130
|
+
options
|
|
131
|
+
);
|
|
132
|
+
return React.createElement(WrappedComponent, { ...props, chainStyles: classNames });
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function cx(...classes) {
|
|
137
|
+
return classes.filter(Boolean).join(' ');
|
|
138
|
+
}
|
package/browser/rtt.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { tokens, createTokens, responsive } from '../shared/tokens.mjs';
|
|
2
|
+
|
|
3
|
+
let cssProperties = [];
|
|
4
|
+
|
|
5
|
+
const loadCSSProperties = async () => {
|
|
6
|
+
try {
|
|
7
|
+
const module = await import('../node/css-properties.json', {
|
|
8
|
+
assert: { type: 'json' }
|
|
9
|
+
});
|
|
10
|
+
return module.default;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.log('CSS properties file not found in package, will fetch from CDN');
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const chain = {
|
|
18
|
+
cssOutput: undefined,
|
|
19
|
+
catcher: {},
|
|
20
|
+
cachedValidProperties: [],
|
|
21
|
+
async initializeProperties() {
|
|
22
|
+
if (this.cachedValidProperties.length > 0) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const localProperties = await loadCSSProperties();
|
|
27
|
+
if (localProperties && localProperties.length > 0) {
|
|
28
|
+
this.cachedValidProperties = localProperties;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
console.log('Loading CSS properties from CDN...');
|
|
34
|
+
const response = await fetch('https://raw.githubusercontent.com/mdn/data/main/css/properties.json');
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
const allProperties = Object.keys(data);
|
|
37
|
+
|
|
38
|
+
const baseProperties = new Set();
|
|
39
|
+
allProperties.forEach(prop => {
|
|
40
|
+
const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
|
|
41
|
+
baseProperties.add(baseProp);
|
|
42
|
+
});
|
|
43
|
+
this.cachedValidProperties = Array.from(baseProperties).sort();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Error loading from CDN:', error);
|
|
46
|
+
this.cachedValidProperties = [];
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
getCachedProperties() {
|
|
50
|
+
return this.cachedValidProperties;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
chain.initializeProperties();
|
|
55
|
+
|
|
56
|
+
const resolveToken = (value, useTokens) => {
|
|
57
|
+
if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
const tokenPath = value.slice(1);
|
|
61
|
+
const tokenValue = tokens.get(tokenPath);
|
|
62
|
+
if (!tokenValue) {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
return tokenValue;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function $(useTokens = true) {
|
|
69
|
+
const regularStyles = {};
|
|
70
|
+
const hoverStyles = {};
|
|
71
|
+
let isBuildingHover = false;
|
|
72
|
+
|
|
73
|
+
const validProperties = chain.cachedValidProperties;
|
|
74
|
+
|
|
75
|
+
// Create the main proxy
|
|
76
|
+
const createProxy = () => {
|
|
77
|
+
const handler = {
|
|
78
|
+
get: (target, prop) => {
|
|
79
|
+
// Handle .block()
|
|
80
|
+
if (prop === 'block') {
|
|
81
|
+
return (...args) => {
|
|
82
|
+
const result = { ...regularStyles };
|
|
83
|
+
|
|
84
|
+
// Add hover styles if any exist
|
|
85
|
+
if (Object.keys(hoverStyles).length > 0) {
|
|
86
|
+
result.hover = { ...hoverStyles };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (args.length > 0) {
|
|
90
|
+
result.selectors = args;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Clear for next use
|
|
94
|
+
Object.keys(regularStyles).forEach(key => delete regularStyles[key]);
|
|
95
|
+
Object.keys(hoverStyles).forEach(key => delete hoverStyles[key]);
|
|
96
|
+
isBuildingHover = false;
|
|
97
|
+
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Handle .hover()
|
|
103
|
+
if (prop === 'hover') {
|
|
104
|
+
return () => {
|
|
105
|
+
isBuildingHover = true;
|
|
106
|
+
return proxy; // Return the same proxy, just with hover mode on
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if(prop==='end'){
|
|
111
|
+
return () => {
|
|
112
|
+
isBuildingHover = false;
|
|
113
|
+
return proxy; // Return the same proxy, but end of hover mode
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle regular CSS properties
|
|
118
|
+
const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
119
|
+
if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
|
|
120
|
+
console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (value) => {
|
|
124
|
+
if (isBuildingHover) {
|
|
125
|
+
hoverStyles[prop] = resolveToken(value, useTokens);
|
|
126
|
+
} else {
|
|
127
|
+
regularStyles[prop] = resolveToken(value, useTokens);
|
|
128
|
+
}
|
|
129
|
+
return proxy;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return new Proxy({}, handler);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const proxy = createProxy();
|
|
138
|
+
return proxy;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const run = (...args) => {
|
|
142
|
+
let cssOutput = '';
|
|
143
|
+
args.forEach((value) => {
|
|
144
|
+
if (value && value.selectors) {
|
|
145
|
+
let rule = `${value.selectors.join(', ')} {\n`;
|
|
146
|
+
for (let key in value) {
|
|
147
|
+
if (key !== 'selectors' && value.hasOwnProperty(key)) {
|
|
148
|
+
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
149
|
+
rule += ` ${kebabKey}: ${value[key]};\n`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
rule += `}\n\n`;
|
|
153
|
+
cssOutput += rule;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
chain.cssOutput = cssOutput.trim();
|
|
157
|
+
return cssOutput.trim();
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const compile = (obj) => {
|
|
161
|
+
let cssString = '';
|
|
162
|
+
for (const key in obj) {
|
|
163
|
+
if (obj.hasOwnProperty(key)) {
|
|
164
|
+
const element = obj[key];
|
|
165
|
+
let selectors = element.selectors || [];
|
|
166
|
+
|
|
167
|
+
// Generate normal styles
|
|
168
|
+
let normalCSS = '';
|
|
169
|
+
let hoverCSS = '';
|
|
170
|
+
|
|
171
|
+
for (let prop in element) {
|
|
172
|
+
if (element.hasOwnProperty(prop) && prop !== 'selectors' && prop !== 'hover') {
|
|
173
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
174
|
+
normalCSS += ` ${kebabKey}: ${element[prop]};\n`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Generate hover styles if present
|
|
179
|
+
if (element.hover && typeof element.hover === 'object') {
|
|
180
|
+
for (let prop in element.hover) {
|
|
181
|
+
if (element.hover.hasOwnProperty(prop)) {
|
|
182
|
+
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
183
|
+
hoverCSS += ` ${kebabKey}: ${element.hover[prop]};\n`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Add normal styles
|
|
189
|
+
if (normalCSS) {
|
|
190
|
+
cssString += `${selectors.join(', ')} {\n${normalCSS}}\n`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Add hover styles as separate rule
|
|
194
|
+
if (hoverCSS) {
|
|
195
|
+
const hoverSelectors = selectors.map(s => `${s}:hover`);
|
|
196
|
+
cssString += `${hoverSelectors.join(', ')} {\n${hoverCSS}}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
chain.cssOutput = cssString.trim();
|
|
201
|
+
return cssString.trim();
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export {
|
|
205
|
+
chain,
|
|
206
|
+
$,
|
|
207
|
+
run,
|
|
208
|
+
compile,
|
|
209
|
+
tokens,
|
|
210
|
+
createTokens,
|
|
211
|
+
responsive
|
|
212
|
+
};
|