chaincss 2.4.3 → 2.4.5

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
@@ -1,678 +1,364 @@
1
- <h1 align="center">ChainCSS</h1>
1
+ # ChainCSS v2.4
2
2
 
3
- <p align="center">
4
- <strong>The CSS-in-JS platform with compiler intelligence.</strong><br>
5
- Zero runtime by default. Semantic styling. WCAG-aware. Intent-driven.
6
- </p>
3
+ **Zero-runtime CSS-in-JS with auto-detection.** Static styles compile to plain CSS. Dynamic values resolve at runtime. No manual mode switching.
7
4
 
8
- <p align="center">
9
- <a href="https://www.npmjs.com/package/chaincss">
10
- <img src="https://img.shields.io/npm/v/chaincss" alt="npm">
11
- </a>
12
-
13
- <a href="https://github.com/melcanz08/chaincss/blob/main/LICENSE">
14
- <img src="https://img.shields.io/github/license/melcanz08/chaincss" alt="license">
15
- </a>
16
-
17
- <a href="https://github.com/melcanz08/chaincss/actions">
18
- <img src="https://img.shields.io/badge/tests-708%20passed-brightgreen" alt="tests">
19
- </a>
20
-
21
- <a href="https://github.com/melcanz08/chaincss">
22
- <img src="https://img.shields.io/badge/modules-17-blue" alt="modules">
23
- </a>
24
- </p>
25
-
26
-
27
-
28
- # What is ChainCSS?
29
-
30
- ChainCSS lets you write styles as **native JavaScript method chains** — no CSS syntax, no template literals, no object literals.
31
-
32
- It automatically detects which styles are static (compiled to zero-runtime CSS) and which are dynamic (stay in JS), then splits them automatically.
5
+ ```bash
6
+ npm install chaincss
7
+ ```
33
8
 
34
- ChainCSS is also a **CSS intelligence platform** — not just a styling library.
9
+ Quick Start
10
+ ### tsx
35
11
 
36
- It writes, checks, and optimizes your styles at build time with zero runtime cost.
12
+ ```tsx
13
+ import { chain } from 'chaincss';
37
14
 
38
- ```ts
39
- import { chain } from "chaincss";
40
-
41
- // A single intent expands to a full,
42
- // accessible, responsive card:
43
- const card = chain()
44
- .intent("card")
45
- .$el("card");
46
-
47
- // Or write explicit styles:
48
- const hero = chain()
49
- .display("flex")
50
- .flexDirection("column")
51
- .gap(16)
52
- .padding(24)
53
- .background("white")
54
- .borderRadius(12)
15
+ const btn = chain()
16
+ .bg('#6366f1')
17
+ .color('#ffffff')
18
+ .padding('12px 24px')
19
+ .rounded(8)
20
+ .fontSize(16)
21
+ .fontWeight(600)
55
22
  .hover()
56
- .boxShadow("0 4px 12px rgba(0,0,0,0.15)")
57
- .transform("translateY(-2px)")
23
+ .bg('#4f46e5')
24
+ .transform('translateY(-2px)')
58
25
  .end()
59
- .$el("hero");
60
- ```
61
-
62
- **No CSS syntax. No template literals. Compiler-enforced quality.**
63
-
64
-
65
- # Constraint-Based Styling
66
-
67
- Declare relationships, not values. The constraint solver compiles them to native CSS at build time.
68
-
69
- **No runtime JS. No manual `calc()`. The compiler picks the best CSS feature.**
70
-
71
- ---
72
-
73
- ## 1. Aspect Ratios from Math
74
-
75
- Describe the relationship between width and height:
76
-
77
- ```ts
78
- chain()
79
- .constrain("height", "= width * 0.5") // 2:1 ratio
80
- .constrain("width", "< parent") // Don't overflow container
81
- .$el("video");
82
- ```
83
-
84
- ### Compiles to
26
+ .$el('button');
85
27
 
86
- ```css
87
- .video {
88
- aspect-ratio: 1 / 2;
89
- max-width: 100%;
90
- }
91
- ```
92
-
93
- The solver detects `width * 0.5` and emits `aspect-ratio` instead of:
94
-
95
- ```css
96
- height: calc(width * 0.5);
97
- ```
98
-
99
- `aspect-ratio` has better performance and avoids layout shifts.
100
-
101
- ### Tailwind Equivalent
102
-
103
- ```txt
104
- aspect-[2/1] max-w-full
105
- ```
106
-
107
- ### ChainCSS Difference
108
-
109
- You write the math. The compiler writes the CSS.
110
-
111
- ---
112
-
113
- ## 2. Container-Aware Responsive Layouts
114
-
115
- Use conditional constraints to generate `@container` queries automatically.
116
-
117
- ```ts
118
- chain()
119
- .constrain("columns", ">= 3 when > 768px")
120
- .constrain("gap", "= 24px")
121
- .$el("grid");
28
+ // { selectors: ['.chain-button'], backgroundColor: '#6366f1', ... }
29
+ // CSS output: .chain-button { background-color: #6366f1; ... }
30
+ // CSS output: .chain-button:hover { background-color: #4f46e5; ... }
122
31
  ```
123
32
 
124
- ### Compiles to
125
-
126
- ```css
127
- .grid {
128
- gap: 24px;
129
- }
130
-
131
- @container (min-width: 768px) {
132
- .grid {
133
- columns: 3;
134
- }
135
- }
136
- ```
137
-
138
- > Requires `container-type: inline-size` on the parent.
139
- > ChainCSS adds it automatically when container constraints are detected.
140
-
141
- ---
142
-
143
- ## 3. Fluid Values with `clamp()`
33
+ Auto-Detection: Static vs Dynamic
34
+ ChainCSS automatically detects what can be compiled at build time and what needs runtime resolution. You never specify which is which.
144
35
 
145
- Stop calculating `clamp()` manually.
146
-
147
- ```ts
148
- chain()
149
- .constrain("fontSize", "= clamp(14px, 5vw, 24px)")
150
- .constrain("padding", "= clamp(16px, 4vw, 32px)")
151
- .$el("hero");
152
- ```
153
-
154
- ### Compiles to
155
-
156
- ```css
157
- .hero {
158
- font-size: clamp(14px, 5vw, 24px);
159
- padding: clamp(16px, 4vw, 32px);
160
- }
36
+ ```tsx
37
+ const styles = chain()
38
+ .bg('#6366f1') // static -> goes to CSS file
39
+ .color(() => isActive ? 'green' : 'red') // dynamic -> resolved at runtime
40
+ .padding(16) // static -> goes to CSS file
41
+ .border(() => isActive ? '2px solid green' : '1px solid gray') // dynamic
42
+ .$el('btn');
161
43
  ```
44
+ Rule: Strings and numbers are static. Functions are dynamic.
162
45
 
163
- Works with:
164
-
165
- - `clamp()`
166
- - `min()`
167
- - `max()`
168
- - `calc()`
169
-
170
- ---
171
-
172
- ## 4. Scroll-Driven Sticky Positioning
173
-
174
- "Stick this element until another element reaches the viewport."
46
+ API
47
+ ### chain()
48
+ Creates a new style chain. Returns a proxy that collects styles.
175
49
 
176
50
  ```ts
177
- chain()
178
- .constrain("sidebar", "sticky until footer")
179
- .$el("sidebar");
180
- ```
181
-
182
- ### Compiles to
51
+ import { chain } from 'chaincss';
183
52
 
184
- ```css
185
- .sidebar {
186
- position: sticky;
187
- top: 0;
188
-
189
- animation: sticky-sidebar 1s linear both;
190
- animation-timeline: scroll();
191
- animation-range: contain 0% contain 100%;
192
- }
193
-
194
- @keyframes sticky-sidebar {
195
- to {
196
- position: relative;
197
- }
198
- }
53
+ const styles = chain()
54
+ .property(value) // any CSS property (camelCase)
55
+ .$el('name'); // finalize, returns { selectors: ['.chain-name'], ...properties }
199
56
  ```
200
57
 
201
- Zero JavaScript. Uses native `animation-timeline: scroll()` available in modern Chromium browsers.
202
-
203
- ---
204
-
205
- ## 5. Parent-Relative Constraints
206
-
207
- Keep elements inside their parents without writing media queries.
58
+ Properties
59
+ All 500+ CSS properties are available as camelCase methods:
208
60
 
209
61
  ```ts
210
62
  chain()
211
- .constrain("width", "< parent")
212
- .constrain("width", "> 320px")
213
- .$el("modal");
214
- ```
215
-
216
- ### Compiles to
217
-
218
- ```css
219
- .modal {
220
- max-width: 100%;
221
- min-width: 320px;
222
- }
223
- ```
224
-
225
- ---
226
-
227
- # How It Works
228
-
229
- The compiler parses constraint expressions and resolves them into the most optimal native CSS feature.
230
-
231
- | You Write | Compiler Resolves To | Strategy |
232
- |---|---|---|
233
- | `width < parent` | `max-width: 100%` | Direct mapping |
234
- | `height = width * 0.5` | `aspect-ratio: 1 / 2` | Aspect-ratio optimization |
235
- | `fontSize = clamp(14, 5vw, 24)` | `font-size: clamp(...)` | Native clamp |
236
- | `columns >= 3 when > 768px` | `@container (min-width: 768px)` | Container query |
237
- | `sidebar sticky until footer` | `animation-timeline: scroll()` | Scroll timeline |
238
-
239
- ---
240
-
241
- ## Supported Operators
242
-
243
- ```txt
244
- < > <= >= = !=
245
- ```
246
-
247
- ## Supported References
248
-
249
- ```txt
250
- parent
251
- viewport
252
- self
253
- sibling.width
254
- ```
255
-
256
- ## Supported Functions
257
-
258
- ```txt
259
- clamp()
260
- min()
261
- max()
262
- calc()
263
- ```
264
-
265
- ---
266
-
267
- # Debugging Constraints
268
-
269
- See how constraints were resolved during compilation.
270
-
271
- ## CLI
272
-
273
- ```bash
274
- chaincss build --explain
275
- ```
276
-
277
- ### Output
278
-
279
- ```txt
280
- card: height = width * 0.5 aspect-ratio: 1/2
281
- card: width < parent max-width: 100%
282
- ```
283
-
284
- ---
285
-
286
- ## Programmatic Debugging
287
-
288
- ```ts
289
- import { resolveConstraint } from "chaincss/compiler";
290
-
291
- const result = resolveConstraint({
292
- property: "height",
293
- operator: "=",
294
- expression: "width * 0.5",
295
- });
296
-
297
- console.log(result.explanation);
298
- // "height = width * 0.5 → aspect-ratio: 1/2"
299
- ```
300
-
301
-
302
- # What Makes ChainCSS Different
303
-
304
- | Capability | ChainCSS | Tailwind | StyleX | Vanilla Extract |
305
- |---|---|---|---|---|
306
- | Zero runtime | ✅ | ✅ | ✅ | ✅ |
307
- | Intent-based API | ✅ | ❌ | ❌ | ❌ |
308
- | Semantic tokens | ✅ | ❌ | ❌ | ❌ |
309
- | WCAG accessibility checking | ✅ | ❌ | ❌ | ❌ |
310
- | Responsive inference | ✅ | ❌ | ❌ | ❌ |
311
- | Pattern learning | ✅ | ❌ | ❌ | ❌ |
312
- | CSS `if()` transpiler | ✅ | ❌ | ❌ | ❌ |
313
- | Constraint-based styling | ✅ | ❌ | ❌ | ❌ |
314
- | Layout intelligence | ✅ | ❌ | ❌ | ❌ |
315
- | Scroll-driven animations | ✅ | ❌ | ❌ | ❌ |
316
- | Self-healing CSS | ✅ | ❌ | ❌ | ❌ |
317
- | Source-aware optimization | ✅ | ❌ | ❌ | ❌ |
318
- | Design system extraction | ✅ | ❌ | ❌ | ❌ |
319
- | 3 modes (build/runtime/hybrid) | ✅ | ❌ | ❌ | ❌ |
320
-
321
-
322
-
323
- # Installation
324
-
325
- ```bash
326
- npm install chaincss
327
- ```
328
-
329
-
330
- # Quick Start
331
-
332
- | Environment | Setup |
333
- |---|---|
334
- | Vite | Add `chaincss()` plugin to `vite.config.ts` |
335
- | Node.js | `import { chain } from "chaincss"` |
336
- | Browser CDN | `import { chain } from "https://cdn.jsdelivr.net/npm/chaincss/dist/browser.js"` |
337
-
338
-
339
- # Intent API (v2.3)
340
-
341
- The highest-level API.
342
-
343
- Write what you want — the compiler figures out how.
344
-
345
- ```ts
346
- // Layout intents
347
- chain().intent("center-content").$el("centered");
348
- chain().intent("stack").$el("stack");
349
- chain().intent("sidebar-layout").$el("dashboard");
350
-
351
- // Component intents
352
- chain().intent("card").$el("card");
353
- chain().intent("button-primary").$el("cta");
354
- chain().intent("modal").$el("dialog");
355
-
356
- // Semantic intents
357
- chain().intent("hero-section").$el("hero");
358
- chain().intent("sticky-header").$el("nav");
359
-
360
- // Interaction intents
361
- chain().intent("hover-lift").$el("interactive");
362
- chain().intent("focus-ring").$el("accessible");
363
- ```
364
-
365
- Each intent triggers:
366
- - semantic tokens
367
- - responsive overrides
368
- - accessibility checks
369
- - theme adaptation
370
-
371
- —all at build time.
372
-
373
-
374
-
375
- # Semantic Tokens
63
+ .backgroundColor('#fff')
64
+ .fontSize(16)
65
+ .borderRadius(8)
66
+ .display('flex')
67
+ .alignItems('center')
68
+ .justifyContent('space-between')
69
+ ```
70
+ Numeric values automatically get px added (except unitless properties like opacity, zIndex, fontWeight).
71
+
72
+ Shorthands
73
+ Common properties have short aliases:
74
+
75
+ | Shorthand | CSS Property |
76
+ | :--- | :--- |
77
+ | bg() | background-color |
78
+ | fs() | font-size |
79
+ | fw() | font-weight |
80
+ | rounded() | border-radius |
81
+ | p() | padding |
82
+ | m() | margin |
83
+ | px() / py() | padding-left/right / padding-top/bottom |
84
+ | mx() / my() | margin-left/right / margin-top/bottom |
85
+ | flex() | display: flex |
86
+ | grid() | display: grid |
87
+ | inlineFlex() | display: inline-flex |
88
+ | block() | display: block |
89
+ | w() / h() | width / height |
90
+ | maxW() / minH() | max-width / min-height |
91
+ | lh() | line-height |
92
+ | ls() | letter-spacing |
93
+ | ov() | overflow |
94
+ | pos() | position |
95
+ | z() | z-index |
96
+ | op() | opacity |
97
+ | gap() | gap |
98
+ | gridCols() | grid-template-columns |
99
+ | align() | text-align |
100
+ | cursor() | cursor |
101
+ | shadow() | box-shadow |
102
+ | transform() | transform |
103
+ | transition() | transition |
104
+
105
+ Macros
106
+ One method replaces multiple CSS declarations:
107
+
108
+ | Macro | What it does |
109
+ | :--- | :--- |
110
+ | center() | display: flex; align-items: center; justify-content: center |
111
+ | flexCenter(dir?) | Flex centering, optional 'row' or 'col' direction |
112
+ | gridCenter() | display: grid; place-items: center |
113
+ | pill() | border-radius: 9999px; padding: 8px 20px; display: inline-flex; align-items: center |
114
+ | circle(size) | Perfect circle with flex centering |
115
+ | square(size) | Square with flex centering |
116
+ | glass(blur?) | Backdrop blur glassmorphism effect |
117
+ | glow({color, size}) | Box-shadow glow effect |
118
+ | hide() | opacity: 0; visibility: hidden; pointer-events: none |
119
+ | show() | opacity: 1; visibility: visible; pointer-events: auto |
120
+ | truncate() | overflow: hidden; text-overflow: ellipsis; white-space: nowrap |
121
+ | textGradient(colors) | Gradient text with webkit background clip |
122
+ | meshGradient(colors) | Multi-color mesh gradient background |
123
+ | absolute(coords?) | position: absolute with optional top/right/bottom/left |
124
+ | fixed(coords?) | position: fixed with optional coordinates |
125
+ | sticky(coords?) | position: sticky with optional coordinates |
126
+ | relative() | position: relative |
127
+ | size(value) | Sets both width and height |
128
+ | stack({spacing, dir?}) | Flex column with configurable spacing and direction |
129
+ | gridTable(minWidth) | Responsive auto-fit grid table |
130
+ | aspect(ratio) | Set aspect-ratio (supports 'square', 'video', 'golden') |
131
+ | safeArea(edge?) | iOS safe area padding |
132
+ | clickScale(amount?) | Scale down on :active pseudo-class |
133
+ | pressable() | Combines cursor: pointer + unselectable + clickScale |
134
+ | focusRing(color?) | Focus-visible outline ring |
135
+ | skeleton({active, color?}) | Loading skeleton animation |
136
+ | shimmer() | Shimmer loading animation |
137
+ | fluidText({min, max, vw?}) | Responsive fluid typography using clamp() |
138
+ | lineClamp(lines?) | Multi-line text truncation |
139
+ | frostedNav(blur?) | Fixed navbar with glass effect + safe area |
140
+ | parallax(scale?) | Parallax scrolling container |
141
+ | noise(opacity?) | SVG noise texture background |
142
+ | scrollable(axis?) | Scrollable container ('x', 'y', 'both') |
143
+ | unselectable() | Disable text selection |
144
+ | outlineDebug() | Red outline debugging for layout |
145
+
146
+ States & Selectors
376
147
 
377
148
  ```ts
378
149
  chain()
379
- .surface("interactive")
380
- .text("primary")
381
- .elevation("floating")
382
- .spacing("comfortable")
383
- .state("hover")
384
- .state("focus")
385
- .$el("composed");
150
+ .hover()
151
+ .bg('red')
152
+ .end()
153
+ .nest('.child', (c) => c.color('blue'))
154
+ .children((c) => c.padding(8)) // shortcut for nest('& > *', ...)
155
+ .media('(min-width: 768px)', (c) => c.flexDirection('row'))
156
+ .supports('display: grid', (c) => c.gap(16))
157
+ .container('(min-width: 400px)', (c) => c.color('red'))
158
+ .layer('base', (c) => c.bg('white'))
159
+ .when(condition, (c) => c.display('none'))
386
160
  ```
387
161
 
388
- | Category | Available Intents |
389
- |---|---|
390
- | surface | interactive, container, overlay, sheet |
391
- | text | primary, secondary, muted, link |
392
- | elevation | flat, raised, floating, modal |
393
- | spacing | none, tight, compact, spacious |
394
- | state | hover, active, focus, disabled |
395
-
396
-
397
-
398
- # Compiler Intelligence
399
-
400
- ChainCSS analyzes your styles at build time and reports issues before they ship.
162
+ Transforms
401
163
 
402
164
  ```ts
403
165
  chain()
404
- .width("1200px")
405
- .color("#999")
406
- .fontSize("10px")
407
- .outline("none")
408
- .animation("fadeIn 1s")
409
- .$el("risky-button");
166
+ .scale(1.2)
167
+ .rotate('45deg')
168
+ .x(10) // translateX with automatic px
169
+ .y(20) // translateY with automatic px
170
+ .skew('5deg')
410
171
  ```
411
172
 
412
- The compiler can:
413
- - detect WCAG contrast failures
414
- - fix invalid font sizes
415
- - add focus-visible fallbacks
416
- - wrap animations in reduced-motion queries
417
- - prevent mobile overflow
418
-
419
-
420
-
421
- # Constraint-Based Styling
173
+ Keyframes & Fonts
422
174
 
423
175
  ```ts
424
176
  chain()
425
- .constrain("width", "< parent")
426
- .constrain("height", "= width * 0.5")
427
- .constrain("columns", ">= 3 when > 768px")
428
- .$el("responsive-card");
177
+ .keyframes('fadeIn', {
178
+ '0%': { opacity: 0 },
179
+ '100%': { opacity: 1 }
180
+ })
181
+ .fontFace({ fontFamily: 'MyFont', src: 'url(/myfont.woff2)' })
429
182
  ```
430
183
 
431
-
432
-
433
- # Scroll Timeline Engine
184
+ Mixins
434
185
 
435
186
  ```ts
436
- import {
437
- createScrollAnimation,
438
- compileScrollAnimation
439
- } from "chaincss";
440
-
441
- const fadeIn = createScrollAnimation(
442
- "fadeIn",
443
- ".reveal"
444
- );
445
-
446
- const parallax = createScrollAnimation(
447
- "parallax",
448
- ".bg"
449
- );
187
+ const mixin = { color: 'red', fontSize: '16px' };
188
+ chain().use(mixin).bg('blue').$el('test');
450
189
  ```
451
190
 
452
- Native scroll-driven animations with zero JavaScript runtime.
453
-
454
-
455
-
456
- # Core API
457
-
458
- ## The Chain
191
+ Compiling to CSS
459
192
 
460
193
  ```ts
461
- import { chain } from "chaincss";
194
+ import { chain, compileToCSS } from 'chaincss';
462
195
 
463
196
  const styles = chain()
464
- .display("flex")
465
- .padding(20)
466
- .color("red")
467
- .$el("my-component");
468
- ```
469
-
470
- ## Smart Chain
197
+ .bg('red')
198
+ .padding(16)
199
+ .hover().bg('darkred').end()
200
+ .build(['button']);
471
201
 
472
- ```ts
473
- import { smartChain } from "chaincss";
474
-
475
- const styles = smartChain()
476
- .display("flex")
477
- .padding(20)
478
- .color(props.textColor)
479
- .fontSize(theme.sizes.lg)
480
- .$el("hybrid-card");
202
+ const css = compileToCSS(styles, { scopeSelector: '.btn' });
203
+ // .btn { background-color: red; padding: 16px; }
204
+ // .btn:hover { background-color: darkred; }
481
205
  ```
482
206
 
207
+ `compileToCSS(styleObject, options?)`
483
208
 
209
+ | Option | Type | Default | Description |
210
+ | :--- | :--- | :--- | :--- |
211
+ | scopeSelector | string | '' | CSS selector for the rule |
212
+ | minify | boolean | false | Minify output |
213
+ | sourceMap | boolean | false | Add source comments |
214
+ | sourceFile | string | '' | Source file path for comments |
484
215
 
485
- # Shorthands
216
+ `partitionForBuild(styleObject, options?)`
217
+ Splits styles into static CSS and dynamic values for build-time extraction:
486
218
 
487
219
  ```ts
488
- chain()
489
- .bg("#fff")
490
- .m(16)
491
- .p(20)
492
- .br(8)
493
- .fs(16)
494
- .fw(700)
495
- .c("#333");
220
+ const { css, dynamicValues, hasDynamic } = partitionForBuild(styles, { scopeSelector: '.btn' });
221
+ // css: '.btn { background-color: red; }'
222
+ // dynamicValues: { color: <function> }
223
+ // hasDynamic: true
496
224
  ```
497
225
 
498
-
499
- # Macros
226
+ Vite Plugin
500
227
 
501
228
  ```ts
502
- chain().flex();
503
- chain().grid();
504
- chain().center();
505
- chain().stack(16);
506
- chain().glass();
507
- chain().glow("#6366f1");
508
- chain().focusRing("#3b82f6");
509
- ```
510
-
511
-
512
-
513
- # Conditional Styles
514
-
515
- ```ts
516
- chain()
517
- .padding(12)
518
- .when(isActive, c =>
519
- c.background("#10b981")
520
- .color("white")
521
- )
522
- .$el("stateful-btn");
229
+ // vite.config.ts
230
+ import chaincssPlugin from 'chaincss/plugin/vite';
231
+ import react from '@vitejs/plugin-react';
232
+
233
+ export default defineConfig({
234
+ plugins: [
235
+ chaincssPlugin({ verbose: true }),
236
+ react(),
237
+ ],
238
+ });
523
239
  ```
240
+ The plugin:
241
+ * Serves generated CSS at `/__chaincss.css`
242
+ * Auto-injects `<link>` tag into HTML
243
+ * Watches for CSS changes and hot-reloads
524
244
 
525
-
526
-
527
- # Responsive Design
245
+ Setup
246
+ Create a CSS generation script and add it to your package.json:
528
247
 
529
248
  ```ts
530
- chain()
531
- .display("flex")
532
- .flexDirection("column")
533
- .responsive("md", c =>
534
- c.flexDirection("row")
535
- )
536
- .$el("responsive");
537
- ```
538
-
539
-
249
+ // src/generate-css.ts
250
+ import { compileToCSS } from 'chaincss';
251
+ import * as styles from './styles.chain';
252
+ import fs from 'fs';
540
253
 
541
- # Math Engine
542
-
543
- ```ts
544
- import {
545
- math,
546
- add,
547
- fluidType,
548
- convert
549
- } from "chaincss";
550
-
551
- add("10px", "2rem");
552
-
553
- fluidType({
554
- minSize: 14,
555
- maxSize: 20
556
- });
254
+ let css = '*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}\n';
255
+ css += 'body{font-family:system-ui,sans-serif}\n\n';
557
256
 
558
- convert("32px", "rem");
257
+ for (const [_, obj] of Object.entries(styles)) {
258
+ const sel = (obj as any).selectors?.[0];
259
+ if (sel) css += compileToCSS(obj as any, { scopeSelector: sel }) + '\n\n';
260
+ }
261
+ fs.writeFileSync('public/chaincss.css', css);
559
262
  ```
560
263
 
561
-
562
-
563
- # Design Tokens
564
-
565
- ```ts
566
- import {
567
- createTokens,
568
- createThemeContract,
569
- createTheme
570
- } from "chaincss";
571
-
572
- const tokens = createTokens({
573
- colors: {
574
- primary: "#6366f1",
575
- secondary: "#10b981"
264
+ ```json
265
+ {
266
+ "scripts": {
267
+ "dev": "npm run css && vite",
268
+ "css": "npx tsx src/generate-css.ts"
576
269
  }
577
- });
270
+ }
578
271
  ```
579
272
 
580
-
581
-
582
- # Recipe System
273
+ Debug Mode
583
274
 
584
275
  ```ts
585
- import { recipe } from "chaincss";
276
+ const debugChain = chain({ debug: true })
277
+ .bg('red')
278
+ .color(() => themeColor)
279
+ .padding(16);
586
280
 
587
- const button = recipe({
588
- base: {
589
- selectors: ["btn"],
590
- display: "inline-flex",
591
- borderRadius: "8px"
592
- }
593
- });
281
+ console.log(debugChain.explain().visualization);
594
282
  ```
595
-
596
-
597
-
598
- # Animations
599
-
600
- ```ts
601
- chain()
602
- .fadeIn()
603
- .slideInUp()
604
- .zoomIn()
605
- .bounce()
606
- .pulse()
607
- .$el("animated");
283
+ Output:
284
+ ```text
285
+ ChainCSS Style Explanation
286
+ --------------------------
287
+ bg -> red (static)
288
+ color -> <fn> (dynamic)
289
+ padding -> 16 (static)
290
+ --------------------------
291
+ Static: 2 | Dynamic: 1
608
292
  ```
609
293
 
294
+ Class Names
295
+ `chain().$el('button')` produces the selector `.chain-button`. To use it in JSX:
610
296
 
297
+ ```tsx
298
+ // Option 1: Extract manually
299
+ <button className={btn.selectors[0].replace('.', '')}>Click</button>
611
300
 
612
- # Self-Healing CSS
301
+ // Option 2: Helper function
302
+ const cls = (c: any) => c.selectors?.[0]?.replace('.', '') || '';
303
+ <button className={cls(btn)}>Click</button>
613
304
 
614
- ```ts
615
- import { correct, heal } from "chaincss";
616
-
617
- correct("display", "flexbox");
618
- correct("position", "abs");
619
-
620
- heal(
621
- {
622
- display: "flexbox",
623
- position: "abs"
624
- },
625
- "smart"
626
- );
305
+ // Option 3: Wrapper file (styles.ts)
306
+ import * as S from './styles.chain';
307
+ export const btn = S.btn.selectors[0].replace('.', '');
627
308
  ```
628
309
 
310
+ Framework Integration
311
+ ChainCSS is framework-agnostic. `$el()` returns a plain object:
629
312
 
313
+ ```tsx
314
+ // React
315
+ <div style={styles}>...</div>
630
316
 
631
- # CLI
632
-
633
- ```bash
634
- chaincss init
635
- chaincss build
636
- chaincss watch
637
- chaincss cache clear
638
- chaincss optimize --report
639
- chaincss doctor
640
- ```
641
-
642
-
643
-
644
- # Performance
645
-
646
- - Zero runtime for static styles
647
- - Atomic CSS extraction
648
- - Smart static/dynamic splitting
649
- - Cross-file dead code elimination
650
- - Multi-pass optimization pipeline
317
+ // Vue
318
+ <div :style="styles">...</div>
651
319
 
320
+ // Svelte
321
+ <div style={styles}>...</div>
652
322
 
323
+ // Plain HTML (with CSS file)
324
+ <div class="chain-button">...</div>
325
+ ```
653
326
 
654
- # Contributing
655
-
656
- ```bash
657
- git clone https://github.com/melcanz08/chaincss.git
658
-
659
- cd chaincss
660
-
661
- npm install
327
+ API Reference
328
+ Main Exports
662
329
 
663
- npm test
664
- ```
330
+ | Export | Description |
331
+ | :--- | :--- |
332
+ | chain(options?) | Create a style chain |
333
+ | compileToCSS(obj, opts?) | Compile style object to CSS string |
334
+ | partitionForBuild(obj, opts?) | Split static/dynamic, return { css, dynamicValues } |
335
+ | classifyValue(value) | Returns 'static' or 'dynamic' |
336
+ | partitionStyles(obj) | Split object into { static, dynamic } |
337
+ | compileToCSS | Generate CSS from style object |
665
338
 
339
+ Value Classification
666
340
 
341
+ | Value type | Classification | Behavior |
342
+ | :--- | :--- | :--- |
343
+ | '#6366f1' (string) | static | Compiled to CSS |
344
+ | 16 (number) | static | Compiled to CSS (px added if needed) |
345
+ | () => themeColor (function) | dynamic | Stays in JS, resolved at runtime |
346
+ | 'theme.primary' (token ref) | dynamic | Resolved at runtime |
667
347
 
668
- # License
348
+ Migration from v2.3
669
349
 
670
- MIT © Rommel Caneos
350
+ | Old API | New API |
351
+ | :--- | :--- |
352
+ | createChain() | chain() |
353
+ | smartChain() | chain() |
354
+ | buildChain() | chain() — static values auto-detected |
355
+ | runtimeChain() | chain() — dynamic values auto-detected |
356
+ | btt.compile() | compileToCSS() |
357
+ | btt.run() | compileToCSS() |
358
+ | enableDebug() | chain({ debug: true }) |
671
359
 
672
- <p align="center">
673
- <strong>ChainCSS v2.3</strong> — The CSS intelligence platform.<br>
360
+ License
361
+ MIT
674
362
 
675
- <a href="https://github.com/melcanz08/chaincss">
676
- github.com/melcanz08/chaincss
677
- </a>
678
- </p>
363
+ Author
364
+ Rommel Caneos
@@ -1,5 +1,4 @@
1
1
  import { Plugin } from 'vite';
2
- export interface ChainCSSPluginOptions {
2
+ export default function chaincssPlugin(options?: {
3
3
  verbose?: boolean;
4
- }
5
- export default function chaincssPlugin(options?: ChainCSSPluginOptions): Plugin;
4
+ }): Plugin;
@@ -19,6 +19,7 @@ function chaincssPlugin(options = {}) {
19
19
  if (file === cssPath) {
20
20
  try {
21
21
  cssContent = fs.readFileSync(cssPath, "utf8");
22
+ log("CSS updated");
22
23
  } catch {
23
24
  }
24
25
  server.ws.send({ type: "full-reload" });
@@ -26,7 +27,9 @@ function chaincssPlugin(options = {}) {
26
27
  });
27
28
  try {
28
29
  cssContent = fs.readFileSync(cssPath, "utf8");
30
+ log(`Serving ${cssContent.length} bytes`);
29
31
  } catch {
32
+ log("No CSS file yet");
30
33
  }
31
34
  server.middlewares.use("/__chaincss.css", (_req, res) => {
32
35
  res.setHeader("Content-Type", "text/css");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaincss",
3
- "version": "2.4.3",
3
+ "version": "2.4.5",
4
4
  "description": "ChainCSS - The first CSS-in-JS library with true auto-detection mixed mode. Zero runtime by default, dynamic when you need it.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -2,11 +2,7 @@ import { Plugin } from 'vite';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
4
 
5
- export interface ChainCSSPluginOptions {
6
- verbose?: boolean;
7
- }
8
-
9
- export default function chaincssPlugin(options: ChainCSSPluginOptions = {}): Plugin {
5
+ export default function chaincssPlugin(options: { verbose?: boolean } = {}): Plugin {
10
6
  let cssContent = '';
11
7
  let cssPath = '';
12
8
 
@@ -26,12 +22,11 @@ export default function chaincssPlugin(options: ChainCSSPluginOptions = {}): Plu
26
22
  server.watcher.add(cssPath);
27
23
  server.watcher.on('change', (file: string) => {
28
24
  if (file === cssPath) {
29
- try { cssContent = fs.readFileSync(cssPath, 'utf8'); } catch {}
25
+ try { cssContent = fs.readFileSync(cssPath, 'utf8'); log('CSS updated'); } catch {}
30
26
  server.ws.send({ type: 'full-reload' });
31
27
  }
32
28
  });
33
-
34
- try { cssContent = fs.readFileSync(cssPath, 'utf8'); } catch {}
29
+ try { cssContent = fs.readFileSync(cssPath, 'utf8'); log(`Serving ${cssContent.length} bytes`); } catch { log('No CSS file yet'); }
35
30
 
36
31
  server.middlewares.use('/__chaincss.css', (_req, res) => {
37
32
  res.setHeader('Content-Type', 'text/css');