@melcanz85/chaincss 1.11.0 → 1.11.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.
package/README.md CHANGED
@@ -6,9 +6,7 @@
6
6
 
7
7
  [![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://melcanz08.github.io/chaincss_react_website/)
8
8
 
9
- **Write CSS with JavaScript. The only CSS-in-JS library that lets you CHOOSE your runtime cost.**
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/DynamicButton.jsx
86
- import { useChainStyles } from '@melcanz85/chaincss';
87
-
88
- function DynamicButton({ variant = 'primary', children }) {
89
- const styles = useChainStyles({
90
- button: () => $()
91
- .backgroundColor(variant === 'primary' ? '#667eea' : '#48bb78')
92
- .color('white')
93
- .padding('0.5rem 1rem')
94
- .borderRadius('4px')
95
- .hover()
96
- .transform('translateY(-2px)')
97
- .boxShadow('0 4px 6px rgba(0,0,0,0.1)')
98
- .block()
99
- });
100
-
101
- return <button className={styles.button}>{children}</button>;
102
- }
103
- // Styles injected at runtime
104
- // Automatic cleanup on unmount
105
- // Fully dynamic based on props
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, // Enable 90% CSS size reduction
234
- threshold: 3, // Styles used 3+ times become atomic
235
- naming: 'hash' // Smallest class names
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
- See ChainCSS in action! Visit our interactive demo site - [https://melcanz08.github.io/chaincss_react_website/]](https://melcanz08.github.io/chaincss_react_website/)
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 is the ONLY library that gives you DUAL options**
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]('https://github.com/melcanz08')
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!]('https://github.com/melcanz08/chaincss') It helps others discover it.
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 { $, run, compile, chain, createTokens, responsive, tokens } from './rtt.js';
2
+ export {$,run,compile,chain,createTokens,responsive,tokens } from './rtt.js';
@@ -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; // SSR safety
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
@@ -1,25 +1,18 @@
1
1
  import { tokens, createTokens, responsive } from '../shared/tokens.mjs';
2
-
3
2
  let cssProperties = [];
4
-
5
- // Try to import the JSON file, fallback to fetch if it fails
6
3
  try {
7
- // Dynamic import - will fail gracefully if file doesn't exist
8
4
  const module = await import('../node/css-properties.json', {
9
5
  assert: { type: 'json' },
10
- // This prevents the error from breaking the build
11
6
  ignore: true
12
7
  });
13
8
  cssProperties = module.default;
14
9
  } catch (e) {
15
10
  console.log('CSS properties file not found, will fetch from CDN');
16
11
  }
17
-
18
12
  const chain = {
19
13
  cssOutput: undefined,
20
14
  catcher: {},
21
15
  cachedValidProperties: [],
22
-
23
16
  async initializeProperties() {
24
17
  if (cssProperties && cssProperties.length > 0) {
25
18
  this.cachedValidProperties = cssProperties;
@@ -45,128 +38,91 @@ const chain = {
45
38
  }
46
39
  }
47
40
  },
48
-
49
- // Synchronous version for internal use
50
41
  getCachedProperties() {
51
42
  return this.cachedValidProperties;
52
43
  }
53
44
  };
54
-
55
- // Initialize properties synchronously when module loads
56
45
  chain.initializeProperties();
57
-
58
46
  const resolveToken = (value, useTokens) => {
59
47
  if (!useTokens || typeof value !== 'string' || !value.startsWith('$')) {
60
48
  return value;
61
49
  }
62
-
63
50
  const tokenPath = value.slice(1);
64
51
  const tokenValue = tokens.get(tokenPath);
65
-
66
52
  if (!tokenValue) {
67
53
  return value;
68
54
  }
69
-
70
55
  return tokenValue;
71
56
  };
72
-
73
57
  function $(useTokens = true){
74
58
  const catcher = {};
75
-
76
- // Use cached properties if available
77
59
  const validProperties = chain.cachedValidProperties;
78
-
79
60
  const handler = {
80
61
  get: (target, prop) => {
81
62
  if (prop === 'block') {
82
63
  return function(...args) {
83
- // If no args, just return current catcher
84
64
  if (args.length === 0) {
85
65
  const result = { ...catcher };
86
- // Clear catcher
87
66
  Object.keys(catcher).forEach(key => delete catcher[key]);
88
67
  return result;
89
68
  }
90
-
91
- // Create result with selectors
92
69
  const result = {
93
70
  selectors: args,
94
71
  ...catcher
95
72
  };
96
-
97
- // Clear catcher
98
73
  Object.keys(catcher).forEach(key => delete catcher[key]);
99
-
100
74
  return result;
101
75
  };
102
76
  }
103
- // Convert camelCase to kebab-case for CSS property
104
77
  const cssProperty = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
105
- // Validate property exists (optional) - use cached properties
106
78
  if (validProperties && validProperties.length > 0 && !validProperties.includes(cssProperty)) {
107
79
  console.warn(`Warning: '${cssProperty}' may not be a valid CSS property`);
108
80
  }
109
- // Return a function that sets the value
110
81
  return function(value) {
111
82
  catcher[prop] = resolveToken(value, useTokens);
112
83
  return proxy;
113
84
  };
114
85
  }
115
86
  };
116
-
117
- // Create the proxy
118
87
  const proxy = new Proxy({}, handler);
119
-
120
88
  return proxy;
121
89
  }
122
-
123
90
  const run = (...args) => {
124
91
  let cssOutput = '';
125
-
126
92
  args.forEach((value) => {
127
93
  if (value && value.selectors) {
128
94
  let rule = `${value.selectors.join(', ')} {\n`;
129
-
130
- // Add all properties (excluding 'selectors')
131
95
  for (let key in value) {
132
96
  if (key !== 'selectors' && value.hasOwnProperty(key)) {
133
97
  const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
134
98
  rule += ` ${kebabKey}: ${value[key]};\n`;
135
99
  }
136
100
  }
137
-
138
101
  rule += `}\n\n`;
139
102
  cssOutput += rule;
140
103
  }
141
104
  });
142
-
143
105
  chain.cssOutput = cssOutput.trim();
144
106
  return cssOutput.trim();
145
107
  };
146
-
147
108
  const compile = (obj) => {
148
109
  let cssString = '';
149
-
150
110
  for (const key in obj) {
151
111
  if (obj.hasOwnProperty(key)) {
152
112
  const element = obj[key];
153
113
  let selectors = element.selectors || [];
154
114
  let elementCSS = '';
155
-
156
115
  for (let prop in element) {
157
116
  if (element.hasOwnProperty(prop) && prop !== 'selectors') {
158
- // Convert camelCase to kebab-case
159
117
  const kebabKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
160
118
  elementCSS += ` ${kebabKey}: ${element[prop]};\n`;
161
119
  }
162
120
  }
163
-
164
121
  cssString += `${selectors.join(', ')} {\n${elementCSS}}\n`;
165
122
  }
166
123
  }
167
-
168
124
  chain.cssOutput = cssString.trim();
169
- return cssString.trim(); // Added return for consistency
125
+ return cssString.trim();
170
126
  };
171
127
 
172
128
  export {