chaincss 1.13.2 → 1.13.3

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
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://chaincss.dev)
6
6
 
7
- > Chainable CSS-in-JS with build-time compilation, atomic CSS, and zero-runtime options
7
+ > ChainCSS is the JavaScript-native styling engine for the modern web
8
8
 
9
9
  > **Note:** The previous package `@melcanz85/chaincss` is no longer supported.
10
10
  > **Please install `chaincss` instead:** `npm install chaincss`
@@ -21,7 +21,7 @@
21
21
  | **Hover States** | `.hover().backgroundColor('blue').end()` |
22
22
  | **Hover Exit** | `.end()` method to exit hover mode |
23
23
  | **Nested Selectors** | `.select('.parent .child')` |
24
- | **Token Resolution** | `$colors.primary` → actual color value |
24
+ | **Token Resolution** | `:colors.primary` → actual color value |
25
25
  | **CSS Property Validation** | Warns on invalid CSS properties |
26
26
 
27
27
  ### At-Rules (CSS Rules)
@@ -44,7 +44,6 @@
44
44
  | **Design Tokens** | `createTokens()` with colors, spacing, typography |
45
45
  | **Token Getter** | `tokens.get('colors.primary')` |
46
46
  | **Token Resolution in Styles** | `$colors.primary` syntax |
47
- | **Responsive Values** | `responsive({ base: '16px', sm: '14px', lg: '18px' })` |
48
47
  | **Theme Switching** | Dynamic token updates at runtime |
49
48
  | **CSS Variables Output** | `tokens.toCSSVariables()` |
50
49
 
@@ -129,20 +128,13 @@
129
128
  | **Atomic CSS Optimization** | Eliminates duplicate styles |
130
129
  | **Cache Strategy** | File + compiled function cache |
131
130
 
132
- ### Security & Compatibility
133
131
 
134
- | Feature | Description |
135
- |---------|-------------|
136
- | **No eval()** | Uses `new Function()` with parameters |
137
- | **No vm2** | Native Node.js module system |
138
- | **Node.js 14+** | Minimum version requirement |
139
- | **ESM/CJS Support** | Dual module format |
140
- | **Browser Support** | Modern browsers via autoprefixer |
132
+ For complete guide, documentation, examples, and API reference see:
133
+
134
+ ## [Documentation](https://www.chaincss.dev/docs)
141
135
 
142
- ## Documentation
136
+
143
137
 
144
- For complete guide, documentation, examples, and API reference,
145
- just go to the docs section of [https://chaincss.dev](https://www.chaincss.dev)
146
138
 
147
139
  ## License
148
140
 
package/browser/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // ChainCSS Browser Entry Point
2
- export { $, run, compile, chain, createTokens, responsive, tokens } from './rtt.js';
2
+ export { $, run, compile, chain, createTokens, tokens } from './rtt.js';
3
3
  export { useChainStyles, useDynamicChainStyles, useThemeChainStyles, ChainCSSGlobal, withChainStyles, cx } from './react-hooks.js';
package/browser/rtt.js CHANGED
@@ -1,4 +1,4 @@
1
- import { tokens, createTokens, responsive } from '../shared/tokens.mjs';
1
+ import { tokens as importedTokens, DesignTokens } from '../shared/tokens.mjs';
2
2
 
3
3
  let cachedProperties = null;
4
4
 
@@ -28,7 +28,7 @@ const loadCSSProperties = async () => {
28
28
  });
29
29
 
30
30
  cachedProperties = Array.from(baseProperties).sort();
31
- console.log(`✅ Loaded ${cachedProperties.length} CSS properties from CDN`);
31
+ //console.log(`Loaded ${cachedProperties.length} CSS properties from CDN`);
32
32
  return cachedProperties;
33
33
  }
34
34
  } catch (error) {
@@ -303,19 +303,43 @@ function $(useTokens = true) {
303
303
 
304
304
  const run = (...args) => {
305
305
  let cssOutput = '';
306
+
306
307
  args.forEach((value) => {
307
308
  if (value && value.selectors) {
308
- let rule = `${value.selectors.join(', ')} {\n`;
309
+ let normalStyles = '';
310
+ let hoverStyles = '';
311
+
312
+ // Separate normal properties from hover
309
313
  for (let key in value) {
310
- if (key !== 'selectors' && value.hasOwnProperty(key)) {
314
+ if (key === 'selectors') continue;
315
+
316
+ if (key === 'hover' && typeof value[key] === 'object') {
317
+ // Build hover styles separately
318
+ hoverStyles = `${value.selectors.join(', ')}:hover {\n`;
319
+ for (let hoverKey in value[key]) {
320
+ const kebabKey = hoverKey.replace(/([A-Z])/g, '-$1').toLowerCase();
321
+ hoverStyles += ` ${kebabKey}: ${value[key][hoverKey]};\n`;
322
+ }
323
+ hoverStyles += `}\n`;
324
+ } else {
325
+ // Build normal styles
311
326
  const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
312
- rule += ` ${kebabKey}: ${value[key]};\n`;
327
+ normalStyles += ` ${kebabKey}: ${value[key]};\n`;
313
328
  }
314
329
  }
315
- rule += `}\n\n`;
316
- cssOutput += rule;
330
+
331
+ // Output normal styles
332
+ if (normalStyles) {
333
+ cssOutput += `${value.selectors.join(', ')} {\n${normalStyles}}\n`;
334
+ }
335
+
336
+ // Output hover styles
337
+ if (hoverStyles) {
338
+ cssOutput += hoverStyles;
339
+ }
317
340
  }
318
341
  });
342
+
319
343
  chain.cssOutput = cssOutput.trim();
320
344
  return cssOutput.trim();
321
345
  };
@@ -359,12 +383,18 @@ const compile = (obj) => {
359
383
  return cssString.trim();
360
384
  };
361
385
 
386
+ function createTokens(tokenValues) {
387
+ const tokenObj = new DesignTokens(tokenValues);
388
+ return tokenObj;
389
+ }
390
+
391
+ const tokens = importedTokens;
392
+
362
393
  export {
363
394
  chain,
364
395
  $,
365
396
  run,
366
397
  compile,
367
398
  tokens,
368
- createTokens,
369
- responsive
399
+ createTokens
370
400
  };
@@ -0,0 +1,200 @@
1
+ import { computed, inject, provide, ref, watch } from 'vue';
2
+
3
+ // Symbol for providing the global ChainCSS instance
4
+ const CHAIN_CSS_KEY = Symbol('chaincss');
5
+
6
+ /**
7
+ * Core function to process styles and generate class names
8
+ */
9
+ function processStyles(styles, atomic = true) {
10
+ const result = {};
11
+
12
+ for (const [key, styleFn] of Object.entries(styles)) {
13
+ if (typeof styleFn === 'function') {
14
+ result[key] = styleFn();
15
+ } else if (styleFn && typeof styleFn === 'object') {
16
+ // If it's already processed, use it
17
+ result[key] = styleFn;
18
+ } else {
19
+ result[key] = styleFn;
20
+ }
21
+ }
22
+
23
+ return result;
24
+ }
25
+
26
+ /**
27
+ * Main composable for using ChainCSS in Vue components
28
+ * @param {Object|Ref|ComputedRef} styles - Style definitions
29
+ * @param {Object} options - Configuration options
30
+ * @returns {Object} - Classes object
31
+ */
32
+ export function useAtomicClasses(styles, options = {}) {
33
+ const { atomic = true, global = false } = options;
34
+
35
+ // Get global ChainCSS instance if available
36
+ const chainCSS = inject(CHAIN_CSS_KEY, null);
37
+
38
+ // Create reactive classes
39
+ const classes = computed(() => {
40
+ // Resolve styles if it's a ref or computed
41
+ const resolvedStyles = typeof styles === 'function'
42
+ ? styles()
43
+ : styles?.value || styles;
44
+
45
+ // Process the styles
46
+ const processed = processStyles(resolvedStyles, atomic);
47
+
48
+ // If we have a global ChainCSS instance, register styles
49
+ if (chainCSS && typeof chainCSS.register === 'function') {
50
+ chainCSS.register(processed, { atomic, global });
51
+ }
52
+
53
+ return processed;
54
+ });
55
+
56
+ return {
57
+ classes,
58
+ // Helper to get specific class
59
+ cx: (name) => classes.value[name],
60
+ // Helper to combine multiple classes
61
+ cn: (...names) => names.map(name => classes.value[name]).filter(Boolean).join(' ')
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Component for injecting global styles
67
+ */
68
+ export const ChainCSSGlobal = {
69
+ name: 'ChainCSSGlobal',
70
+ props: {
71
+ styles: {
72
+ type: Object,
73
+ required: true
74
+ },
75
+ atomic: {
76
+ type: Boolean,
77
+ default: true
78
+ }
79
+ },
80
+ setup(props) {
81
+ // Create a computed ref for styles
82
+ const globalStyles = computed(() => props.styles);
83
+
84
+ // Process and inject styles
85
+ const processedStyles = computed(() => {
86
+ return processStyles(globalStyles.value, props.atomic);
87
+ });
88
+
89
+ // Provide ChainCSS instance to children
90
+ const chainCSSInstance = {
91
+ register: (styles, options) => {
92
+ // This would integrate with your actual ChainCSS runtime
93
+ console.log('Registering styles:', styles, options);
94
+ // TODO: Connect to actual ChainCSS runtime
95
+ }
96
+ };
97
+
98
+ provide(CHAIN_CSS_KEY, chainCSSInstance);
99
+
100
+ // In development, log the styles
101
+ if (process.env.NODE_ENV === 'development') {
102
+ watch(processedStyles, (styles) => {
103
+ console.log('[ChainCSS] Global styles registered:', styles);
104
+ }, { immediate: true });
105
+ }
106
+
107
+ // This component doesn't render anything
108
+ return () => null;
109
+ }
110
+ };
111
+
112
+ /**
113
+ * Create a themed component with ChainCSS styles
114
+ * @param {Object|Function} styles - Style definitions
115
+ * @param {Object} options - Component options
116
+ * @returns {Object} - Vue component
117
+ */
118
+ export function createStyledComponent(styles, options = {}) {
119
+ const { name = 'StyledComponent', atomic = true } = options;
120
+
121
+ return {
122
+ name,
123
+ props: {
124
+ as: {
125
+ type: String,
126
+ default: 'div'
127
+ },
128
+ className: {
129
+ type: String,
130
+ default: ''
131
+ },
132
+ ...options.props
133
+ },
134
+ setup(props, { slots, attrs }) {
135
+ // Resolve styles (can be a function that receives props)
136
+ const resolvedStyles = computed(() => {
137
+ if (typeof styles === 'function') {
138
+ return styles(props);
139
+ }
140
+ return styles;
141
+ });
142
+
143
+ const { classes, cn } = useAtomicClasses(resolvedStyles, { atomic });
144
+
145
+ // Combine classes
146
+ const combinedClasses = computed(() => {
147
+ return cn('root', props.className);
148
+ });
149
+
150
+ return () => {
151
+ const tag = props.as;
152
+ const componentProps = {
153
+ ...attrs,
154
+ class: combinedClasses.value
155
+ };
156
+
157
+ // Remove props that shouldn't be passed to DOM elements
158
+ delete componentProps.as;
159
+
160
+ return h(tag, componentProps, slots.default?.());
161
+ };
162
+ }
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Create a reactive theme system
168
+ * @param {Object} themes - Theme definitions
169
+ * @returns {Object} - Theme utilities
170
+ */
171
+ export function createTheme(themes) {
172
+ const currentTheme = ref(Object.keys(themes)[0] || 'light');
173
+
174
+ const themeStyles = computed(() => {
175
+ return themes[currentTheme.value] || {};
176
+ });
177
+
178
+ const setTheme = (themeName) => {
179
+ if (themes[themeName]) {
180
+ currentTheme.value = themeName;
181
+ }
182
+ };
183
+
184
+ const toggleTheme = () => {
185
+ const keys = Object.keys(themes);
186
+ const currentIndex = keys.indexOf(currentTheme.value);
187
+ const nextIndex = (currentIndex + 1) % keys.length;
188
+ currentTheme.value = keys[nextIndex];
189
+ };
190
+
191
+ return {
192
+ currentTheme: readonly(currentTheme),
193
+ themeStyles,
194
+ setTheme,
195
+ toggleTheme
196
+ };
197
+ }
198
+
199
+ // Import Vue's h function for createStyledComponent
200
+ import { h, readonly } from 'vue';