@melcanz85/chaincss 1.11.0 → 1.11.2
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 +114 -45
- package/browser/index.js +1 -1
- package/browser/react-hooks.jsx +1 -63
- package/browser/rtt.js +47 -68
- package/node/atomic-optimizer.js +2 -67
- package/node/btt.js +348 -84
- package/node/cache-manager.js +0 -12
- package/node/chaincss.js +14 -99
- package/node/index.js +2 -13
- package/node/prefixer.js +2 -61
- package/node/strVal.js +1 -1
- package/package.json +1 -1
- package/types.d.ts +0 -26
package/README.md
CHANGED
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://melcanz08.github.io/chaincss_react_website/)
|
|
8
8
|
|
|
9
|
-
**Write CSS with JavaScript.
|
|
10
|
-
|
|
11
|
-
ChainCSS is a revolutionary CSS-in-JS solution that gives you **two powerful modes** in one package:
|
|
9
|
+
**Write CSS with JavaScript. Lets you CHOOSE your runtime cost. DUAL MODE**
|
|
12
10
|
|
|
13
11
|
**Build-time compilation** → Pure CSS, zero JavaScript in browser
|
|
14
12
|
|
|
@@ -16,11 +14,56 @@ ChainCSS is a revolutionary CSS-in-JS solution that gives you **two powerful mod
|
|
|
16
14
|
|
|
17
15
|
## Installation
|
|
18
16
|
|
|
17
|
+
* Install [nodejs.](https://nodejs.org/en/download)
|
|
18
|
+
|
|
19
19
|
```bash
|
|
20
20
|
|
|
21
21
|
npm install @melcanz85/chaincss
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
* In your html add a link tag with an href value of style/global.css this serves as the
|
|
25
|
+
stylesheet for your entire webpage. You dont need to touch this css file.
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<!-- index.html -->
|
|
29
|
+
<!DOCTYPE html>
|
|
30
|
+
<html>
|
|
31
|
+
<head>
|
|
32
|
+
<title>chaincss</title>
|
|
33
|
+
<link rel="stylesheet" type="text/css" href="style/global.css">
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<p>Hello World</p>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Syntax
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// main.jcss
|
|
45
|
+
<@
|
|
46
|
+
const text = $().color('blue').textAlign('center').block('p');
|
|
47
|
+
|
|
48
|
+
//text.fontSize = '2rem';
|
|
49
|
+
|
|
50
|
+
run(text);
|
|
51
|
+
@>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
* To apply this styles run this in your terminal / command prompt.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx chaincss ./src/main.jcss ./style --watch
|
|
58
|
+
````
|
|
59
|
+
|
|
60
|
+
* Open your index.html in the browser.
|
|
61
|
+
|
|
62
|
+
* To make changes uncomment styles between text variable declaration and run() method.
|
|
63
|
+
|
|
64
|
+
* Thats how you add or modify the style block you treat them as a regular javascript object.
|
|
65
|
+
|
|
66
|
+
|
|
24
67
|
### File Structure
|
|
25
68
|
|
|
26
69
|
```text
|
|
@@ -54,6 +97,7 @@ ChainCSS is a revolutionary CSS-in-JS solution that gives you **two powerful mod
|
|
|
54
97
|
**in your main.jcss**
|
|
55
98
|
|
|
56
99
|
```javascript
|
|
100
|
+
// src/main.jcss
|
|
57
101
|
<@
|
|
58
102
|
const button = get('./button.jcss');
|
|
59
103
|
|
|
@@ -66,45 +110,68 @@ ChainCSS is a revolutionary CSS-in-JS solution that gives you **two powerful mod
|
|
|
66
110
|
npx chaincss ./src/main.jcss ./style --watch
|
|
67
111
|
# ./style/global.css generated!
|
|
68
112
|
````
|
|
69
|
-
OR with vanilla nodejs project
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
npx chaincss ./src/main.jcss ./style --watch & node server.js
|
|
73
|
-
# ./style/global.css generated!
|
|
74
|
-
````
|
|
75
113
|
* Note: running `npx chaincss ./src/main.jcss ./style --watch ` for the first time will
|
|
76
114
|
generate ./chaincss.config.js with default values. You can edit this to
|
|
77
115
|
customize your build!.
|
|
78
116
|
|
|
79
|
-
|
|
80
117
|
### Mode 2: Runtime (React Hooks)
|
|
81
118
|
|
|
82
119
|
**Perfect for:** Dynamic styles that respond to props, state, or themes.
|
|
83
120
|
|
|
84
121
|
```jsx
|
|
85
|
-
// components/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
// src/components/Counter.jsx
|
|
123
|
+
import { useState } from 'react';
|
|
124
|
+
import { useChainStyles, $ } from '@melcanz85/chaincss/react';
|
|
125
|
+
|
|
126
|
+
export default function Counter() {
|
|
127
|
+
const [count, setCount] = useState(0);
|
|
128
|
+
|
|
129
|
+
const styles = useChainStyles(() => {
|
|
130
|
+
const container = $()
|
|
131
|
+
.display('flex')
|
|
132
|
+
.flexDirection('column')
|
|
133
|
+
.alignItems('center')
|
|
134
|
+
.justifyContent('center')
|
|
135
|
+
.gap('3rem')
|
|
136
|
+
.padding('4rem 2.5rem')
|
|
137
|
+
.backgroundColor('rgba(255, 255, 255, 0.92)')
|
|
138
|
+
.borderRadius('2rem')
|
|
139
|
+
.boxShadow('0 25px 50px -12px rgba(0, 0, 0, 0.25)')
|
|
140
|
+
.backdropFilter('blur(12px)')
|
|
141
|
+
.maxWidth('420px')
|
|
142
|
+
.width('90%')
|
|
143
|
+
.block();
|
|
144
|
+
|
|
145
|
+
const numberBase = $()
|
|
146
|
+
.fontSize('6rem')
|
|
147
|
+
.fontWeight('800')
|
|
148
|
+
.letterSpacing('-0.05em')
|
|
149
|
+
.transition('color 0.5s ease, transform 0.3s ease')
|
|
150
|
+
.transform(count === 0 ? 'scale(1)' : 'scale(1.1)')
|
|
151
|
+
.animation(count > 0 ? 'pulse 1.8s infinite ease-in-out' : 'none')
|
|
152
|
+
.block();
|
|
153
|
+
|
|
154
|
+
const numberColor = $()
|
|
155
|
+
.color(
|
|
156
|
+
count === 0 ? '#4a5568' : `hsl(${ (count * 40) % 360 }, 80%, 60%)`
|
|
157
|
+
)
|
|
158
|
+
.block();
|
|
159
|
+
|
|
160
|
+
return { container, numberBase, numberColor };
|
|
161
|
+
}, [count]);
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div className={styles.container}>
|
|
165
|
+
<div className={`${styles.numberBase} ${styles.numberColor}`}>
|
|
166
|
+
{count}
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<button className="increment-btn" onClick={() => setCount(prev => prev + 1)}>
|
|
170
|
+
Tap to Count Up
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
108
175
|
```
|
|
109
176
|
|
|
110
177
|
## Use BOTH in the Same Project!
|
|
@@ -119,8 +186,8 @@ OR with vanilla nodejs project
|
|
|
119
186
|
|
|
120
187
|
// components/Card.jsx (runtime)
|
|
121
188
|
function Card({ isHighlighted }) {
|
|
122
|
-
const styles = useChainStyles({
|
|
123
|
-
card
|
|
189
|
+
const styles = useChainStyles(() => {
|
|
190
|
+
const card = $()
|
|
124
191
|
.backgroundColor(isHighlighted ? '#fffacd' : 'white')
|
|
125
192
|
.padding('1rem')
|
|
126
193
|
.block()
|
|
@@ -230,9 +297,9 @@ OR with vanilla nodejs project
|
|
|
230
297
|
// chaincss.config.js
|
|
231
298
|
module.exports = {
|
|
232
299
|
atomic: {
|
|
233
|
-
enabled: true,
|
|
234
|
-
threshold: 3,
|
|
235
|
-
naming: 'hash'
|
|
300
|
+
enabled: true,
|
|
301
|
+
threshold: 3,
|
|
302
|
+
naming: 'hash'
|
|
236
303
|
}
|
|
237
304
|
};
|
|
238
305
|
```
|
|
@@ -279,8 +346,8 @@ compile({ hello });" > chaincss/main.jcss
|
|
|
279
346
|
import { useChainStyles } from '@melcanz85/chaincss';
|
|
280
347
|
|
|
281
348
|
export function Button({ variant = 'primary', children }) {
|
|
282
|
-
const styles = useChainStyles({
|
|
283
|
-
button
|
|
349
|
+
const styles = useChainStyles(() => {
|
|
350
|
+
const button = $()
|
|
284
351
|
.backgroundColor(variant === 'primary' ? '#667eea' : '#48bb78')
|
|
285
352
|
.color('white')
|
|
286
353
|
.padding('0.5rem 1rem')
|
|
@@ -289,13 +356,15 @@ compile({ hello });" > chaincss/main.jcss
|
|
|
289
356
|
.transform('translateY(-2px)')
|
|
290
357
|
.boxShadow('0 4px 6px rgba(0,0,0,0.1)')
|
|
291
358
|
.block()
|
|
359
|
+
return { button };
|
|
292
360
|
});
|
|
293
|
-
|
|
294
361
|
return <button className={styles.button}>{children}</button>;
|
|
295
362
|
}
|
|
296
363
|
```
|
|
297
364
|
|
|
298
|
-
|
|
365
|
+
|
|
366
|
+
See ChainCSS in action! Visit our interactive demo site - [https://melcanz08.github.io/chaincss_react_website/](https://melcanz08.github.io/chaincss_react_website/)
|
|
367
|
+
|
|
299
368
|
|
|
300
369
|
## Performance Comparison
|
|
301
370
|
|
|
@@ -313,7 +382,7 @@ See ChainCSS in action! Visit our interactive demo site - [https://melcanz08.git
|
|
|
313
382
|
|
|
314
383
|
CSS Modules Zero Just CSS None Low
|
|
315
384
|
|
|
316
|
-
**ChainCSS
|
|
385
|
+
**ChainCSS a "DUAL MODE optioned" css-in-js library**
|
|
317
386
|
|
|
318
387
|
## API Reference
|
|
319
388
|
|
|
@@ -390,9 +459,9 @@ Please see CONTRIBUTING.md for guidelines.
|
|
|
390
459
|
|
|
391
460
|
## License
|
|
392
461
|
|
|
393
|
-
MIT © [Rommel Caneos](
|
|
462
|
+
MIT © [Rommel Caneos](https://github.com/melcanz08)
|
|
394
463
|
|
|
395
464
|
|
|
396
465
|
## Star Us on GitHub!
|
|
397
466
|
|
|
398
|
-
If ChainCSS helps you, please [give it a star!](
|
|
467
|
+
If ChainCSS helps you, please [give it a star!](https://github.com/melcanz08/chaincss) It helps others discover it.
|
package/browser/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './react-hooks.jsx';
|
|
2
|
-
export {
|
|
2
|
+
export {$,run,compile,chain,createTokens,responsive,tokens } from './rtt.js';
|
package/browser/react-hooks.jsx
CHANGED
|
@@ -1,129 +1,81 @@
|
|
|
1
1
|
import { useMemo, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { $, compile, chain } from './rtt';
|
|
3
|
-
|
|
4
|
-
// Cache for generated styles to avoid duplication
|
|
5
3
|
const styleCache = new Map();
|
|
6
4
|
let styleSheet = null;
|
|
7
|
-
|
|
8
|
-
// Initialize style sheet (add to document head)
|
|
9
5
|
const initStyleSheet = () => {
|
|
10
|
-
if (typeof document === 'undefined') return null;
|
|
11
|
-
|
|
6
|
+
if (typeof document === 'undefined') return null;
|
|
12
7
|
if (!styleSheet) {
|
|
13
|
-
// Check if already exists
|
|
14
8
|
const existing = document.getElementById('chaincss-styles');
|
|
15
9
|
if (existing) {
|
|
16
10
|
styleSheet = existing;
|
|
17
11
|
return styleSheet;
|
|
18
12
|
}
|
|
19
|
-
|
|
20
|
-
// Create new style element
|
|
21
13
|
const style = document.createElement('style');
|
|
22
14
|
style.id = 'chaincss-styles';
|
|
23
15
|
style.setAttribute('data-chaincss', 'true');
|
|
24
16
|
document.head.appendChild(style);
|
|
25
17
|
styleSheet = style;
|
|
26
18
|
}
|
|
27
|
-
|
|
28
19
|
return styleSheet;
|
|
29
20
|
};
|
|
30
|
-
|
|
31
|
-
// Update styles in the style sheet
|
|
32
21
|
const updateStyles = (css) => {
|
|
33
22
|
const sheet = initStyleSheet();
|
|
34
23
|
if (sheet) {
|
|
35
24
|
sheet.textContent = css;
|
|
36
25
|
}
|
|
37
26
|
};
|
|
38
|
-
|
|
39
|
-
// Main hook for using ChainCSS styles in React
|
|
40
27
|
export function useChainStyles(styles, deps = [], options = {}) {
|
|
41
28
|
const {
|
|
42
29
|
cache = true,
|
|
43
30
|
namespace = 'chain',
|
|
44
31
|
watch = false
|
|
45
32
|
} = options;
|
|
46
|
-
|
|
47
|
-
// Generate a unique ID for this component instance
|
|
48
33
|
const id = useRef(`chain-${Math.random().toString(36).substr(2, 9)}`);
|
|
49
|
-
|
|
50
|
-
// Store the generated class names
|
|
51
34
|
const [classNames, setClassNames] = useState({});
|
|
52
|
-
|
|
53
|
-
// Process styles and generate CSS
|
|
54
35
|
const processed = useMemo(() => {
|
|
55
|
-
// ✅ FIRST: Resolve styles if it's a function
|
|
56
36
|
const resolvedStyles = typeof styles === 'function' ? styles() : styles;
|
|
57
|
-
|
|
58
37
|
if (!resolvedStyles || Object.keys(resolvedStyles).length === 0) {
|
|
59
38
|
return { classNames: {}, css: '' };
|
|
60
39
|
}
|
|
61
|
-
|
|
62
|
-
// ✅ NOW use resolvedStyles for cache key
|
|
63
40
|
const cacheKey = JSON.stringify(resolvedStyles);
|
|
64
41
|
if (cache && styleCache.has(cacheKey)) {
|
|
65
42
|
return styleCache.get(cacheKey);
|
|
66
43
|
}
|
|
67
|
-
|
|
68
|
-
// Generate unique class names for each style
|
|
69
44
|
const newClassNames = {};
|
|
70
45
|
const compiledStyles = {};
|
|
71
|
-
|
|
72
|
-
// ✅ Use resolvedStyles here
|
|
73
46
|
Object.entries(resolvedStyles).forEach(([key, styleDef]) => {
|
|
74
|
-
// Generate a unique class name
|
|
75
47
|
const className = `${namespace}-${key}-${id.current}`;
|
|
76
|
-
|
|
77
|
-
// Create a style definition with the unique class
|
|
78
48
|
const styleObj = typeof styleDef === 'function'
|
|
79
49
|
? styleDef()
|
|
80
50
|
: styleDef;
|
|
81
|
-
|
|
82
|
-
// Store the class name mapping
|
|
83
51
|
newClassNames[key] = className;
|
|
84
|
-
|
|
85
|
-
// Create the style rule
|
|
86
52
|
compiledStyles[`${key}_${id.current}`] = {
|
|
87
53
|
selectors: [`.${className}`],
|
|
88
54
|
...styleObj
|
|
89
55
|
};
|
|
90
56
|
});
|
|
91
|
-
|
|
92
|
-
// Compile to CSS
|
|
93
57
|
compile(compiledStyles);
|
|
94
58
|
const css = chain.cssOutput;
|
|
95
|
-
|
|
96
59
|
const result = { classNames: newClassNames, css };
|
|
97
|
-
|
|
98
60
|
if (cache) {
|
|
99
61
|
styleCache.set(cacheKey, result);
|
|
100
62
|
}
|
|
101
|
-
|
|
102
63
|
return result;
|
|
103
64
|
}, [styles, namespace, id.current, ...deps]);
|
|
104
|
-
|
|
105
|
-
// Update the style sheet when styles change
|
|
106
65
|
useEffect(() => {
|
|
107
66
|
if (processed.css) {
|
|
108
|
-
// For simple apps, just append
|
|
109
67
|
if (!watch) {
|
|
110
68
|
const sheet = initStyleSheet();
|
|
111
69
|
if (sheet) {
|
|
112
|
-
// Remove old styles for this component
|
|
113
70
|
const existingStyles = sheet.textContent || '';
|
|
114
71
|
const styleRegex = new RegExp(`\\.[\\w-]*${id.current}[\\s\\S]*?}`, 'g');
|
|
115
72
|
const cleanedStyles = existingStyles.replace(styleRegex, '');
|
|
116
|
-
|
|
117
|
-
// Add new styles
|
|
118
73
|
sheet.textContent = cleanedStyles + processed.css;
|
|
119
74
|
}
|
|
120
75
|
} else {
|
|
121
|
-
// For watch mode, update everything
|
|
122
76
|
updateStyles(processed.css);
|
|
123
77
|
}
|
|
124
78
|
}
|
|
125
|
-
|
|
126
|
-
// Cleanup on unmount
|
|
127
79
|
return () => {
|
|
128
80
|
if (!watch && styleSheet) {
|
|
129
81
|
const existingStyles = styleSheet.textContent || '';
|
|
@@ -132,20 +84,14 @@ export function useChainStyles(styles, deps = [], options = {}) {
|
|
|
132
84
|
}
|
|
133
85
|
};
|
|
134
86
|
}, [processed.css, watch]);
|
|
135
|
-
|
|
136
87
|
return processed.classNames;
|
|
137
88
|
}
|
|
138
|
-
|
|
139
|
-
// Hook for dynamic styles that depend on props/state
|
|
140
89
|
export function useDynamicChainStyles(styleFactory, deps = [], options = {}) {
|
|
141
90
|
const styles = useMemo(() => {
|
|
142
91
|
return styleFactory();
|
|
143
92
|
}, deps);
|
|
144
|
-
|
|
145
93
|
return useChainStyles(styles, options);
|
|
146
94
|
}
|
|
147
|
-
|
|
148
|
-
// Hook for theme-aware styles
|
|
149
95
|
export function useThemeChainStyles(styles, theme, options = {}) {
|
|
150
96
|
const themedStyles = useMemo(() => {
|
|
151
97
|
if (typeof styles === 'function') {
|
|
@@ -153,29 +99,21 @@ export function useThemeChainStyles(styles, theme, options = {}) {
|
|
|
153
99
|
}
|
|
154
100
|
return styles;
|
|
155
101
|
}, [styles, theme]);
|
|
156
|
-
|
|
157
102
|
return useChainStyles(themedStyles, options);
|
|
158
103
|
}
|
|
159
|
-
|
|
160
|
-
// Component for injecting global ChainCSS styles
|
|
161
104
|
export function ChainCSSGlobal({ styles }) {
|
|
162
105
|
useChainStyles(styles, { watch: true });
|
|
163
106
|
return null;
|
|
164
107
|
}
|
|
165
|
-
|
|
166
|
-
// HOC for adding ChainCSS styles to components
|
|
167
108
|
export function withChainStyles(styles, options = {}) {
|
|
168
109
|
return function WrappedComponent(props) {
|
|
169
110
|
const classNames = useChainStyles(
|
|
170
111
|
typeof styles === 'function' ? styles(props) : styles,
|
|
171
112
|
options
|
|
172
113
|
);
|
|
173
|
-
|
|
174
114
|
return <WrappedComponent {...props} chainStyles={classNames} />;
|
|
175
115
|
};
|
|
176
116
|
}
|
|
177
|
-
|
|
178
|
-
// Utility to combine multiple class names
|
|
179
117
|
export function cx(...classes) {
|
|
180
118
|
return classes.filter(Boolean).join(' ');
|
|
181
119
|
}
|
package/browser/rtt.js
CHANGED
|
@@ -2,171 +2,150 @@ import { tokens, createTokens, responsive } from '../shared/tokens.mjs';
|
|
|
2
2
|
|
|
3
3
|
let cssProperties = [];
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
try
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} catch (e) {
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
// Instead of using dynamic import with unsupported options
|
|
6
|
+
// Use a try-catch with proper dynamic import syntax
|
|
7
|
+
const loadCSSProperties = async () => {
|
|
8
|
+
try {
|
|
9
|
+
// Remove the unsupported 'ignore' option
|
|
10
|
+
const module = await import('../node/css-properties.json', {
|
|
11
|
+
assert: { type: 'json' }
|
|
12
|
+
});
|
|
13
|
+
return module.default;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
// If the file doesn't exist in the package, fallback to CDN
|
|
16
|
+
console.log('CSS properties file not found in package, will fetch from CDN');
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
17
20
|
|
|
21
|
+
// Initialize asynchronously
|
|
18
22
|
const chain = {
|
|
19
23
|
cssOutput: undefined,
|
|
20
24
|
catcher: {},
|
|
21
25
|
cachedValidProperties: [],
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
async initializeProperties() {
|
|
27
|
+
if (this.cachedValidProperties.length > 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Try to load from local file first
|
|
32
|
+
const localProperties = await loadCSSProperties();
|
|
33
|
+
if (localProperties && localProperties.length > 0) {
|
|
34
|
+
this.cachedValidProperties = localProperties;
|
|
26
35
|
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fallback to CDN
|
|
39
|
+
try {
|
|
40
|
+
console.log('Loading CSS properties from CDN...');
|
|
41
|
+
const response = await fetch('https://raw.githubusercontent.com/mdn/data/main/css/properties.json');
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
const allProperties = Object.keys(data);
|
|
44
|
+
|
|
45
|
+
// Strip vendor prefixes and remove duplicates
|
|
46
|
+
const baseProperties = new Set();
|
|
47
|
+
allProperties.forEach(prop => {
|
|
48
|
+
const baseProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
|
|
49
|
+
baseProperties.add(baseProp);
|
|
50
|
+
});
|
|
51
|
+
this.cachedValidProperties = Array.from(baseProperties).sort();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error loading from CDN:', error);
|
|
54
|
+
this.cachedValidProperties = [];
|
|
46
55
|
}
|
|
47
56
|
},
|
|
48
|
-
|
|
49
|
-
// Synchronous version for internal use
|
|
50
57
|
getCachedProperties() {
|
|
51
58
|
return this.cachedValidProperties;
|
|
52
59
|
}
|
|
53
60
|
};
|
|
54
61
|
|
|
55
|
-
//
|
|
62
|
+
// Start initialization but don't await (non-blocking)
|
|
56
63
|
chain.initializeProperties();
|
|
57
64
|
|
|
65
|
+
// Rest of your code remains the same...
|
|
58
66
|
const resolveToken = (value, useTokens) => {
|
|
59
67
|
if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
|
|
60
68
|
return value;
|
|
61
69
|
}
|
|
62
|
-
|
|
63
70
|
const tokenPath = value.slice(1);
|
|
64
71
|
const tokenValue = tokens.get(tokenPath);
|
|
65
|
-
|
|
66
72
|
if (!tokenValue) {
|
|
67
73
|
return value;
|
|
68
74
|
}
|
|
69
|
-
|
|
70
75
|
return tokenValue;
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
function $(useTokens = true){
|
|
74
79
|
const catcher = {};
|
|
75
|
-
|
|
76
|
-
// Use cached properties if available
|
|
77
80
|
const validProperties = chain.cachedValidProperties;
|
|
78
|
-
|
|
79
81
|
const handler = {
|
|
80
82
|
get: (target, prop) => {
|
|
81
83
|
if (prop === 'block') {
|
|
82
84
|
return function(...args) {
|
|
83
|
-
// If no args, just return current catcher
|
|
84
85
|
if (args.length === 0) {
|
|
85
86
|
const result = { ...catcher };
|
|
86
|
-
// Clear catcher
|
|
87
87
|
Object.keys(catcher).forEach(key => delete catcher[key]);
|
|
88
88
|
return result;
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
// Create result with selectors
|
|
92
90
|
const result = {
|
|
93
91
|
selectors: args,
|
|
94
92
|
...catcher
|
|
95
93
|
};
|
|
96
|
-
|
|
97
|
-
// Clear catcher
|
|
98
94
|
Object.keys(catcher).forEach(key => delete catcher[key]);
|
|
99
|
-
|
|
100
95
|
return result;
|
|
101
96
|
};
|
|
102
97
|
}
|
|
103
|
-
// Convert camelCase to kebab-case for CSS property
|
|
104
98
|
const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
105
|
-
// Validate property exists (optional) - use cached properties
|
|
106
99
|
if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
|
|
107
100
|
console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
|
|
108
101
|
}
|
|
109
|
-
// Return a function that sets the value
|
|
110
102
|
return function(value) {
|
|
111
103
|
catcher[prop] = resolveToken(value, useTokens);
|
|
112
104
|
return proxy;
|
|
113
105
|
};
|
|
114
106
|
}
|
|
115
107
|
};
|
|
116
|
-
|
|
117
|
-
// Create the proxy
|
|
118
108
|
const proxy = new Proxy({}, handler);
|
|
119
|
-
|
|
120
109
|
return proxy;
|
|
121
110
|
}
|
|
122
111
|
|
|
123
112
|
const run = (...args) => {
|
|
124
113
|
let cssOutput = '';
|
|
125
|
-
|
|
126
114
|
args.forEach((value) => {
|
|
127
115
|
if (value && value.selectors) {
|
|
128
116
|
let rule = `${value.selectors.join(', ')} {\n`;
|
|
129
|
-
|
|
130
|
-
// Add all properties (excluding 'selectors')
|
|
131
117
|
for (let key in value) {
|
|
132
118
|
if (key !== 'selectors' && value.hasOwnProperty(key)) {
|
|
133
119
|
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
134
120
|
rule += ` ${kebabKey}: ${value[key]};\n`;
|
|
135
121
|
}
|
|
136
122
|
}
|
|
137
|
-
|
|
138
123
|
rule += `}\n\n`;
|
|
139
124
|
cssOutput += rule;
|
|
140
125
|
}
|
|
141
126
|
});
|
|
142
|
-
|
|
143
127
|
chain.cssOutput = cssOutput.trim();
|
|
144
128
|
return cssOutput.trim();
|
|
145
129
|
};
|
|
146
130
|
|
|
147
131
|
const compile = (obj) => {
|
|
148
132
|
let cssString = '';
|
|
149
|
-
|
|
150
133
|
for (const key in obj) {
|
|
151
134
|
if (obj.hasOwnProperty(key)) {
|
|
152
135
|
const element = obj[key];
|
|
153
136
|
let selectors = element.selectors || [];
|
|
154
137
|
let elementCSS = '';
|
|
155
|
-
|
|
156
138
|
for (let prop in element) {
|
|
157
139
|
if (element.hasOwnProperty(prop) && prop !== 'selectors') {
|
|
158
|
-
// Convert camelCase to kebab-case
|
|
159
140
|
const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
160
141
|
elementCSS += ` ${kebabKey}: ${element[prop]};\n`;
|
|
161
142
|
}
|
|
162
143
|
}
|
|
163
|
-
|
|
164
144
|
cssString += `${selectors.join(', ')} {\n${elementCSS}}\n`;
|
|
165
145
|
}
|
|
166
146
|
}
|
|
167
|
-
|
|
168
147
|
chain.cssOutput = cssString.trim();
|
|
169
|
-
return cssString.trim();
|
|
148
|
+
return cssString.trim();
|
|
170
149
|
};
|
|
171
150
|
|
|
172
151
|
export {
|