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 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
+ ![npm downloads](https://img.shields.io/npm/dm/@melcanz85/chaincss)
4
+ [![npm version](https://img.shields.io/npm/v/@melcanz85/chaincss.svg)](https://www.npmjs.com/package/@melcanz85/chaincss)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ [![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](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)
@@ -0,0 +1,3 @@
1
+ // ChainCSS Browser Entry Point
2
+ export { $, run, compile, chain, createTokens, responsive, tokens } from './rtt.js';
3
+ export { useChainStyles, useDynamicChainStyles, useThemeChainStyles, ChainCSSGlobal, withChainStyles, cx } from './react-hooks.js';
@@ -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
+ };