@kbach/react 0.6.11 → 0.6.13
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 +27 -0
- package/dist/{chunk-Z5SUFJ53.mjs → chunk-7DHBNCL5.mjs} +1 -1
- package/dist/{chunk-OWJUMH2V.mjs → chunk-HCRWLOJE.mjs} +83 -25
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +82 -24
- package/dist/index.mjs +1 -1
- package/dist/jsx-dev-runtime.js +83 -25
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.js +83 -25
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/vite-plugin.d.mts +17 -0
- package/dist/vite-plugin.d.ts +17 -0
- package/dist/vite-plugin.js +86 -28
- package/dist/vite-plugin.mjs +86 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -362,6 +362,18 @@ Nested groups need names, or the inner element reacts to whichever `.group` is n
|
|
|
362
362
|
<div className="text-[18px]" />
|
|
363
363
|
```
|
|
364
364
|
|
|
365
|
+
### Arbitrary properties (web only)
|
|
366
|
+
|
|
367
|
+
For the rare CSS property with no named utility, write `[property:value]` directly — no utility prefix:
|
|
368
|
+
|
|
369
|
+
```jsx
|
|
370
|
+
<div className="[mask-type:luminance]" />
|
|
371
|
+
<div className="[-webkit-line-clamp:3]" />
|
|
372
|
+
<div className="[--my-var:10px]" /> {/* CSS custom property */}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Underscores become spaces, same as any other arbitrary value: `[background:url(/a.png)_no-repeat]`. Only the first `:` splits the property from the value, so a value that itself contains a colon (a URL's `http://`) still comes through intact.
|
|
376
|
+
|
|
365
377
|
## Color system
|
|
366
378
|
|
|
367
379
|
12-shade scale — 1 lightest, 12 darkest. Use as `bg-blue-6`, `text-gray-10`, `border-red-4/50`.
|
|
@@ -406,6 +418,15 @@ module.exports = {
|
|
|
406
418
|
sans: 'Inter, sans-serif',
|
|
407
419
|
'sans-bold': 'Inter_700Bold, sans-serif',
|
|
408
420
|
},
|
|
421
|
+
// Custom @keyframes (web only) — reference by name from `animation`,
|
|
422
|
+
// then use that name as animate-{name}. Declaration values are plain
|
|
423
|
+
// CSS strings, same idea as a style object but for a keyframe step.
|
|
424
|
+
keyframes: {
|
|
425
|
+
wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } },
|
|
426
|
+
},
|
|
427
|
+
animation: {
|
|
428
|
+
wiggle: 'wiggle 1s ease-in-out infinite',
|
|
429
|
+
},
|
|
409
430
|
},
|
|
410
431
|
|
|
411
432
|
plugins: [
|
|
@@ -422,6 +443,12 @@ module.exports = {
|
|
|
422
443
|
|
|
423
444
|
Setting `fontFamily.sans` to anything other than `'System'` auto-injects `body { font-family: … }`.
|
|
424
445
|
|
|
446
|
+
```jsx
|
|
447
|
+
<div className="animate-wiggle" />
|
|
448
|
+
{/* Override duration/timing/repeat inline, keeps the same wiggle @keyframes: */}
|
|
449
|
+
<div className="animate-[wiggle_2s_ease-in-out]" />
|
|
450
|
+
```
|
|
451
|
+
|
|
425
452
|
Color aliases:
|
|
426
453
|
|
|
427
454
|
```js
|
|
@@ -540,7 +540,13 @@ var defaultTheme = {
|
|
|
540
540
|
xl: 1280,
|
|
541
541
|
// desktop
|
|
542
542
|
"2xl": 1536
|
|
543
|
-
}
|
|
543
|
+
},
|
|
544
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
545
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
546
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
547
|
+
// alongside them without needing to redeclare the built-ins.
|
|
548
|
+
keyframes: {},
|
|
549
|
+
animation: {}
|
|
544
550
|
};
|
|
545
551
|
|
|
546
552
|
// src/core/platform.ts
|
|
@@ -1240,6 +1246,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1240
1246
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1241
1247
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1242
1248
|
};
|
|
1249
|
+
function keyframeDeclToCSS(decl) {
|
|
1250
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1251
|
+
}
|
|
1252
|
+
function buildKeyframeCSS(name, steps) {
|
|
1253
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1254
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1255
|
+
}
|
|
1256
|
+
var BUILTIN_ANIMATIONS = {
|
|
1257
|
+
none: { animation: "none" },
|
|
1258
|
+
spin: {
|
|
1259
|
+
animation: "kb-spin 1s linear infinite",
|
|
1260
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1261
|
+
},
|
|
1262
|
+
ping: {
|
|
1263
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1264
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1265
|
+
},
|
|
1266
|
+
pulse: {
|
|
1267
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1268
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1269
|
+
},
|
|
1270
|
+
bounce: {
|
|
1271
|
+
animation: "kb-bounce 1s infinite",
|
|
1272
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1276
|
+
const words = animation.trim().split(/\s+/);
|
|
1277
|
+
const name = words[0];
|
|
1278
|
+
if (!name) return { animation };
|
|
1279
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1280
|
+
if (builtin?.__keyframe) {
|
|
1281
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1282
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1283
|
+
}
|
|
1284
|
+
const steps = keyframes?.[name];
|
|
1285
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1286
|
+
}
|
|
1243
1287
|
var RESOLVERS = {
|
|
1244
1288
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1245
1289
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1793,6 +1837,38 @@ var RESOLVERS = {
|
|
|
1793
1837
|
};
|
|
1794
1838
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1795
1839
|
},
|
|
1840
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
1841
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
1842
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
1843
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
1844
|
+
content: ({ value, isArbitrary }) => {
|
|
1845
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1846
|
+
if (isArbitrary) return { content: value };
|
|
1847
|
+
const presets = { none: "none" };
|
|
1848
|
+
return value in presets ? { content: presets[value] } : null;
|
|
1849
|
+
},
|
|
1850
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
1851
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
1852
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
1853
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
1854
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
1855
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
1856
|
+
//
|
|
1857
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
1858
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
1859
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
1860
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
1861
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
1862
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
1863
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
1864
|
+
if (negative) return null;
|
|
1865
|
+
const colonIdx = value.indexOf(":");
|
|
1866
|
+
if (colonIdx <= 0) return null;
|
|
1867
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
1868
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
1869
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
1870
|
+
return { [prop]: cssValue };
|
|
1871
|
+
},
|
|
1796
1872
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1797
1873
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1798
1874
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1986,30 +2062,12 @@ var RESOLVERS = {
|
|
|
1986
2062
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
1987
2063
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
1988
2064
|
// so it never reaches element inline styles.
|
|
1989
|
-
animate: ({ value, isArbitrary }) => {
|
|
1990
|
-
if (!getEffectiveIsWeb()) return null;
|
|
1991
|
-
if (isArbitrary) return
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
animation: "kb-spin 1s linear infinite",
|
|
1996
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1997
|
-
},
|
|
1998
|
-
ping: {
|
|
1999
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
2000
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
2001
|
-
},
|
|
2002
|
-
pulse: {
|
|
2003
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
2004
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
2005
|
-
},
|
|
2006
|
-
bounce: {
|
|
2007
|
-
animation: "kb-bounce 1s infinite",
|
|
2008
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
2009
|
-
}
|
|
2010
|
-
};
|
|
2011
|
-
const def = defs[value];
|
|
2012
|
-
return def ? def : null;
|
|
2065
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
2066
|
+
if (!getEffectiveIsWeb()) return null;
|
|
2067
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
2068
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
2069
|
+
const custom = theme.animation?.[value];
|
|
2070
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
2013
2071
|
},
|
|
2014
2072
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
2015
2073
|
"line-clamp": ({ value }) => {
|
package/dist/index.d.mts
CHANGED
|
@@ -90,6 +90,23 @@ interface ThemeConfig {
|
|
|
90
90
|
flex: Record<string, number | string>;
|
|
91
91
|
shadow: Record<string, StyleValue>;
|
|
92
92
|
screens: Record<string, string | number>;
|
|
93
|
+
/**
|
|
94
|
+
* Custom @keyframes, web only. Each key is a keyframe name, its value maps
|
|
95
|
+
* percentage/from/to selectors to a plain CSS declaration object (camelCase
|
|
96
|
+
* properties, same shape as an inline style object):
|
|
97
|
+
* keyframes: { wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } } }
|
|
98
|
+
* Referenced from `animation` below, or directly via animate-[wiggle_1s_ease-in-out].
|
|
99
|
+
*/
|
|
100
|
+
keyframes: Record<string, Record<string, StyleValue>>;
|
|
101
|
+
/**
|
|
102
|
+
* Named animation shorthands built on `keyframes` above, referenced via
|
|
103
|
+
* animate-{name} (e.g. animate-wiggle):
|
|
104
|
+
* animation: { wiggle: 'wiggle 1s ease-in-out infinite' }
|
|
105
|
+
* The first word must match a `keyframes` key so its @keyframes rule can be
|
|
106
|
+
* injected alongside the animation — a name with no matching keyframes entry
|
|
107
|
+
* still sets the `animation` CSS property, it just won't animate anything.
|
|
108
|
+
*/
|
|
109
|
+
animation: Record<string, string>;
|
|
93
110
|
[key: string]: unknown;
|
|
94
111
|
}
|
|
95
112
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -90,6 +90,23 @@ interface ThemeConfig {
|
|
|
90
90
|
flex: Record<string, number | string>;
|
|
91
91
|
shadow: Record<string, StyleValue>;
|
|
92
92
|
screens: Record<string, string | number>;
|
|
93
|
+
/**
|
|
94
|
+
* Custom @keyframes, web only. Each key is a keyframe name, its value maps
|
|
95
|
+
* percentage/from/to selectors to a plain CSS declaration object (camelCase
|
|
96
|
+
* properties, same shape as an inline style object):
|
|
97
|
+
* keyframes: { wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } } }
|
|
98
|
+
* Referenced from `animation` below, or directly via animate-[wiggle_1s_ease-in-out].
|
|
99
|
+
*/
|
|
100
|
+
keyframes: Record<string, Record<string, StyleValue>>;
|
|
101
|
+
/**
|
|
102
|
+
* Named animation shorthands built on `keyframes` above, referenced via
|
|
103
|
+
* animate-{name} (e.g. animate-wiggle):
|
|
104
|
+
* animation: { wiggle: 'wiggle 1s ease-in-out infinite' }
|
|
105
|
+
* The first word must match a `keyframes` key so its @keyframes rule can be
|
|
106
|
+
* injected alongside the animation — a name with no matching keyframes entry
|
|
107
|
+
* still sets the `animation` CSS property, it just won't animate anything.
|
|
108
|
+
*/
|
|
109
|
+
animation: Record<string, string>;
|
|
93
110
|
[key: string]: unknown;
|
|
94
111
|
}
|
|
95
112
|
/**
|
package/dist/index.js
CHANGED
|
@@ -625,7 +625,13 @@ var defaultTheme = {
|
|
|
625
625
|
xl: 1280,
|
|
626
626
|
// desktop
|
|
627
627
|
"2xl": 1536
|
|
628
|
-
}
|
|
628
|
+
},
|
|
629
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
630
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
631
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
632
|
+
// alongside them without needing to redeclare the built-ins.
|
|
633
|
+
keyframes: {},
|
|
634
|
+
animation: {}
|
|
629
635
|
};
|
|
630
636
|
|
|
631
637
|
// src/core/reset.ts
|
|
@@ -1401,6 +1407,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1401
1407
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1402
1408
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1403
1409
|
};
|
|
1410
|
+
function keyframeDeclToCSS(decl) {
|
|
1411
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1412
|
+
}
|
|
1413
|
+
function buildKeyframeCSS(name, steps) {
|
|
1414
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1415
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1416
|
+
}
|
|
1417
|
+
var BUILTIN_ANIMATIONS = {
|
|
1418
|
+
none: { animation: "none" },
|
|
1419
|
+
spin: {
|
|
1420
|
+
animation: "kb-spin 1s linear infinite",
|
|
1421
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1422
|
+
},
|
|
1423
|
+
ping: {
|
|
1424
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1425
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1426
|
+
},
|
|
1427
|
+
pulse: {
|
|
1428
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1429
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1430
|
+
},
|
|
1431
|
+
bounce: {
|
|
1432
|
+
animation: "kb-bounce 1s infinite",
|
|
1433
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1437
|
+
const words = animation.trim().split(/\s+/);
|
|
1438
|
+
const name = words[0];
|
|
1439
|
+
if (!name) return { animation };
|
|
1440
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1441
|
+
if (builtin?.__keyframe) {
|
|
1442
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1443
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1444
|
+
}
|
|
1445
|
+
const steps = keyframes?.[name];
|
|
1446
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1447
|
+
}
|
|
1404
1448
|
var RESOLVERS = {
|
|
1405
1449
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1406
1450
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1954,6 +1998,38 @@ var RESOLVERS = {
|
|
|
1954
1998
|
};
|
|
1955
1999
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1956
2000
|
},
|
|
2001
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
2002
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
2003
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
2004
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
2005
|
+
content: ({ value, isArbitrary }) => {
|
|
2006
|
+
if (!getEffectiveIsWeb()) return null;
|
|
2007
|
+
if (isArbitrary) return { content: value };
|
|
2008
|
+
const presets = { none: "none" };
|
|
2009
|
+
return value in presets ? { content: presets[value] } : null;
|
|
2010
|
+
},
|
|
2011
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
2012
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
2013
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
2014
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
2015
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
2016
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
2017
|
+
//
|
|
2018
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
2019
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
2020
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
2021
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
2022
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
2023
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
2024
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
2025
|
+
if (negative) return null;
|
|
2026
|
+
const colonIdx = value.indexOf(":");
|
|
2027
|
+
if (colonIdx <= 0) return null;
|
|
2028
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
2029
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
2030
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
2031
|
+
return { [prop]: cssValue };
|
|
2032
|
+
},
|
|
1957
2033
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1958
2034
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1959
2035
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2147,30 +2223,12 @@ var RESOLVERS = {
|
|
|
2147
2223
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
2148
2224
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
2149
2225
|
// so it never reaches element inline styles.
|
|
2150
|
-
animate: ({ value, isArbitrary }) => {
|
|
2226
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
2151
2227
|
if (!getEffectiveIsWeb()) return null;
|
|
2152
|
-
if (isArbitrary) return
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
animation: "kb-spin 1s linear infinite",
|
|
2157
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
2158
|
-
},
|
|
2159
|
-
ping: {
|
|
2160
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
2161
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
2162
|
-
},
|
|
2163
|
-
pulse: {
|
|
2164
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
2165
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
2166
|
-
},
|
|
2167
|
-
bounce: {
|
|
2168
|
-
animation: "kb-bounce 1s infinite",
|
|
2169
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
2170
|
-
}
|
|
2171
|
-
};
|
|
2172
|
-
const def = defs[value];
|
|
2173
|
-
return def ? def : null;
|
|
2228
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
2229
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
2230
|
+
const custom = theme.animation?.[value];
|
|
2231
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
2174
2232
|
},
|
|
2175
2233
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
2176
2234
|
"line-clamp": ({ value }) => {
|
package/dist/index.mjs
CHANGED
package/dist/jsx-dev-runtime.js
CHANGED
|
@@ -580,7 +580,13 @@ var defaultTheme = {
|
|
|
580
580
|
xl: 1280,
|
|
581
581
|
// desktop
|
|
582
582
|
"2xl": 1536
|
|
583
|
-
}
|
|
583
|
+
},
|
|
584
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
585
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
586
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
587
|
+
// alongside them without needing to redeclare the built-ins.
|
|
588
|
+
keyframes: {},
|
|
589
|
+
animation: {}
|
|
584
590
|
};
|
|
585
591
|
|
|
586
592
|
// src/core/reset.ts
|
|
@@ -1359,6 +1365,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1359
1365
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1360
1366
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1361
1367
|
};
|
|
1368
|
+
function keyframeDeclToCSS(decl) {
|
|
1369
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1370
|
+
}
|
|
1371
|
+
function buildKeyframeCSS(name, steps) {
|
|
1372
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1373
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1374
|
+
}
|
|
1375
|
+
var BUILTIN_ANIMATIONS = {
|
|
1376
|
+
none: { animation: "none" },
|
|
1377
|
+
spin: {
|
|
1378
|
+
animation: "kb-spin 1s linear infinite",
|
|
1379
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1380
|
+
},
|
|
1381
|
+
ping: {
|
|
1382
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1383
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1384
|
+
},
|
|
1385
|
+
pulse: {
|
|
1386
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1387
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1388
|
+
},
|
|
1389
|
+
bounce: {
|
|
1390
|
+
animation: "kb-bounce 1s infinite",
|
|
1391
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1395
|
+
const words = animation.trim().split(/\s+/);
|
|
1396
|
+
const name = words[0];
|
|
1397
|
+
if (!name) return { animation };
|
|
1398
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1399
|
+
if (builtin?.__keyframe) {
|
|
1400
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1401
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1402
|
+
}
|
|
1403
|
+
const steps = keyframes?.[name];
|
|
1404
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1405
|
+
}
|
|
1362
1406
|
var RESOLVERS = {
|
|
1363
1407
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1364
1408
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1912,6 +1956,38 @@ var RESOLVERS = {
|
|
|
1912
1956
|
};
|
|
1913
1957
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1914
1958
|
},
|
|
1959
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
1960
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
1961
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
1962
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
1963
|
+
content: ({ value, isArbitrary }) => {
|
|
1964
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1965
|
+
if (isArbitrary) return { content: value };
|
|
1966
|
+
const presets = { none: "none" };
|
|
1967
|
+
return value in presets ? { content: presets[value] } : null;
|
|
1968
|
+
},
|
|
1969
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
1970
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
1971
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
1972
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
1973
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
1974
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
1975
|
+
//
|
|
1976
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
1977
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
1978
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
1979
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
1980
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
1981
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
1982
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
1983
|
+
if (negative) return null;
|
|
1984
|
+
const colonIdx = value.indexOf(":");
|
|
1985
|
+
if (colonIdx <= 0) return null;
|
|
1986
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
1987
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
1988
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
1989
|
+
return { [prop]: cssValue };
|
|
1990
|
+
},
|
|
1915
1991
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1916
1992
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1917
1993
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2105,30 +2181,12 @@ var RESOLVERS = {
|
|
|
2105
2181
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
2106
2182
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
2107
2183
|
// so it never reaches element inline styles.
|
|
2108
|
-
animate: ({ value, isArbitrary }) => {
|
|
2109
|
-
if (!getEffectiveIsWeb()) return null;
|
|
2110
|
-
if (isArbitrary) return
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
animation: "kb-spin 1s linear infinite",
|
|
2115
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
2116
|
-
},
|
|
2117
|
-
ping: {
|
|
2118
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
2119
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
2120
|
-
},
|
|
2121
|
-
pulse: {
|
|
2122
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
2123
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
2124
|
-
},
|
|
2125
|
-
bounce: {
|
|
2126
|
-
animation: "kb-bounce 1s infinite",
|
|
2127
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
2128
|
-
}
|
|
2129
|
-
};
|
|
2130
|
-
const def = defs[value];
|
|
2131
|
-
return def ? def : null;
|
|
2184
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
2185
|
+
if (!getEffectiveIsWeb()) return null;
|
|
2186
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
2187
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
2188
|
+
const custom = theme.animation?.[value];
|
|
2189
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
2132
2190
|
},
|
|
2133
2191
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
2134
2192
|
"line-clamp": ({ value }) => {
|
package/dist/jsx-dev-runtime.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
Fragment,
|
|
4
4
|
jsx,
|
|
5
5
|
jsxs
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-7DHBNCL5.mjs";
|
|
7
|
+
import "./chunk-HCRWLOJE.mjs";
|
|
8
8
|
|
|
9
9
|
// src/jsx-dev-runtime.tsx
|
|
10
10
|
function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -581,7 +581,13 @@ var defaultTheme = {
|
|
|
581
581
|
xl: 1280,
|
|
582
582
|
// desktop
|
|
583
583
|
"2xl": 1536
|
|
584
|
-
}
|
|
584
|
+
},
|
|
585
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
586
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
587
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
588
|
+
// alongside them without needing to redeclare the built-ins.
|
|
589
|
+
keyframes: {},
|
|
590
|
+
animation: {}
|
|
585
591
|
};
|
|
586
592
|
|
|
587
593
|
// src/core/reset.ts
|
|
@@ -1360,6 +1366,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1360
1366
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1361
1367
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1362
1368
|
};
|
|
1369
|
+
function keyframeDeclToCSS(decl) {
|
|
1370
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1371
|
+
}
|
|
1372
|
+
function buildKeyframeCSS(name, steps) {
|
|
1373
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1374
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1375
|
+
}
|
|
1376
|
+
var BUILTIN_ANIMATIONS = {
|
|
1377
|
+
none: { animation: "none" },
|
|
1378
|
+
spin: {
|
|
1379
|
+
animation: "kb-spin 1s linear infinite",
|
|
1380
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1381
|
+
},
|
|
1382
|
+
ping: {
|
|
1383
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1384
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1385
|
+
},
|
|
1386
|
+
pulse: {
|
|
1387
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1388
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1389
|
+
},
|
|
1390
|
+
bounce: {
|
|
1391
|
+
animation: "kb-bounce 1s infinite",
|
|
1392
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1396
|
+
const words = animation.trim().split(/\s+/);
|
|
1397
|
+
const name = words[0];
|
|
1398
|
+
if (!name) return { animation };
|
|
1399
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1400
|
+
if (builtin?.__keyframe) {
|
|
1401
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1402
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1403
|
+
}
|
|
1404
|
+
const steps = keyframes?.[name];
|
|
1405
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1406
|
+
}
|
|
1363
1407
|
var RESOLVERS = {
|
|
1364
1408
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1365
1409
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1913,6 +1957,38 @@ var RESOLVERS = {
|
|
|
1913
1957
|
};
|
|
1914
1958
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1915
1959
|
},
|
|
1960
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
1961
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
1962
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
1963
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
1964
|
+
content: ({ value, isArbitrary }) => {
|
|
1965
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1966
|
+
if (isArbitrary) return { content: value };
|
|
1967
|
+
const presets = { none: "none" };
|
|
1968
|
+
return value in presets ? { content: presets[value] } : null;
|
|
1969
|
+
},
|
|
1970
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
1971
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
1972
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
1973
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
1974
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
1975
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
1976
|
+
//
|
|
1977
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
1978
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
1979
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
1980
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
1981
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
1982
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
1983
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
1984
|
+
if (negative) return null;
|
|
1985
|
+
const colonIdx = value.indexOf(":");
|
|
1986
|
+
if (colonIdx <= 0) return null;
|
|
1987
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
1988
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
1989
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
1990
|
+
return { [prop]: cssValue };
|
|
1991
|
+
},
|
|
1916
1992
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1917
1993
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1918
1994
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2106,30 +2182,12 @@ var RESOLVERS = {
|
|
|
2106
2182
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
2107
2183
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
2108
2184
|
// so it never reaches element inline styles.
|
|
2109
|
-
animate: ({ value, isArbitrary }) => {
|
|
2110
|
-
if (!getEffectiveIsWeb()) return null;
|
|
2111
|
-
if (isArbitrary) return
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
animation: "kb-spin 1s linear infinite",
|
|
2116
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
2117
|
-
},
|
|
2118
|
-
ping: {
|
|
2119
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
2120
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
2121
|
-
},
|
|
2122
|
-
pulse: {
|
|
2123
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
2124
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
2125
|
-
},
|
|
2126
|
-
bounce: {
|
|
2127
|
-
animation: "kb-bounce 1s infinite",
|
|
2128
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
2129
|
-
}
|
|
2130
|
-
};
|
|
2131
|
-
const def = defs[value];
|
|
2132
|
-
return def ? def : null;
|
|
2185
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
2186
|
+
if (!getEffectiveIsWeb()) return null;
|
|
2187
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
2188
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
2189
|
+
const custom = theme.animation?.[value];
|
|
2190
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
2133
2191
|
},
|
|
2134
2192
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
2135
2193
|
"line-clamp": ({ value }) => {
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
_invalidateDefaultFontCache,
|
|
5
5
|
jsx,
|
|
6
6
|
jsxs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7DHBNCL5.mjs";
|
|
8
8
|
import {
|
|
9
9
|
registerWebElement
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HCRWLOJE.mjs";
|
|
11
11
|
export {
|
|
12
12
|
Fragment,
|
|
13
13
|
_invalidateDefaultFontCache,
|
package/dist/vite-plugin.d.mts
CHANGED
|
@@ -65,6 +65,23 @@ interface ThemeConfig {
|
|
|
65
65
|
flex: Record<string, number | string>;
|
|
66
66
|
shadow: Record<string, StyleValue>;
|
|
67
67
|
screens: Record<string, string | number>;
|
|
68
|
+
/**
|
|
69
|
+
* Custom @keyframes, web only. Each key is a keyframe name, its value maps
|
|
70
|
+
* percentage/from/to selectors to a plain CSS declaration object (camelCase
|
|
71
|
+
* properties, same shape as an inline style object):
|
|
72
|
+
* keyframes: { wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } } }
|
|
73
|
+
* Referenced from `animation` below, or directly via animate-[wiggle_1s_ease-in-out].
|
|
74
|
+
*/
|
|
75
|
+
keyframes: Record<string, Record<string, StyleValue>>;
|
|
76
|
+
/**
|
|
77
|
+
* Named animation shorthands built on `keyframes` above, referenced via
|
|
78
|
+
* animate-{name} (e.g. animate-wiggle):
|
|
79
|
+
* animation: { wiggle: 'wiggle 1s ease-in-out infinite' }
|
|
80
|
+
* The first word must match a `keyframes` key so its @keyframes rule can be
|
|
81
|
+
* injected alongside the animation — a name with no matching keyframes entry
|
|
82
|
+
* still sets the `animation` CSS property, it just won't animate anything.
|
|
83
|
+
*/
|
|
84
|
+
animation: Record<string, string>;
|
|
68
85
|
[key: string]: unknown;
|
|
69
86
|
}
|
|
70
87
|
/**
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -65,6 +65,23 @@ interface ThemeConfig {
|
|
|
65
65
|
flex: Record<string, number | string>;
|
|
66
66
|
shadow: Record<string, StyleValue>;
|
|
67
67
|
screens: Record<string, string | number>;
|
|
68
|
+
/**
|
|
69
|
+
* Custom @keyframes, web only. Each key is a keyframe name, its value maps
|
|
70
|
+
* percentage/from/to selectors to a plain CSS declaration object (camelCase
|
|
71
|
+
* properties, same shape as an inline style object):
|
|
72
|
+
* keyframes: { wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } } }
|
|
73
|
+
* Referenced from `animation` below, or directly via animate-[wiggle_1s_ease-in-out].
|
|
74
|
+
*/
|
|
75
|
+
keyframes: Record<string, Record<string, StyleValue>>;
|
|
76
|
+
/**
|
|
77
|
+
* Named animation shorthands built on `keyframes` above, referenced via
|
|
78
|
+
* animate-{name} (e.g. animate-wiggle):
|
|
79
|
+
* animation: { wiggle: 'wiggle 1s ease-in-out infinite' }
|
|
80
|
+
* The first word must match a `keyframes` key so its @keyframes rule can be
|
|
81
|
+
* injected alongside the animation — a name with no matching keyframes entry
|
|
82
|
+
* still sets the `animation` CSS property, it just won't animate anything.
|
|
83
|
+
*/
|
|
84
|
+
animation: Record<string, string>;
|
|
68
85
|
[key: string]: unknown;
|
|
69
86
|
}
|
|
70
87
|
/**
|
package/dist/vite-plugin.js
CHANGED
|
@@ -640,7 +640,13 @@ var defaultTheme = {
|
|
|
640
640
|
xl: 1280,
|
|
641
641
|
// desktop
|
|
642
642
|
"2xl": 1536
|
|
643
|
-
}
|
|
643
|
+
},
|
|
644
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
645
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
646
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
647
|
+
// alongside them without needing to redeclare the built-ins.
|
|
648
|
+
keyframes: {},
|
|
649
|
+
animation: {}
|
|
644
650
|
};
|
|
645
651
|
|
|
646
652
|
// src/core/utilities.ts
|
|
@@ -1174,6 +1180,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1174
1180
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1175
1181
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1176
1182
|
};
|
|
1183
|
+
function keyframeDeclToCSS(decl) {
|
|
1184
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1185
|
+
}
|
|
1186
|
+
function buildKeyframeCSS(name, steps) {
|
|
1187
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1188
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1189
|
+
}
|
|
1190
|
+
var BUILTIN_ANIMATIONS = {
|
|
1191
|
+
none: { animation: "none" },
|
|
1192
|
+
spin: {
|
|
1193
|
+
animation: "kb-spin 1s linear infinite",
|
|
1194
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1195
|
+
},
|
|
1196
|
+
ping: {
|
|
1197
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1198
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1199
|
+
},
|
|
1200
|
+
pulse: {
|
|
1201
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1202
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1203
|
+
},
|
|
1204
|
+
bounce: {
|
|
1205
|
+
animation: "kb-bounce 1s infinite",
|
|
1206
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1210
|
+
const words = animation.trim().split(/\s+/);
|
|
1211
|
+
const name = words[0];
|
|
1212
|
+
if (!name) return { animation };
|
|
1213
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1214
|
+
if (builtin?.__keyframe) {
|
|
1215
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1216
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1217
|
+
}
|
|
1218
|
+
const steps = keyframes?.[name];
|
|
1219
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1220
|
+
}
|
|
1177
1221
|
var RESOLVERS = {
|
|
1178
1222
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1179
1223
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1727,6 +1771,38 @@ var RESOLVERS = {
|
|
|
1727
1771
|
};
|
|
1728
1772
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1729
1773
|
},
|
|
1774
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
1775
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
1776
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
1777
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
1778
|
+
content: ({ value, isArbitrary }) => {
|
|
1779
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1780
|
+
if (isArbitrary) return { content: value };
|
|
1781
|
+
const presets = { none: "none" };
|
|
1782
|
+
return value in presets ? { content: presets[value] } : null;
|
|
1783
|
+
},
|
|
1784
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
1785
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
1786
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
1787
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
1788
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
1789
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
1790
|
+
//
|
|
1791
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
1792
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
1793
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
1794
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
1795
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
1796
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
1797
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
1798
|
+
if (negative) return null;
|
|
1799
|
+
const colonIdx = value.indexOf(":");
|
|
1800
|
+
if (colonIdx <= 0) return null;
|
|
1801
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
1802
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
1803
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
1804
|
+
return { [prop]: cssValue };
|
|
1805
|
+
},
|
|
1730
1806
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1731
1807
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1732
1808
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1920,30 +1996,12 @@ var RESOLVERS = {
|
|
|
1920
1996
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
1921
1997
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
1922
1998
|
// so it never reaches element inline styles.
|
|
1923
|
-
animate: ({ value, isArbitrary }) => {
|
|
1924
|
-
if (!getEffectiveIsWeb()) return null;
|
|
1925
|
-
if (isArbitrary) return
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
animation: "kb-spin 1s linear infinite",
|
|
1930
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1931
|
-
},
|
|
1932
|
-
ping: {
|
|
1933
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1934
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1935
|
-
},
|
|
1936
|
-
pulse: {
|
|
1937
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1938
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1939
|
-
},
|
|
1940
|
-
bounce: {
|
|
1941
|
-
animation: "kb-bounce 1s infinite",
|
|
1942
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
const def = defs[value];
|
|
1946
|
-
return def ? def : null;
|
|
1999
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
2000
|
+
if (!getEffectiveIsWeb()) return null;
|
|
2001
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
2002
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
2003
|
+
const custom = theme.animation?.[value];
|
|
2004
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
1947
2005
|
},
|
|
1948
2006
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
1949
2007
|
"line-clamp": ({ value }) => {
|
|
@@ -3232,7 +3290,7 @@ function pushTemplateLiteralBody(body, into) {
|
|
|
3232
3290
|
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3233
3291
|
pushClassLikeStrings(im[0], into);
|
|
3234
3292
|
}
|
|
3235
|
-
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g,
|
|
3293
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
3236
3294
|
}
|
|
3237
3295
|
function pushClassLikeStrings(text, into) {
|
|
3238
3296
|
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
@@ -3245,9 +3303,9 @@ function pushClassLikeStrings(text, into) {
|
|
|
3245
3303
|
}
|
|
3246
3304
|
function extractClassStrings(code) {
|
|
3247
3305
|
const found = /* @__PURE__ */ new Set();
|
|
3248
|
-
const simpleRe = /(?:className|kb)=
|
|
3306
|
+
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
3249
3307
|
let m;
|
|
3250
|
-
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[
|
|
3308
|
+
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
3251
3309
|
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
3252
3310
|
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
3253
3311
|
let depth = 1;
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -606,7 +606,13 @@ var defaultTheme = {
|
|
|
606
606
|
xl: 1280,
|
|
607
607
|
// desktop
|
|
608
608
|
"2xl": 1536
|
|
609
|
-
}
|
|
609
|
+
},
|
|
610
|
+
// Empty by default — the 4 built-in presets (spin/ping/pulse/bounce) are handled
|
|
611
|
+
// directly in the `animate` resolver, not through this theme table. This exists so
|
|
612
|
+
// user-defined ones (kbach.config.js theme.extend.keyframes/animation) merge in
|
|
613
|
+
// alongside them without needing to redeclare the built-ins.
|
|
614
|
+
keyframes: {},
|
|
615
|
+
animation: {}
|
|
610
616
|
};
|
|
611
617
|
|
|
612
618
|
// src/core/utilities.ts
|
|
@@ -1140,6 +1146,44 @@ var BORDER_SIDE_PROPS = {
|
|
|
1140
1146
|
"border-b": ["borderBottomWidth", "borderBottomColor"],
|
|
1141
1147
|
"border-l": ["borderLeftWidth", "borderLeftColor"]
|
|
1142
1148
|
};
|
|
1149
|
+
function keyframeDeclToCSS(decl) {
|
|
1150
|
+
return Object.entries(decl).map(([prop, val]) => `${prop.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${val}`).join("; ");
|
|
1151
|
+
}
|
|
1152
|
+
function buildKeyframeCSS(name, steps) {
|
|
1153
|
+
const body = Object.entries(steps).map(([selector, decl]) => `${selector} { ${keyframeDeclToCSS(decl)} }`).join(" ");
|
|
1154
|
+
return `@keyframes ${name} { ${body} }`;
|
|
1155
|
+
}
|
|
1156
|
+
var BUILTIN_ANIMATIONS = {
|
|
1157
|
+
none: { animation: "none" },
|
|
1158
|
+
spin: {
|
|
1159
|
+
animation: "kb-spin 1s linear infinite",
|
|
1160
|
+
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1161
|
+
},
|
|
1162
|
+
ping: {
|
|
1163
|
+
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1164
|
+
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1165
|
+
},
|
|
1166
|
+
pulse: {
|
|
1167
|
+
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1168
|
+
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1169
|
+
},
|
|
1170
|
+
bounce: {
|
|
1171
|
+
animation: "kb-bounce 1s infinite",
|
|
1172
|
+
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
function buildAnimationValue(animation, keyframes) {
|
|
1176
|
+
const words = animation.trim().split(/\s+/);
|
|
1177
|
+
const name = words[0];
|
|
1178
|
+
if (!name) return { animation };
|
|
1179
|
+
const builtin = BUILTIN_ANIMATIONS[name];
|
|
1180
|
+
if (builtin?.__keyframe) {
|
|
1181
|
+
const kbName = builtin.animation.split(/\s+/, 1)[0];
|
|
1182
|
+
return { animation: [kbName, ...words.slice(1)].join(" "), __keyframe: builtin.__keyframe };
|
|
1183
|
+
}
|
|
1184
|
+
const steps = keyframes?.[name];
|
|
1185
|
+
return steps ? { animation, __keyframe: buildKeyframeCSS(name, steps) } : { animation };
|
|
1186
|
+
}
|
|
1143
1187
|
var RESOLVERS = {
|
|
1144
1188
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1145
1189
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
@@ -1693,6 +1737,38 @@ var RESOLVERS = {
|
|
|
1693
1737
|
};
|
|
1694
1738
|
return offsets[value] ? { textUnderlineOffset: offsets[value] } : null;
|
|
1695
1739
|
},
|
|
1740
|
+
// ── Content (web-only — for before:/after: pseudo-elements, which don't exist on native) ──
|
|
1741
|
+
// Arbitrary value carries its own quotes from the bracket syntax (content-['*'] parses to
|
|
1742
|
+
// the literal string 'you can see it' quotes included), so it passes straight through as a
|
|
1743
|
+
// valid `content: '*'` CSS value with no extra wrapping needed.
|
|
1744
|
+
content: ({ value, isArbitrary }) => {
|
|
1745
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1746
|
+
if (isArbitrary) return { content: value };
|
|
1747
|
+
const presets = { none: "none" };
|
|
1748
|
+
return value in presets ? { content: presets[value] } : null;
|
|
1749
|
+
},
|
|
1750
|
+
// ── Arbitrary CSS property (web-only): [property:value] with no utility prefix ──
|
|
1751
|
+
// e.g. [mask-type:luminance], [--my-var:10px]. parser.ts already isolates this case
|
|
1752
|
+
// as utility: '' — it's ALSO how a stray, prefix-less negative bracket ("-[10px]",
|
|
1753
|
+
// presumably a typo) parses, which is why this only fires when the arbitrary value
|
|
1754
|
+
// itself contains a "property:value" pair; anything else (no colon) still resolves
|
|
1755
|
+
// to null exactly as before this resolver existed, rather than guessing a property.
|
|
1756
|
+
//
|
|
1757
|
+
// Only the FIRST colon splits property from value — the value itself may contain
|
|
1758
|
+
// more colons (a URL's "http://…") and must stay intact: [background:url(http://x/a.png)].
|
|
1759
|
+
// camelToKebab() in resolver.ts is a no-op on an already-kebab-case (or "--"-prefixed
|
|
1760
|
+
// custom property) key, so it can be used as the style object key as-written with no
|
|
1761
|
+
// extra case conversion — it only needs to look like a plausible CSS property name.
|
|
1762
|
+
"": ({ value, isArbitrary, negative }) => {
|
|
1763
|
+
if (!getEffectiveIsWeb() || !isArbitrary || !value) return null;
|
|
1764
|
+
if (negative) return null;
|
|
1765
|
+
const colonIdx = value.indexOf(":");
|
|
1766
|
+
if (colonIdx <= 0) return null;
|
|
1767
|
+
const prop = value.slice(0, colonIdx).trim();
|
|
1768
|
+
const cssValue = value.slice(colonIdx + 1).trim();
|
|
1769
|
+
if (!cssValue || !/^(--[\w-]+|[a-zA-Z-]+)$/.test(prop)) return null;
|
|
1770
|
+
return { [prop]: cssValue };
|
|
1771
|
+
},
|
|
1696
1772
|
// ── Outline extended (web-only) ───────────────────────────────────────────
|
|
1697
1773
|
outline: ({ value, isArbitrary }, { colors }) => {
|
|
1698
1774
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1886,30 +1962,12 @@ var RESOLVERS = {
|
|
|
1886
1962
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
1887
1963
|
// resolver.ts detects __keyframe, injects the rule, then strips the marker
|
|
1888
1964
|
// so it never reaches element inline styles.
|
|
1889
|
-
animate: ({ value, isArbitrary }) => {
|
|
1890
|
-
if (!getEffectiveIsWeb()) return null;
|
|
1891
|
-
if (isArbitrary) return
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
animation: "kb-spin 1s linear infinite",
|
|
1896
|
-
__keyframe: "@keyframes kb-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
1897
|
-
},
|
|
1898
|
-
ping: {
|
|
1899
|
-
animation: "kb-ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
1900
|
-
__keyframe: "@keyframes kb-ping { 75%, 100% { transform: scale(2); opacity: 0 } }"
|
|
1901
|
-
},
|
|
1902
|
-
pulse: {
|
|
1903
|
-
animation: "kb-pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
1904
|
-
__keyframe: "@keyframes kb-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }"
|
|
1905
|
-
},
|
|
1906
|
-
bounce: {
|
|
1907
|
-
animation: "kb-bounce 1s infinite",
|
|
1908
|
-
__keyframe: "@keyframes kb-bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1) } 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1) } }"
|
|
1909
|
-
}
|
|
1910
|
-
};
|
|
1911
|
-
const def = defs[value];
|
|
1912
|
-
return def ? def : null;
|
|
1965
|
+
animate: ({ value, isArbitrary }, theme) => {
|
|
1966
|
+
if (!getEffectiveIsWeb()) return null;
|
|
1967
|
+
if (isArbitrary) return buildAnimationValue(value.replace(/_/g, " "), theme.keyframes);
|
|
1968
|
+
if (value in BUILTIN_ANIMATIONS) return BUILTIN_ANIMATIONS[value];
|
|
1969
|
+
const custom = theme.animation?.[value];
|
|
1970
|
+
return custom ? buildAnimationValue(custom, theme.keyframes) : null;
|
|
1913
1971
|
},
|
|
1914
1972
|
// ── Line-clamp (web-only) ─────────────────────────────────────────────────
|
|
1915
1973
|
"line-clamp": ({ value }) => {
|
|
@@ -3198,7 +3256,7 @@ function pushTemplateLiteralBody(body, into) {
|
|
|
3198
3256
|
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3199
3257
|
pushClassLikeStrings(im[0], into);
|
|
3200
3258
|
}
|
|
3201
|
-
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g,
|
|
3259
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, () => "$"), into);
|
|
3202
3260
|
}
|
|
3203
3261
|
function pushClassLikeStrings(text, into) {
|
|
3204
3262
|
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
@@ -3211,9 +3269,9 @@ function pushClassLikeStrings(text, into) {
|
|
|
3211
3269
|
}
|
|
3212
3270
|
function extractClassStrings(code) {
|
|
3213
3271
|
const found = /* @__PURE__ */ new Set();
|
|
3214
|
-
const simpleRe = /(?:className|kb)=
|
|
3272
|
+
const simpleRe = /(?:className|kb)=(["'])((?:(?!\1).)*)\1/g;
|
|
3215
3273
|
let m;
|
|
3216
|
-
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[
|
|
3274
|
+
while ((m = simpleRe.exec(code)) !== null) pushTokens(m[2], found);
|
|
3217
3275
|
const jsxExprRe = /(?:className|kb)=\{/g;
|
|
3218
3276
|
while ((m = jsxExprRe.exec(code)) !== null) {
|
|
3219
3277
|
let depth = 1;
|