@kbach/react 0.2.7 → 0.2.9
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 +53 -10
- package/dist/{chunk-QMJ4XUJA.mjs → chunk-Q5RMVTAU.mjs} +1 -1
- package/dist/{chunk-LYGD2GSW.mjs → chunk-V5KHX4CZ.mjs} +192 -47
- package/dist/index.d.mts +26 -7
- package/dist/index.d.ts +26 -7
- package/dist/index.js +332 -116
- package/dist/index.mjs +83 -15
- package/dist/jsx-dev-runtime.js +188 -47
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +188 -47
- package/dist/jsx-runtime.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,14 +119,14 @@ function Badge() {
|
|
|
119
119
|
}
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
###
|
|
122
|
+
### kb(classes)
|
|
123
123
|
|
|
124
|
-
Resolve classes outside a component (static contexts).
|
|
124
|
+
Resolve classes outside a component (static contexts, StyleSheet.create, etc.).
|
|
125
125
|
|
|
126
126
|
```js
|
|
127
|
-
import {
|
|
127
|
+
import { kb } from '@kbach/react';
|
|
128
128
|
|
|
129
|
-
const cardStyle =
|
|
129
|
+
const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
### cx(...classes)
|
|
@@ -148,16 +148,59 @@ import { cx } from '@kbach/react';
|
|
|
148
148
|
```js
|
|
149
149
|
import { useTheme } from '@kbach/react';
|
|
150
150
|
|
|
151
|
-
const { mode, resolvedMode, isDark, setMode, toggle } = useTheme();
|
|
151
|
+
const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
| Value | Type | Description |
|
|
155
155
|
|---|---|---|
|
|
156
|
-
| mode | `'light' \| 'dark' \| 'system'` | User-selected mode |
|
|
157
|
-
| resolvedMode | `'light' \| 'dark'` | Resolved after system lookup |
|
|
158
|
-
| isDark | boolean | Shorthand for `resolvedMode === 'dark'` |
|
|
159
|
-
| setMode |
|
|
160
|
-
| toggle |
|
|
156
|
+
| `mode` | `'light' \| 'dark' \| 'system'` | User-selected mode |
|
|
157
|
+
| `resolvedMode` | `'light' \| 'dark'` | Resolved after system lookup |
|
|
158
|
+
| `isDark` | `boolean` | Shorthand for `resolvedMode === 'dark'` |
|
|
159
|
+
| `setMode` | `fn` | Set mode explicitly |
|
|
160
|
+
| `toggle` | `fn` | Toggle between light and dark |
|
|
161
|
+
| `config` | `ResolvedConfig` | Full resolved config (theme, darkMode, plugins) |
|
|
162
|
+
|
|
163
|
+
### useIsDark()
|
|
164
|
+
|
|
165
|
+
Shorthand hook when you only need the dark-mode boolean.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
import { useIsDark } from '@kbach/react';
|
|
169
|
+
|
|
170
|
+
const isDark = useIsDark();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### useColors()
|
|
174
|
+
|
|
175
|
+
Returns the active theme's color palette as a smart proxy. Supports shade access, the `/opacity` modifier, flat colors with opacity, and arbitrary CSS color manipulation.
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import { useColors } from '@kbach/react';
|
|
179
|
+
|
|
180
|
+
const colors = useColors();
|
|
181
|
+
|
|
182
|
+
// Shade access
|
|
183
|
+
colors.blue[6] // '#6c87b6'
|
|
184
|
+
colors.red[11] // '#3e0606'
|
|
185
|
+
|
|
186
|
+
// Shade + opacity (0–100)
|
|
187
|
+
colors.blue['6/50'] // 'rgba(108,135,182,0.5)'
|
|
188
|
+
colors.slate['3/10'] // 'rgba(221,227,242,0.1)'
|
|
189
|
+
|
|
190
|
+
// Flat colors
|
|
191
|
+
colors.white // '#ffffff'
|
|
192
|
+
colors.transparent // 'transparent'
|
|
193
|
+
|
|
194
|
+
// Flat color + opacity
|
|
195
|
+
colors['white/20'] // 'rgba(255,255,255,0.2)'
|
|
196
|
+
colors['black/80'] // 'rgba(0,0,0,0.8)'
|
|
197
|
+
|
|
198
|
+
// Arbitrary CSS color with opacity
|
|
199
|
+
colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
|
|
200
|
+
colors.alpha('rgb(100,200,100)', 30) // 'rgba(100,200,100,0.3)'
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Common uses: chart libraries that need raw hex values, dynamic inline styles, color pickers that mirror the theme palette.
|
|
161
204
|
|
|
162
205
|
### ThemeToggle
|
|
163
206
|
|
|
@@ -529,6 +529,13 @@ function escapeCSSSelector(cls) {
|
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
// src/core/parser.ts
|
|
532
|
+
var PLUGIN_MODIFIERS = /* @__PURE__ */ new Set();
|
|
533
|
+
function registerModifier(name) {
|
|
534
|
+
PLUGIN_MODIFIERS.add(name);
|
|
535
|
+
}
|
|
536
|
+
function clearPluginModifiers() {
|
|
537
|
+
PLUGIN_MODIFIERS.clear();
|
|
538
|
+
}
|
|
532
539
|
var MODIFIERS = /* @__PURE__ */ new Set([
|
|
533
540
|
// Theme
|
|
534
541
|
"dark",
|
|
@@ -958,7 +965,7 @@ function parseClass(className) {
|
|
|
958
965
|
const colonIdx = findOuterColon(remaining);
|
|
959
966
|
if (colonIdx === -1) break;
|
|
960
967
|
const candidate = remaining.slice(0, colonIdx);
|
|
961
|
-
if (MODIFIERS.has(candidate)) {
|
|
968
|
+
if (MODIFIERS.has(candidate) || PLUGIN_MODIFIERS.has(candidate)) {
|
|
962
969
|
modifiers.push(candidate);
|
|
963
970
|
remaining = remaining.slice(colonIdx + 1);
|
|
964
971
|
} else {
|
|
@@ -1424,43 +1431,64 @@ var RESOLVERS = {
|
|
|
1424
1431
|
// ── Border width ───────────────────────────────────────────────────────────
|
|
1425
1432
|
border: ({ value, isArbitrary }, { colors, borderWidth, spacing }) => {
|
|
1426
1433
|
if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
|
|
1427
|
-
const color = resolveColor(value, colors, isArbitrary);
|
|
1428
|
-
if (color) return { borderColor: color };
|
|
1429
1434
|
if (isArbitrary) {
|
|
1430
1435
|
const w2 = toNativeValue(value);
|
|
1431
|
-
|
|
1436
|
+
if (typeof w2 === "number") return { borderWidth: w2 };
|
|
1437
|
+
return { borderColor: value };
|
|
1432
1438
|
}
|
|
1439
|
+
const color = resolveColor(value, colors, false);
|
|
1440
|
+
if (color) return { borderColor: color };
|
|
1433
1441
|
const w = borderWidth[value] ?? spacing[value];
|
|
1434
1442
|
if (w !== void 0) return { borderWidth: typeof w === "number" ? w : parseFloat(String(w)) };
|
|
1435
1443
|
return null;
|
|
1436
1444
|
},
|
|
1437
1445
|
"border-t": ({ value, isArbitrary }, { colors, borderWidth }) => {
|
|
1438
1446
|
if (!value) return { borderTopWidth: 1 };
|
|
1439
|
-
|
|
1447
|
+
if (isArbitrary) {
|
|
1448
|
+
const w2 = toNativeValue(value);
|
|
1449
|
+
if (typeof w2 === "number") return { borderTopWidth: w2 };
|
|
1450
|
+
return { borderTopColor: value };
|
|
1451
|
+
}
|
|
1452
|
+
const color = resolveColor(value, colors, false);
|
|
1440
1453
|
if (color) return { borderTopColor: color };
|
|
1441
|
-
const w = borderWidth[value]
|
|
1442
|
-
return w !==
|
|
1454
|
+
const w = borderWidth[value];
|
|
1455
|
+
return w !== void 0 ? { borderTopWidth: w } : null;
|
|
1443
1456
|
},
|
|
1444
1457
|
"border-r": ({ value, isArbitrary }, { colors, borderWidth }) => {
|
|
1445
1458
|
if (!value) return { borderRightWidth: 1 };
|
|
1446
|
-
|
|
1459
|
+
if (isArbitrary) {
|
|
1460
|
+
const w2 = toNativeValue(value);
|
|
1461
|
+
if (typeof w2 === "number") return { borderRightWidth: w2 };
|
|
1462
|
+
return { borderRightColor: value };
|
|
1463
|
+
}
|
|
1464
|
+
const color = resolveColor(value, colors, false);
|
|
1447
1465
|
if (color) return { borderRightColor: color };
|
|
1448
|
-
const w = borderWidth[value]
|
|
1449
|
-
return w !==
|
|
1466
|
+
const w = borderWidth[value];
|
|
1467
|
+
return w !== void 0 ? { borderRightWidth: w } : null;
|
|
1450
1468
|
},
|
|
1451
1469
|
"border-b": ({ value, isArbitrary }, { colors, borderWidth }) => {
|
|
1452
1470
|
if (!value) return { borderBottomWidth: 1 };
|
|
1453
|
-
|
|
1471
|
+
if (isArbitrary) {
|
|
1472
|
+
const w2 = toNativeValue(value);
|
|
1473
|
+
if (typeof w2 === "number") return { borderBottomWidth: w2 };
|
|
1474
|
+
return { borderBottomColor: value };
|
|
1475
|
+
}
|
|
1476
|
+
const color = resolveColor(value, colors, false);
|
|
1454
1477
|
if (color) return { borderBottomColor: color };
|
|
1455
|
-
const w = borderWidth[value]
|
|
1456
|
-
return w !==
|
|
1478
|
+
const w = borderWidth[value];
|
|
1479
|
+
return w !== void 0 ? { borderBottomWidth: w } : null;
|
|
1457
1480
|
},
|
|
1458
1481
|
"border-l": ({ value, isArbitrary }, { colors, borderWidth }) => {
|
|
1459
1482
|
if (!value) return { borderLeftWidth: 1 };
|
|
1460
|
-
|
|
1483
|
+
if (isArbitrary) {
|
|
1484
|
+
const w2 = toNativeValue(value);
|
|
1485
|
+
if (typeof w2 === "number") return { borderLeftWidth: w2 };
|
|
1486
|
+
return { borderLeftColor: value };
|
|
1487
|
+
}
|
|
1488
|
+
const color = resolveColor(value, colors, false);
|
|
1461
1489
|
if (color) return { borderLeftColor: color };
|
|
1462
|
-
const w = borderWidth[value]
|
|
1463
|
-
return w !==
|
|
1490
|
+
const w = borderWidth[value];
|
|
1491
|
+
return w !== void 0 ? { borderLeftWidth: w } : null;
|
|
1464
1492
|
},
|
|
1465
1493
|
// ── Border radius ──────────────────────────────────────────────────────────
|
|
1466
1494
|
rounded: ({ value, isArbitrary }, { borderRadius }) => {
|
|
@@ -1525,7 +1553,6 @@ var RESOLVERS = {
|
|
|
1525
1553
|
return { flexShrink: isNaN(n) ? 1 : n };
|
|
1526
1554
|
},
|
|
1527
1555
|
order: ({ value, isArbitrary }, _) => {
|
|
1528
|
-
if (isArbitrary) return { order: parseInt(value) };
|
|
1529
1556
|
const n = parseInt(value);
|
|
1530
1557
|
return isNaN(n) ? null : { order: n };
|
|
1531
1558
|
},
|
|
@@ -1573,9 +1600,15 @@ var RESOLVERS = {
|
|
|
1573
1600
|
},
|
|
1574
1601
|
// ── Z-index ────────────────────────────────────────────────────────────────
|
|
1575
1602
|
z: ({ value, isArbitrary }, { zIndex }) => {
|
|
1576
|
-
if (isArbitrary)
|
|
1603
|
+
if (isArbitrary) {
|
|
1604
|
+
const n2 = parseInt(value);
|
|
1605
|
+
return isNaN(n2) ? null : { zIndex: n2 };
|
|
1606
|
+
}
|
|
1577
1607
|
const v = zIndex[value];
|
|
1578
|
-
if (v !== void 0)
|
|
1608
|
+
if (v !== void 0) {
|
|
1609
|
+
if (v === "auto") return isWeb ? { zIndex: "auto" } : null;
|
|
1610
|
+
return { zIndex: v };
|
|
1611
|
+
}
|
|
1579
1612
|
const n = parseInt(value);
|
|
1580
1613
|
return isNaN(n) ? null : { zIndex: n };
|
|
1581
1614
|
},
|
|
@@ -1618,25 +1651,30 @@ var RESOLVERS = {
|
|
|
1618
1651
|
return shadow[key] ?? null;
|
|
1619
1652
|
},
|
|
1620
1653
|
// ── Scale ──────────────────────────────────────────────────────────────────
|
|
1654
|
+
// Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
|
|
1655
|
+
// Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
|
|
1621
1656
|
scale: ({ value, isArbitrary }) => {
|
|
1622
|
-
const n = isArbitrary ? parseFloat(value)
|
|
1657
|
+
const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
|
|
1623
1658
|
if (isNaN(n)) return null;
|
|
1624
1659
|
return isWeb ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
|
|
1625
1660
|
},
|
|
1626
1661
|
"scale-x": ({ value, isArbitrary }) => {
|
|
1627
|
-
const n = isArbitrary ? parseFloat(value)
|
|
1662
|
+
const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
|
|
1628
1663
|
if (isNaN(n)) return null;
|
|
1629
1664
|
return isWeb ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
|
|
1630
1665
|
},
|
|
1631
1666
|
"scale-y": ({ value, isArbitrary }) => {
|
|
1632
|
-
const n = isArbitrary ? parseFloat(value)
|
|
1667
|
+
const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
|
|
1633
1668
|
if (isNaN(n)) return null;
|
|
1634
1669
|
return isWeb ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
|
|
1635
1670
|
},
|
|
1636
1671
|
// ── Rotate ─────────────────────────────────────────────────────────────────
|
|
1637
1672
|
rotate: ({ value, negative, isArbitrary }) => {
|
|
1638
|
-
|
|
1639
|
-
|
|
1673
|
+
if (isArbitrary) {
|
|
1674
|
+
const finalValue = negative ? `-${value}` : value;
|
|
1675
|
+
return isWeb ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
|
|
1676
|
+
}
|
|
1677
|
+
const deg = parseFloat(value);
|
|
1640
1678
|
if (isNaN(deg)) return null;
|
|
1641
1679
|
const finalDeg = negative ? -deg : deg;
|
|
1642
1680
|
return isWeb ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
|
|
@@ -1645,18 +1683,31 @@ var RESOLVERS = {
|
|
|
1645
1683
|
"translate-x": ({ value, negative, isArbitrary }, { spacing }) => {
|
|
1646
1684
|
const v = resolveSpacing(value, negative, spacing, isArbitrary);
|
|
1647
1685
|
if (v === null) return null;
|
|
1648
|
-
|
|
1686
|
+
if (isWeb) return { transform: `translateX(${v})` };
|
|
1687
|
+
if (typeof v === "string") {
|
|
1688
|
+
const n = parseFloat(v);
|
|
1689
|
+
return isNaN(n) ? null : { transform: [{ translateX: n }] };
|
|
1690
|
+
}
|
|
1691
|
+
return { transform: [{ translateX: v }] };
|
|
1649
1692
|
},
|
|
1650
1693
|
"translate-y": ({ value, negative, isArbitrary }, { spacing }) => {
|
|
1651
1694
|
const v = resolveSpacing(value, negative, spacing, isArbitrary);
|
|
1652
1695
|
if (v === null) return null;
|
|
1653
|
-
|
|
1696
|
+
if (isWeb) return { transform: `translateY(${v})` };
|
|
1697
|
+
if (typeof v === "string") {
|
|
1698
|
+
const n = parseFloat(v);
|
|
1699
|
+
return isNaN(n) ? null : { transform: [{ translateY: n }] };
|
|
1700
|
+
}
|
|
1701
|
+
return { transform: [{ translateY: v }] };
|
|
1654
1702
|
},
|
|
1655
1703
|
// ── Aspect ratio ───────────────────────────────────────────────────────────
|
|
1656
1704
|
aspect: ({ value, isArbitrary }) => {
|
|
1657
1705
|
if (isArbitrary) return { aspectRatio: value };
|
|
1658
1706
|
const presets = { auto: "auto", square: 1, video: 16 / 9 };
|
|
1659
|
-
|
|
1707
|
+
if (!(value in presets)) return null;
|
|
1708
|
+
const v = presets[value];
|
|
1709
|
+
if (v === "auto") return isWeb ? { aspectRatio: "auto" } : null;
|
|
1710
|
+
return { aspectRatio: v };
|
|
1660
1711
|
},
|
|
1661
1712
|
// ── Transition (web-only; use Animated API on native) ─────────────────────
|
|
1662
1713
|
transition: ({ value }) => {
|
|
@@ -1733,18 +1784,25 @@ var RESOLVERS = {
|
|
|
1733
1784
|
}
|
|
1734
1785
|
};
|
|
1735
1786
|
function resolveUtility(parsed, theme) {
|
|
1736
|
-
if (!parsed.value
|
|
1737
|
-
return
|
|
1787
|
+
if (!parsed.value) {
|
|
1788
|
+
if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
|
|
1789
|
+
if (parsed.utility in STANDALONE) return STANDALONE[parsed.utility] ?? null;
|
|
1738
1790
|
}
|
|
1739
|
-
const resolver = RESOLVERS[parsed.utility];
|
|
1791
|
+
const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
|
|
1740
1792
|
if (resolver) return resolver(parsed, theme);
|
|
1741
1793
|
return null;
|
|
1742
1794
|
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1795
|
+
var PLUGIN_STANDALONE = {};
|
|
1796
|
+
var PLUGIN_RESOLVERS = {};
|
|
1797
|
+
function clearPluginUtilities() {
|
|
1798
|
+
for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
|
|
1799
|
+
for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
|
|
1800
|
+
}
|
|
1801
|
+
function getPluginStandaloneMap() {
|
|
1802
|
+
return PLUGIN_STANDALONE;
|
|
1745
1803
|
}
|
|
1746
|
-
function
|
|
1747
|
-
return
|
|
1804
|
+
function getPluginResolverMap() {
|
|
1805
|
+
return PLUGIN_RESOLVERS;
|
|
1748
1806
|
}
|
|
1749
1807
|
|
|
1750
1808
|
// src/core/cache.ts
|
|
@@ -1797,8 +1855,42 @@ function getStyleEl() {
|
|
|
1797
1855
|
function injectRule(rule) {
|
|
1798
1856
|
if (_injectedRules.has(rule)) return;
|
|
1799
1857
|
_injectedRules.add(rule);
|
|
1800
|
-
|
|
1858
|
+
try {
|
|
1859
|
+
getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
|
|
1860
|
+
} catch {
|
|
1861
|
+
}
|
|
1801
1862
|
}
|
|
1863
|
+
var MODIFIER_TO_PSEUDO = {
|
|
1864
|
+
hover: ":hover",
|
|
1865
|
+
focus: ":focus",
|
|
1866
|
+
"focus-within": ":focus-within",
|
|
1867
|
+
"focus-visible": ":focus-visible",
|
|
1868
|
+
active: ":active",
|
|
1869
|
+
pressed: ":active",
|
|
1870
|
+
disabled: ":disabled",
|
|
1871
|
+
checked: ":checked",
|
|
1872
|
+
visited: ":visited",
|
|
1873
|
+
placeholder: "::placeholder",
|
|
1874
|
+
first: ":first-child",
|
|
1875
|
+
last: ":last-child",
|
|
1876
|
+
odd: ":nth-child(odd)",
|
|
1877
|
+
even: ":nth-child(even)",
|
|
1878
|
+
only: ":only-child",
|
|
1879
|
+
"not-hover": ":not(:hover)",
|
|
1880
|
+
"not-focus": ":not(:focus)",
|
|
1881
|
+
"not-active": ":not(:active)",
|
|
1882
|
+
"not-pressed": ":not(:active)",
|
|
1883
|
+
"not-disabled": ":not(:disabled)",
|
|
1884
|
+
"not-checked": ":not(:checked)",
|
|
1885
|
+
"not-visited": ":not(:visited)"
|
|
1886
|
+
};
|
|
1887
|
+
var GROUP_PEER_PREFIX = {
|
|
1888
|
+
"group-hover": ".group:hover",
|
|
1889
|
+
"group-focus": ".group:focus",
|
|
1890
|
+
"peer-hover": ".peer:hover ~",
|
|
1891
|
+
"peer-focus": ".peer:focus ~"
|
|
1892
|
+
};
|
|
1893
|
+
var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
|
|
1802
1894
|
function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
1803
1895
|
const cssDecls = styleValueToCSS(styles);
|
|
1804
1896
|
if (!cssDecls) return;
|
|
@@ -1808,10 +1900,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
|
1808
1900
|
return;
|
|
1809
1901
|
}
|
|
1810
1902
|
const mods = bucketKey.split(":");
|
|
1811
|
-
const
|
|
1812
|
-
const
|
|
1813
|
-
const
|
|
1814
|
-
const
|
|
1903
|
+
const hasDark = mods.includes("dark") || mods.includes("not-light");
|
|
1904
|
+
const hasLight = mods.includes("light") || mods.includes("not-dark");
|
|
1905
|
+
const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
|
|
1906
|
+
const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
|
|
1907
|
+
const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
|
|
1908
|
+
const elementSelector = `.${escaped}${pseudoSuffix}`;
|
|
1909
|
+
const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
|
|
1910
|
+
const selector = `${ancestorPrefix}${elementSelector}`;
|
|
1815
1911
|
let rule;
|
|
1816
1912
|
if (hasDark) {
|
|
1817
1913
|
if (darkMode === "media") {
|
|
@@ -1821,13 +1917,54 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
|
1821
1917
|
} else {
|
|
1822
1918
|
rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
|
|
1823
1919
|
}
|
|
1920
|
+
} else if (hasLight) {
|
|
1921
|
+
if (darkMode === "media") {
|
|
1922
|
+
rule = `@media (prefers-color-scheme: light) { ${selector} { ${cssDecls} } }`;
|
|
1923
|
+
} else if (darkMode === "class") {
|
|
1924
|
+
rule = `:not(.dark) ${selector} { ${cssDecls} }`;
|
|
1925
|
+
} else {
|
|
1926
|
+
rule = `[data-theme="light"] ${selector} { ${cssDecls} }`;
|
|
1927
|
+
}
|
|
1824
1928
|
} else {
|
|
1825
1929
|
rule = `${selector} { ${cssDecls} }`;
|
|
1826
1930
|
}
|
|
1827
1931
|
injectRule(rule);
|
|
1828
1932
|
}
|
|
1933
|
+
var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
|
|
1934
|
+
"shadowColor",
|
|
1935
|
+
"shadowOffset",
|
|
1936
|
+
"shadowOpacity",
|
|
1937
|
+
"shadowRadius",
|
|
1938
|
+
"elevation",
|
|
1939
|
+
"includeFontPadding",
|
|
1940
|
+
"textAlignVertical",
|
|
1941
|
+
"writingDirection"
|
|
1942
|
+
]);
|
|
1943
|
+
var CSS_UNITLESS = /* @__PURE__ */ new Set([
|
|
1944
|
+
"opacity",
|
|
1945
|
+
"fontWeight",
|
|
1946
|
+
"flex",
|
|
1947
|
+
"flexGrow",
|
|
1948
|
+
"flexShrink",
|
|
1949
|
+
"order",
|
|
1950
|
+
"zIndex",
|
|
1951
|
+
"aspectRatio",
|
|
1952
|
+
"columnCount",
|
|
1953
|
+
"lineHeight"
|
|
1954
|
+
]);
|
|
1829
1955
|
function styleValueToCSS(styles) {
|
|
1830
|
-
return Object.entries(styles).filter(
|
|
1956
|
+
return Object.entries(styles).filter(
|
|
1957
|
+
([prop, v]) => v !== void 0 && v !== null && typeof v !== "object" && !RN_ONLY_PROPS.has(prop)
|
|
1958
|
+
).map(([prop, val]) => {
|
|
1959
|
+
const cssProp = camelToKebab(prop);
|
|
1960
|
+
let cssVal;
|
|
1961
|
+
if (typeof val === "number") {
|
|
1962
|
+
cssVal = val === 0 || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
1963
|
+
} else {
|
|
1964
|
+
cssVal = String(val);
|
|
1965
|
+
}
|
|
1966
|
+
return `${cssProp}: ${cssVal}`;
|
|
1967
|
+
}).join("; ");
|
|
1831
1968
|
}
|
|
1832
1969
|
function camelToKebab(str) {
|
|
1833
1970
|
return str.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
@@ -1913,6 +2050,9 @@ function matchesMods(mods, isDark, state) {
|
|
|
1913
2050
|
}
|
|
1914
2051
|
});
|
|
1915
2052
|
}
|
|
2053
|
+
function clearCache() {
|
|
2054
|
+
styleCache.clear();
|
|
2055
|
+
}
|
|
1916
2056
|
|
|
1917
2057
|
// src/core/config.ts
|
|
1918
2058
|
function deepMerge(base, override) {
|
|
@@ -1968,20 +2108,24 @@ function buildConfig(userConfig) {
|
|
|
1968
2108
|
theme,
|
|
1969
2109
|
plugins: userConfig.plugins ?? []
|
|
1970
2110
|
};
|
|
2111
|
+
clearPluginUtilities();
|
|
2112
|
+
clearPluginModifiers();
|
|
2113
|
+
const pluginAPI = makePluginAPI(resolved.theme);
|
|
1971
2114
|
for (const plugin of resolved.plugins) {
|
|
1972
|
-
plugin(
|
|
2115
|
+
plugin(pluginAPI);
|
|
1973
2116
|
}
|
|
1974
2117
|
return resolved;
|
|
1975
2118
|
}
|
|
1976
2119
|
function makePluginAPI(theme) {
|
|
1977
|
-
const standalone =
|
|
1978
|
-
const resolvers =
|
|
2120
|
+
const standalone = getPluginStandaloneMap();
|
|
2121
|
+
const resolvers = getPluginResolverMap();
|
|
1979
2122
|
return {
|
|
1980
2123
|
addUtility(name, styles) {
|
|
1981
2124
|
standalone[name] = styles;
|
|
1982
2125
|
},
|
|
1983
|
-
addVariant(name,
|
|
1984
|
-
customVariants[name] =
|
|
2126
|
+
addVariant(name, selector) {
|
|
2127
|
+
customVariants[name] = selector;
|
|
2128
|
+
registerModifier(name);
|
|
1985
2129
|
},
|
|
1986
2130
|
theme(path, defaultValue) {
|
|
1987
2131
|
const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
|
|
@@ -2005,6 +2149,7 @@ function onConfigChange(listener) {
|
|
|
2005
2149
|
function updateConfig(userConfig) {
|
|
2006
2150
|
const store = getConfigStore();
|
|
2007
2151
|
store.resolved = buildConfig(userConfig);
|
|
2152
|
+
clearCache();
|
|
2008
2153
|
for (const listener of store.listeners) {
|
|
2009
2154
|
listener(store.resolved);
|
|
2010
2155
|
}
|
|
@@ -2117,8 +2262,8 @@ var InteractiveWrapper = forwardRef(
|
|
|
2117
2262
|
...restWithoutChildren,
|
|
2118
2263
|
style: finalStyle,
|
|
2119
2264
|
...isWeb && className ? { className } : {},
|
|
2120
|
-
onPressIn
|
|
2121
|
-
onPressOut: handlePressOut,
|
|
2265
|
+
// onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
|
|
2266
|
+
...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
|
|
2122
2267
|
onMouseEnter: handleMouseEnter,
|
|
2123
2268
|
onMouseLeave: handleMouseLeave,
|
|
2124
2269
|
onFocus: handleFocus,
|
package/dist/index.d.mts
CHANGED
|
@@ -139,6 +139,25 @@ interface ThemeContextValue {
|
|
|
139
139
|
}
|
|
140
140
|
declare const ThemeContext: React.Context<ThemeContextValue | null>;
|
|
141
141
|
declare function useTheme(): ThemeContextValue;
|
|
142
|
+
declare function useIsDark(): boolean;
|
|
143
|
+
|
|
144
|
+
interface ColorScale {
|
|
145
|
+
/** `colors.blue[6]` → raw hex string */
|
|
146
|
+
readonly [shade: number]: string;
|
|
147
|
+
/** `colors.blue['6/50']` → shade 6 at 50% opacity */
|
|
148
|
+
readonly [key: string]: string;
|
|
149
|
+
}
|
|
150
|
+
interface ColorsAPI {
|
|
151
|
+
/** Named color scale (e.g. `colors.blue`) or flat value (e.g. `colors.white`) */
|
|
152
|
+
readonly [color: string]: string | ColorScale | ColorsAPI['alpha'];
|
|
153
|
+
/** Flat color with opacity shorthand: `colors['white/50']` */
|
|
154
|
+
/** Apply opacity to any CSS color string.
|
|
155
|
+
* `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
|
|
156
|
+
* `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'` */
|
|
157
|
+
alpha(color: string, opacity: number): string;
|
|
158
|
+
}
|
|
159
|
+
declare function wrapColors(rawColors: ThemeColors): ColorsAPI;
|
|
160
|
+
declare function useColors(): ColorsAPI;
|
|
142
161
|
|
|
143
162
|
interface ThemeProviderProps {
|
|
144
163
|
children: ReactNode;
|
|
@@ -249,7 +268,7 @@ declare function useResolvedStyle(classString: string | string[]): ResolvedStyle
|
|
|
249
268
|
|
|
250
269
|
/**
|
|
251
270
|
* Subscribe to the global dark-mode store with React's concurrent-safe
|
|
252
|
-
* useSyncExternalStore so that any component using className/
|
|
271
|
+
* useSyncExternalStore so that any component using className/kb re-renders
|
|
253
272
|
* immediately when the theme changes — without needing ThemeContext.
|
|
254
273
|
*/
|
|
255
274
|
declare function useGlobalDarkMode(): boolean;
|
|
@@ -272,7 +291,7 @@ interface InteractiveWrapperProps {
|
|
|
272
291
|
onBlur?: (...args: any[]) => void;
|
|
273
292
|
}
|
|
274
293
|
/**
|
|
275
|
-
* Thin wrapper rendered automatically by the JSX runtime whenever a className/
|
|
294
|
+
* Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
|
|
276
295
|
* string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
|
|
277
296
|
*
|
|
278
297
|
* Manages interaction state locally and flattens the correct style bucket on
|
|
@@ -291,20 +310,20 @@ declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<Inter
|
|
|
291
310
|
*
|
|
292
311
|
* ```ts
|
|
293
312
|
* // Inside a component use useStyles() instead.
|
|
294
|
-
* //
|
|
313
|
+
* // kb() is useful for StyleSheet.create() calls and static values.
|
|
295
314
|
*
|
|
296
315
|
* const styles = StyleSheet.create({
|
|
297
|
-
* container:
|
|
316
|
+
* container: kb('flex-1 bg-white p-4') as any,
|
|
298
317
|
* });
|
|
299
318
|
*
|
|
300
319
|
* // Web: use as className
|
|
301
|
-
* <div className={
|
|
320
|
+
* <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
|
|
302
321
|
* ```
|
|
303
322
|
*
|
|
304
323
|
* @param classString Space-separated utility classes
|
|
305
324
|
* @param isDark Whether dark mode is active (default: false)
|
|
306
325
|
*/
|
|
307
|
-
declare function
|
|
326
|
+
declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
308
327
|
/**
|
|
309
328
|
* Conditionally join class names. Falsy values are ignored.
|
|
310
329
|
*
|
|
@@ -315,4 +334,4 @@ declare function tw(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
315
334
|
*/
|
|
316
335
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
317
336
|
|
|
318
|
-
export { type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThemeToggle, type ThemeToggleProps, type ToggleVariant, buildConfig, cx, defaultColors, defaultTheme, flatten, getConfig, parseClass, parseClasses, resolve, styled,
|
|
337
|
+
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThemeToggle, type ThemeToggleProps, type ToggleVariant, buildConfig, cx, defaultColors, defaultTheme, flatten, getConfig, kb, parseClass, parseClasses, resolve, styled, updateConfig, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useStyles, useTheme, wrapColors };
|
package/dist/index.d.ts
CHANGED
|
@@ -139,6 +139,25 @@ interface ThemeContextValue {
|
|
|
139
139
|
}
|
|
140
140
|
declare const ThemeContext: React.Context<ThemeContextValue | null>;
|
|
141
141
|
declare function useTheme(): ThemeContextValue;
|
|
142
|
+
declare function useIsDark(): boolean;
|
|
143
|
+
|
|
144
|
+
interface ColorScale {
|
|
145
|
+
/** `colors.blue[6]` → raw hex string */
|
|
146
|
+
readonly [shade: number]: string;
|
|
147
|
+
/** `colors.blue['6/50']` → shade 6 at 50% opacity */
|
|
148
|
+
readonly [key: string]: string;
|
|
149
|
+
}
|
|
150
|
+
interface ColorsAPI {
|
|
151
|
+
/** Named color scale (e.g. `colors.blue`) or flat value (e.g. `colors.white`) */
|
|
152
|
+
readonly [color: string]: string | ColorScale | ColorsAPI['alpha'];
|
|
153
|
+
/** Flat color with opacity shorthand: `colors['white/50']` */
|
|
154
|
+
/** Apply opacity to any CSS color string.
|
|
155
|
+
* `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
|
|
156
|
+
* `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'` */
|
|
157
|
+
alpha(color: string, opacity: number): string;
|
|
158
|
+
}
|
|
159
|
+
declare function wrapColors(rawColors: ThemeColors): ColorsAPI;
|
|
160
|
+
declare function useColors(): ColorsAPI;
|
|
142
161
|
|
|
143
162
|
interface ThemeProviderProps {
|
|
144
163
|
children: ReactNode;
|
|
@@ -249,7 +268,7 @@ declare function useResolvedStyle(classString: string | string[]): ResolvedStyle
|
|
|
249
268
|
|
|
250
269
|
/**
|
|
251
270
|
* Subscribe to the global dark-mode store with React's concurrent-safe
|
|
252
|
-
* useSyncExternalStore so that any component using className/
|
|
271
|
+
* useSyncExternalStore so that any component using className/kb re-renders
|
|
253
272
|
* immediately when the theme changes — without needing ThemeContext.
|
|
254
273
|
*/
|
|
255
274
|
declare function useGlobalDarkMode(): boolean;
|
|
@@ -272,7 +291,7 @@ interface InteractiveWrapperProps {
|
|
|
272
291
|
onBlur?: (...args: any[]) => void;
|
|
273
292
|
}
|
|
274
293
|
/**
|
|
275
|
-
* Thin wrapper rendered automatically by the JSX runtime whenever a className/
|
|
294
|
+
* Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
|
|
276
295
|
* string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
|
|
277
296
|
*
|
|
278
297
|
* Manages interaction state locally and flattens the correct style bucket on
|
|
@@ -291,20 +310,20 @@ declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<Inter
|
|
|
291
310
|
*
|
|
292
311
|
* ```ts
|
|
293
312
|
* // Inside a component use useStyles() instead.
|
|
294
|
-
* //
|
|
313
|
+
* // kb() is useful for StyleSheet.create() calls and static values.
|
|
295
314
|
*
|
|
296
315
|
* const styles = StyleSheet.create({
|
|
297
|
-
* container:
|
|
316
|
+
* container: kb('flex-1 bg-white p-4') as any,
|
|
298
317
|
* });
|
|
299
318
|
*
|
|
300
319
|
* // Web: use as className
|
|
301
|
-
* <div className={
|
|
320
|
+
* <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
|
|
302
321
|
* ```
|
|
303
322
|
*
|
|
304
323
|
* @param classString Space-separated utility classes
|
|
305
324
|
* @param isDark Whether dark mode is active (default: false)
|
|
306
325
|
*/
|
|
307
|
-
declare function
|
|
326
|
+
declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
308
327
|
/**
|
|
309
328
|
* Conditionally join class names. Falsy values are ignored.
|
|
310
329
|
*
|
|
@@ -315,4 +334,4 @@ declare function tw(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
315
334
|
*/
|
|
316
335
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
317
336
|
|
|
318
|
-
export { type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThemeToggle, type ThemeToggleProps, type ToggleVariant, buildConfig, cx, defaultColors, defaultTheme, flatten, getConfig, parseClass, parseClasses, resolve, styled,
|
|
337
|
+
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThemeToggle, type ThemeToggleProps, type ToggleVariant, buildConfig, cx, defaultColors, defaultTheme, flatten, getConfig, kb, parseClass, parseClasses, resolve, styled, updateConfig, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useStyles, useTheme, wrapColors };
|