chaincss 2.1.9 → 2.1.11

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,9 +1,8 @@
1
+ # ChainCSS
2
+
1
3
  <p align="center">
2
- <h1 align="center">ChainCSS</h1>
3
- <p align="center">
4
- <strong>The first CSS-in-JS library with true auto-detection mixed mode.</strong><br>
5
- Zero runtime by default. Dynamic when you need it. No compromises.
6
- </p>
4
+ <strong>The first CSS-in-JS library with true auto-detection mixed mode.</strong><br>
5
+ Zero runtime by default. Dynamic when you need it. No compromises.
7
6
  </p>
8
7
 
9
8
  <p align="center">
@@ -16,48 +15,48 @@
16
15
 
17
16
  ## What is ChainCSS?
18
17
 
19
- ChainCSS lets you write styles as **native JavaScript method chains** instead of CSS strings or object literals. It auto-detects which parts of your styles are static (compile to zero-runtime CSS) and which are dynamic (stay in JS), then splits them automatically.
18
+ ChainCSS lets you write styles as **native JavaScript method chains** instead of CSS strings or object literals.
19
+
20
+ It automatically detects which styles are static (compiled to CSS) and which are dynamic (kept in JS), then splits them for optimal performance.
20
21
 
22
+ ```ts
21
23
  import { chain } from "chaincss";
22
24
 
23
25
  const card = chain()
24
- .display("flex")
25
- .flexDirection("column")
26
- .gap(16)
27
- .padding(24)
28
- .background("white")
29
- .borderRadius(12)
30
- .hover()
31
- .boxShadow("0 4px 12px rgba(0,0,0,0.15)")
32
- .transform("translateY(-2px)")
33
- .end()
34
- .$el("card");
35
- text
36
-
26
+ .display("flex")
27
+ .flexDirection("column")
28
+ .gap(16)
29
+ .padding(24)
30
+ .background("white")
31
+ .borderRadius(12)
32
+ .hover()
33
+ .boxShadow("0 4px 12px rgba(0,0,0,0.15)")
34
+ .transform("translateY(-2px)")
35
+ .end()
36
+ .$el("card");
37
+ ```
37
38
 
38
39
  **No CSS syntax. No template literals. No object literals. Just JavaScript.**
39
40
 
40
41
  ---
41
42
 
42
- ## The Killer Feature: Auto-Detection Mixed Mode
43
-
44
- ChainCSS automatically detects which styles are static and which are dynamic.
43
+ ## Auto-Detection Mixed Mode
45
44
 
45
+ ```ts
46
46
  import { smartChain } from "chaincss";
47
47
 
48
48
  const styles = smartChain()
49
- .display("flex") // static -> extracted to CSS file
50
- .padding(20) // static -> extracted to CSS file
51
- .color(props.textColor) // dynamic -> stays in JS
52
- .fontSize(theme.sizes.lg) // dynamic -> stays in JS
53
- .$el("dynamic-card");
54
- text
55
-
49
+ .display("flex")
50
+ .padding(20)
51
+ .color(props.textColor)
52
+ .fontSize(theme.sizes.lg)
53
+ .$el("dynamic-card");
54
+ ```
56
55
 
57
56
  | Library | Approach | Dynamic Support | Runtime Cost |
58
- |---------|----------|----------------|-------------|
57
+ |--------|---------|----------------|-------------|
59
58
  | Tailwind | Utility classes | No | Zero |
60
- | Styled Components | Runtime CSS-in-JS | Yes | Full |
59
+ | Styled Components | Runtime CSS-in-JS | Yes | High |
61
60
  | Panda CSS | Build-time + recipes | Limited | Near-zero |
62
61
  | Vanilla Extract | Build-time only | No | Zero |
63
62
  | **ChainCSS** | **Auto-detection + split** | **Yes** | **Minimal** |
@@ -66,424 +65,175 @@ text
66
65
 
67
66
  ## Installation
68
67
 
68
+ ```bash
69
69
  npm install chaincss
70
- text
71
-
70
+ ```
72
71
 
73
- ### Quick Start with Vite
72
+ ### Vite Setup
74
73
 
75
- // vite.config.ts
74
+ ```ts
76
75
  import { defineConfig } from "vite";
77
76
  import chaincss from "chaincss/plugin/vite";
78
77
 
79
78
  export default defineConfig({
80
- plugins: [chaincss()],
79
+ plugins: [chaincss()],
81
80
  });
82
- text
83
-
81
+ ```
84
82
 
85
83
  ---
86
84
 
87
85
  ## Core API
88
86
 
89
- ### The Chain
87
+ ### Basic Chain
90
88
 
89
+ ```ts
91
90
  import { chain } from "chaincss";
92
91
 
93
92
  const styles = chain()
94
- .display("flex")
95
- .padding(20) // numbers auto-convert to px
96
- .color("red")
97
- .$el("my-component");
98
- text
99
-
100
-
101
- ### Shorthands (80+)
102
-
103
- chain()
104
- .bg("#fff") // backgroundColor
105
- .m(16) // margin
106
- .p(20) // padding
107
- .br(8) // borderRadius
108
- .fs(16) // fontSize
109
- .fw(700) // fontWeight
110
- .c("#333") // color
111
- .w(200) // width
112
- .h(100) // height
113
- .d("flex") // display
114
- text
115
-
116
-
117
- ### Macros (57 built-in)
118
-
119
- **Layout:**
120
-
121
- chain().flex() // display: flex
122
- chain().grid() // display: grid
123
- chain().center() // flex + align-items + justify-content center
124
- chain().flexCenter() // flex centering
125
- chain().gridCenter() // grid centering
126
- chain().stack(16) // flex column with gap
127
- chain().cols(3) // grid-template-columns: repeat(3, 1fr)
128
- chain().bento(4) // bento grid layout
129
- text
130
-
131
-
132
- **Spacing:**
133
-
134
- chain().mx(10) // margin-left + margin-right
135
- chain().my(20) // margin-top + margin-bottom
136
- chain().px(16) // padding-left + padding-right
137
- chain().py(24) // padding-top + padding-bottom
138
- chain().size(50) // width + height
139
- chain().gap(16) // gap
140
- text
141
-
142
-
143
- **Positioning:**
144
-
145
- chain().absolute({ top: 0, left: 0 })
146
- chain().fixed({ top: 0, right: 0 })
147
- chain().sticky({ top: 0 })
148
- chain().relative()
149
- text
150
-
151
-
152
- **Visibility:**
153
-
154
- chain().hide() // visibility: hidden + opacity: 0
155
- chain().show() // visibility: visible + opacity: 1
156
- chain().unselectable() // user-select: none
157
- chain().scrollable("y") // overflow-y: auto
158
- text
159
-
160
-
161
- **Shapes:**
162
-
163
- chain().circle(50) // width + height + border-radius: 50%
164
- chain().square(40) // width + height
165
- chain().pill() // border-radius: 9999px
166
- chain().truncate() // text-overflow: ellipsis
167
- chain().aspect("16/9") // aspect-ratio
168
- text
169
-
170
-
171
- **Aesthetic Effects:**
172
-
173
- chain().glass() // backdrop-filter blur + semi-transparent bg
174
- chain().glow("#ff0000") // box-shadow glow
175
- chain().textGradient(["#667eea", "#764ba2"]) // gradient text
176
- chain().meshGradient(["#f0f", "#0ff", "#ff0", "#0f0"]) // mesh gradient
177
- chain().noise(0.05) // noise texture overlay
178
- chain().shimmer() // loading shimmer effect
179
- text
180
-
181
-
182
- **State & Interactions:**
183
-
184
- chain()
185
- .hover()
186
- .background("#2563eb")
187
- .transform("scale(1.05)")
188
- .end()
189
-
190
- chain().clickScale(0.95) // scale on click
191
- chain().pressable() // cursor pointer + hover effects
192
- chain().focusRing("#3b82f6") // focus-visible ring
193
- chain().skeleton(true) // loading skeleton animation
194
- chain().dark(c => c.bg("#1a202c").c("white")) // dark mode
195
- chain().light(c => c.bg("white").c("#1a202c")) // light mode
196
- chain().children(c => c.gap(8)) // direct children styles
197
- text
198
-
199
-
200
- **Utility:**
201
-
202
- chain().fullScreen() // fixed fullscreen overlay
203
- chain().containerMacro(1200) // centered container
204
- chain().outlineDebug() // debug outlines
205
- chain().parallax(2) // parallax scrolling
206
- chain().lineClamp(3) // text line clamp
207
- chain().frostedNav(15) // frosted glass navigation
208
- text
209
-
210
-
211
- ### Conditional Styles
212
-
213
- chain()
214
- .padding(12)
215
- .when(isActive, c => c
216
- .background("#10b981")
217
- .color("white")
218
- )
219
- .when(isDisabled, c => c
220
- .opacity(0.5)
221
- .cursor("not-allowed")
222
- )
223
- .$el("stateful-btn");
224
- text
225
-
226
-
227
- ### Nested Selectors
228
-
229
- chain()
230
- .display("flex")
231
- .nest("& > *", c => c.flex(1))
232
- .nest("&:first-child", c => c.fontWeight(700))
233
- .nest(".child", c => c.color("red"))
234
- .$el("flex-container");
235
- text
236
-
237
-
238
- ### Mixins with use()
239
-
240
- const sharedStyles = { display: "flex", alignItems: "center", gap: 8 };
241
- chain().use(sharedStyles).padding(20).$el("reused");
242
- text
93
+ .display("flex")
94
+ .padding(20)
95
+ .color("red")
96
+ .$el("my-component");
97
+ ```
243
98
 
99
+ ---
244
100
 
245
- ### Responsive Design
101
+ ## Shorthands
246
102
 
103
+ ```ts
247
104
  chain()
248
- .display("flex")
249
- .flexDirection("column")
250
- .responsive("md", c => c.flexDirection("row"))
251
- .media("(min-width: 1024px)", c => c.maxWidth(1200))
252
- .$el("responsive");
253
- text
254
-
105
+ .bg("#fff")
106
+ .m(16)
107
+ .p(20)
108
+ .br(8)
109
+ .fs(16)
110
+ .fw(700)
111
+ .c("#333")
112
+ .w(200)
113
+ .h(100)
114
+ .d("flex");
115
+ ```
255
116
 
256
- Built-in breakpoints: sm, md, lg, xl, 2xl, mobile, tablet, desktop, dark, light, reducedMotion, highContrast, print, hover, portrait, landscape.
257
-
258
- ### Transform Methods
117
+ ---
259
118
 
260
- chain().scale(1.1).rotate("45deg").x(10).y(20).$el("transformed");
261
- text
119
+ ## Macros
262
120
 
121
+ ### Layout
263
122
 
264
- ### Math Helpers
123
+ ```ts
124
+ chain().flex();
125
+ chain().grid();
126
+ chain().center();
127
+ chain().flexCenter();
128
+ chain().gridCenter();
129
+ chain().stack(16);
130
+ chain().cols(3);
131
+ ```
265
132
 
266
- chain()
267
- .width(chain().calc("100% - 20px"))
268
- .fontSize(chain().clamp(16, 4, 24))
269
- .margin(chain().add(10, 20))
270
- .$el("calculated");
271
- text
133
+ ### Spacing
272
134
 
135
+ ```ts
136
+ chain().mx(10);
137
+ chain().my(20);
138
+ chain().px(16);
139
+ chain().py(24);
140
+ chain().size(50);
141
+ chain().gap(16);
142
+ ```
273
143
 
274
144
  ---
275
145
 
276
- ## Recipe System (Variants)
277
-
278
- import { recipe } from "chaincss";
279
-
280
- const button = recipe({
281
- base: {
282
- selectors: ["btn"],
283
- display: "inline-flex",
284
- borderRadius: "8px",
285
- fontWeight: 600,
286
- },
287
- variants: {
288
- size: {
289
- sm: { padding: "8px 16px", fontSize: "14px" },
290
- md: { padding: "12px 24px", fontSize: "16px" },
291
- lg: { padding: "16px 32px", fontSize: "18px" },
292
- },
293
- color: {
294
- primary: { background: "#3b82f6", color: "white" },
295
- danger: { background: "#ef4444", color: "white" },
296
- },
297
- },
298
- defaultVariants: { size: "md", color: "primary" },
299
- compoundVariants: [
300
- {
301
- variants: { size: "lg", color: "primary" },
302
- style: { fontWeight: 800 },
303
- },
304
- ],
305
- });
146
+ ## States & Interactions
306
147
 
307
- const styles = button({ size: "lg", color: "danger" });
308
- text
309
-
310
-
311
- ---
312
-
313
- ## Animations (42 built-in presets)
314
-
315
- chain()
316
- .fadeIn()
317
- .slideInUp()
318
- .zoomIn()
319
- .bounce()
320
- .pulse()
321
- .spin()
322
- .shake()
323
- .wiggle()
324
- .float()
325
- .flash()
326
- .textReveal()
327
- .$el("animated");
328
-
329
- // Custom animation
148
+ ```ts
330
149
  chain()
331
- .animate("myKeyframes", {
332
- "0%": { opacity: 0, transform: "scale(0.5)" },
333
- "100%": { opacity: 1, transform: "scale(1)" },
334
- }, { duration: "0.5s", timing: "ease-out" })
335
- .$el("custom-anim");
336
- text
337
-
150
+ .hover()
151
+ .background("#2563eb")
152
+ .transform("scale(1.05)")
153
+ .end();
154
+ ```
338
155
 
339
156
  ---
340
157
 
341
- ## Design Tokens
342
-
343
- import { createTokens } from "chaincss";
344
-
345
- const tokens = createTokens({
346
- colors: { primary: "#3b82f6", secondary: "#10b981", danger: "#ef4444" },
347
- spacing: { sm: "8px", md: "16px", lg: "24px" },
348
- });
349
-
350
- chain(true)
351
- .color("colors.primary").padding("colors.primary").padding("spacing.md")
352
- .$el("themed");
353
- text
158
+ ## Conditional Styles
354
159
 
160
+ ```ts
161
+ chain()
162
+ .padding(12)
163
+ .when(isActive, c => c.background("#10b981").color("white"))
164
+ .when(isDisabled, c => c.opacity(0.5).cursor("not-allowed"))
165
+ .$el("stateful-btn");
166
+ ```
355
167
 
356
168
  ---
357
169
 
358
- ## Theme Contract
359
-
360
- import { createThemeContract, createTheme, validateTheme } from "chaincss";
361
-
362
- const contract = createThemeContract({
363
- colors: { primary: "", background: "", text: "" },
364
- });
365
-
366
- const lightTheme = createTheme(contract, {
367
- colors: { primary: "#3b82f6", background: "#ffffff", text: "#1a202c" },
368
- });
369
-
370
- const darkTheme = createTheme(contract, {
371
- colors: { primary: "#60a5fa", background: "#1a202c", text: "#f7fafc" },
372
- });
373
- text
170
+ ## Nested Selectors
374
171
 
172
+ ```ts
173
+ chain()
174
+ .display("flex")
175
+ .nest("& > *", c => c.flex(1))
176
+ .nest(".child", c => c.color("red"))
177
+ .$el("flex-container");
178
+ ```
375
179
 
376
180
  ---
377
181
 
378
- ## Framework Integration
379
-
380
- ### React
381
-
382
- import { chain } from "chaincss";
383
-
384
- function Card() {
385
- const styles = chain().bg("white").p(24).rounded(12).$el("card");
386
- return <div className={styles.selectors[0]}>Content</div>;
387
- }
388
- text
389
-
390
-
391
- ### Vue
392
-
393
- import { chain } from "chaincss";
394
- const styles = chain().display("grid").cols(3).gap(16).$el("vue-grid");
395
- // <div :class="styles.selectors[0]">
396
- text
397
-
398
-
399
- ### Svelte
400
-
401
- import { chain } from "chaincss";
402
- const styles = chain().flex().center().$el("centered");
403
- // <div class={styles.selectors[0]}>Content</div>
404
- text
182
+ ## Responsive
405
183
 
184
+ ```ts
185
+ chain()
186
+ .display("flex")
187
+ .flexDirection("column")
188
+ .responsive("md", c => c.flexDirection("row"))
189
+ .$el("responsive");
190
+ ```
406
191
 
407
192
  ---
408
193
 
409
- ## Smart Chain (Auto-Detection)
410
-
411
- import { smartChain, buildChain, runtimeChain } from "chaincss";
412
-
413
- // Auto-detect
414
- const styles = smartChain()
415
- .display("flex") // static -> build
416
- .color(dynamicVariable) // dynamic -> runtime
417
- .$el("hybrid");
418
-
419
- // Force build-time only
420
- const static = buildChain().display("flex").$el("static");
421
-
422
- // Force runtime only
423
- const dynamic = runtimeChain().color(props.color).$el("runtime");
424
- text
194
+ ## Animations
425
195
 
196
+ ```ts
197
+ chain()
198
+ .fadeIn()
199
+ .slideInUp()
200
+ .zoomIn()
201
+ .$el("animated");
202
+ ```
426
203
 
427
204
  ---
428
205
 
429
206
  ## CLI
430
207
 
431
- chaincss init # Create config file
432
- chaincss build # Build all styles
433
- chaincss watch # Watch for changes
434
- chaincss cache clear # Clear cache
435
- chaincss cache stats # Cache statistics
436
- chaincss timeline list # Style history
437
- text
438
-
208
+ ```bash
209
+ chaincss init
210
+ chaincss build
211
+ chaincss watch
212
+ chaincss cache clear
213
+ chaincss cache stats
214
+ ```
439
215
 
440
216
  ---
441
217
 
442
218
  ## Configuration
443
219
 
444
- // chaincss.config.js
220
+ ```ts
445
221
  export default {
446
- inputs: ["src//*.chain.{js,ts}", "src//*.tsx"],
447
- output: { cssFile: "global.css", minify: true },
448
- atomic: { enabled: true, mode: "hybrid", threshold: 2, naming: "hash" },
449
- breakpoints: { sm: "(min-width: 640px)", md: "(min-width: 768px)", lg: "(min-width: 1024px)" },
450
- tokens: { enabled: true, prefix: "$" },
222
+ inputs: ["src/**/*.chain.{js,ts}", "src/**/*.tsx"],
223
+ output: { cssFile: "global.css", minify: true },
451
224
  };
452
- text
453
-
454
-
455
- ---
456
-
457
- ## Complete Feature List
458
-
459
- | Category | Count |
460
- |----------|-------|
461
- | Macros | 57 |
462
- | Shorthand Properties | 80 |
463
- | Animation Presets | 42 |
464
- | Breakpoints | 20 |
465
- | Chain API Methods | 30+ |
466
- | Math Helpers | 15 |
467
- | CSS Properties | 300+ |
468
- | CLI Commands | 5 |
469
- | Framework Integrations | 4 (React, Vue, Svelte, Solid) |
470
- | Plugins | 2 (Vite, Webpack) |
471
- | React Hooks | 6 |
472
- | Recipe System | Variants, defaultVariants, compoundVariants |
473
- | Theme System | Theme, createThemeContract, validateTheme |
474
-
475
- **Total: 550+ features | 210 tests | Zero-runtime by default**
225
+ ```
476
226
 
477
227
  ---
478
228
 
479
229
  ## Contributing
480
230
 
231
+ ```bash
481
232
  git clone https://github.com/melcanz08/chaincss.git
482
233
  cd chaincss
483
234
  npm install
484
235
  npm test
485
- text
486
-
236
+ ```
487
237
 
488
238
  ---
489
239
 
@@ -492,6 +242,6 @@ text
492
242
  MIT © Rommel Caneos
493
243
 
494
244
  <p align="center">
495
- <strong>ChainCSS</strong> — Write styles like JavaScript. Ship zero runtime.<br>
496
- <a href="https://chaincss.dev">chaincss.dev</a>
245
+ <strong>ChainCSS</strong> — Write styles like JavaScript. Ship zero runtime.
497
246
  </p>
247
+
@@ -22,3 +22,4 @@ export declare const chainStyles: (...args: any[]) => any;
22
22
  export { setupHMR, registerForHMR } from './hmr.js';
23
23
  export { generateStyleId, hashString, kebabCase, isBrowser, isDevelopment, isProduction, debounce, memoize, cn as cnUtils, devWarn, devLog, logError, createDebugger } from './utils.js';
24
24
  export type { RuntimeStyleDefinition, UseChainStylesOptions, RuntimeCompiledResult, StyleInjector, UseAtomicClassesReturn, HMRPayload, ChainCSSDebugger } from './types.js';
25
+ export declare function injectChainStyles(styles: Record<string, any>): HTMLStyleElement;
@@ -3907,6 +3907,36 @@ var useComputedStylesSvelte = (...args) => getSvelteExports().useComputedStyles?
3907
3907
  var provideStyleContextSvelte = (...args) => getSvelteExports().provideStyleContext?.(...args);
3908
3908
  var injectStyleContextSvelte = (...args) => getSvelteExports().injectStyleContext?.(...args);
3909
3909
  var chainStyles2 = (...args) => getSvelteExports().chainStyles?.(...args);
3910
+ function injectChainStyles(styles3) {
3911
+ let css = "";
3912
+ for (const [key, obj] of Object.entries(styles3)) {
3913
+ if (!obj || !obj.selectors) continue;
3914
+ const sel = "." + obj.selectors[0];
3915
+ css += sel + "{";
3916
+ for (const [k, v] of Object.entries(obj)) {
3917
+ if (["selectors", "hover", "atRules", "nestedRules", "_name", "_classes"].includes(k)) continue;
3918
+ css += k.replace(/([A-Z])/g, "-$1").toLowerCase() + ":" + v + ";";
3919
+ }
3920
+ css += "}";
3921
+ if (obj.hover) {
3922
+ css += sel + ":hover{";
3923
+ for (const [k, v] of Object.entries(obj.hover)) css += k.replace(/([A-Z])/g, "-$1").toLowerCase() + ":" + v + ";";
3924
+ css += "}";
3925
+ }
3926
+ if (obj.nestedRules) {
3927
+ for (const rule of obj.nestedRules) {
3928
+ css += rule.selector.replace("&", sel) + "{";
3929
+ for (const [k, v] of Object.entries(rule.styles || {})) css += k.replace(/([A-Z])/g, "-$1").toLowerCase() + ":" + v + ";";
3930
+ css += "}";
3931
+ }
3932
+ }
3933
+ }
3934
+ const el = document.createElement("style");
3935
+ el.setAttribute("data-chaincss", "runtime");
3936
+ el.textContent = css;
3937
+ document.head.appendChild(el);
3938
+ return el;
3939
+ }
3910
3940
  export {
3911
3941
  $,
3912
3942
  $t,
@@ -3932,6 +3962,7 @@ export {
3932
3962
  enableChainCSSDebug,
3933
3963
  generateStyleId,
3934
3964
  hashString,
3965
+ injectChainStyles,
3935
3966
  injectStyleContext,
3936
3967
  injectStyleContextSvelte,
3937
3968
  isBrowser,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaincss",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "ChainCSS v3.0 - 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",
@@ -88,4 +88,35 @@ export type {
88
88
  UseAtomicClassesReturn,
89
89
  HMRPayload,
90
90
  ChainCSSDebugger
91
- } from './types.js';
91
+ } from './types.js';
92
+ // Auto-inject styles into DOM
93
+ export function injectChainStyles(styles: Record<string, any>) {
94
+ let css = '';
95
+ for (const [key, obj] of Object.entries(styles)) {
96
+ if (!obj || !obj.selectors) continue;
97
+ const sel = '.' + obj.selectors[0];
98
+ css += sel + '{';
99
+ for (const [k, v] of Object.entries(obj)) {
100
+ if (['selectors','hover','atRules','nestedRules','_name','_classes'].includes(k)) continue;
101
+ css += k.replace(/([A-Z])/g,'-$1').toLowerCase() + ':' + v + ';';
102
+ }
103
+ css += '}';
104
+ if (obj.hover) {
105
+ css += sel + ':hover{';
106
+ for (const [k, v] of Object.entries(obj.hover)) css += k.replace(/([A-Z])/g,'-$1').toLowerCase() + ':' + v + ';';
107
+ css += '}';
108
+ }
109
+ if (obj.nestedRules) {
110
+ for (const rule of obj.nestedRules) {
111
+ css += rule.selector.replace('&', sel) + '{';
112
+ for (const [k, v] of Object.entries(rule.styles || {})) css += k.replace(/([A-Z])/g,'-$1').toLowerCase() + ':' + v + ';';
113
+ css += '}';
114
+ }
115
+ }
116
+ }
117
+ const el = document.createElement('style');
118
+ el.setAttribute('data-chaincss', 'runtime');
119
+ el.textContent = css;
120
+ document.head.appendChild(el);
121
+ return el;
122
+ }