@kbach/react 0.2.8 → 0.3.0
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-YDKLJFFY.mjs → chunk-Q5RMVTAU.mjs} +1 -1
- package/dist/{chunk-GN4JPJHC.mjs → chunk-V5KHX4CZ.mjs} +154 -45
- package/dist/index.d.mts +31 -7
- package/dist/index.d.ts +31 -7
- package/dist/index.js +294 -114
- package/dist/index.mjs +83 -15
- package/dist/jsx-dev-runtime.js +150 -45
- 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 +150 -45
- package/dist/jsx-runtime.mjs +2 -2
- package/package.json +4 -2
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
|
|
@@ -1802,6 +1860,37 @@ function injectRule(rule) {
|
|
|
1802
1860
|
} catch {
|
|
1803
1861
|
}
|
|
1804
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"]);
|
|
1805
1894
|
function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
1806
1895
|
const cssDecls = styleValueToCSS(styles);
|
|
1807
1896
|
if (!cssDecls) return;
|
|
@@ -1811,10 +1900,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
|
1811
1900
|
return;
|
|
1812
1901
|
}
|
|
1813
1902
|
const mods = bucketKey.split(":");
|
|
1814
|
-
const
|
|
1815
|
-
const
|
|
1816
|
-
const
|
|
1817
|
-
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}`;
|
|
1818
1911
|
let rule;
|
|
1819
1912
|
if (hasDark) {
|
|
1820
1913
|
if (darkMode === "media") {
|
|
@@ -1824,6 +1917,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
|
|
|
1824
1917
|
} else {
|
|
1825
1918
|
rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
|
|
1826
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
|
+
}
|
|
1827
1928
|
} else {
|
|
1828
1929
|
rule = `${selector} { ${cssDecls} }`;
|
|
1829
1930
|
}
|
|
@@ -1949,6 +2050,9 @@ function matchesMods(mods, isDark, state) {
|
|
|
1949
2050
|
}
|
|
1950
2051
|
});
|
|
1951
2052
|
}
|
|
2053
|
+
function clearCache() {
|
|
2054
|
+
styleCache.clear();
|
|
2055
|
+
}
|
|
1952
2056
|
|
|
1953
2057
|
// src/core/config.ts
|
|
1954
2058
|
function deepMerge(base, override) {
|
|
@@ -2004,20 +2108,24 @@ function buildConfig(userConfig) {
|
|
|
2004
2108
|
theme,
|
|
2005
2109
|
plugins: userConfig.plugins ?? []
|
|
2006
2110
|
};
|
|
2111
|
+
clearPluginUtilities();
|
|
2112
|
+
clearPluginModifiers();
|
|
2113
|
+
const pluginAPI = makePluginAPI(resolved.theme);
|
|
2007
2114
|
for (const plugin of resolved.plugins) {
|
|
2008
|
-
plugin(
|
|
2115
|
+
plugin(pluginAPI);
|
|
2009
2116
|
}
|
|
2010
2117
|
return resolved;
|
|
2011
2118
|
}
|
|
2012
2119
|
function makePluginAPI(theme) {
|
|
2013
|
-
const standalone =
|
|
2014
|
-
const resolvers =
|
|
2120
|
+
const standalone = getPluginStandaloneMap();
|
|
2121
|
+
const resolvers = getPluginResolverMap();
|
|
2015
2122
|
return {
|
|
2016
2123
|
addUtility(name, styles) {
|
|
2017
2124
|
standalone[name] = styles;
|
|
2018
2125
|
},
|
|
2019
|
-
addVariant(name,
|
|
2020
|
-
customVariants[name] =
|
|
2126
|
+
addVariant(name, selector) {
|
|
2127
|
+
customVariants[name] = selector;
|
|
2128
|
+
registerModifier(name);
|
|
2021
2129
|
},
|
|
2022
2130
|
theme(path, defaultValue) {
|
|
2023
2131
|
const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
|
|
@@ -2041,6 +2149,7 @@ function onConfigChange(listener) {
|
|
|
2041
2149
|
function updateConfig(userConfig) {
|
|
2042
2150
|
const store = getConfigStore();
|
|
2043
2151
|
store.resolved = buildConfig(userConfig);
|
|
2152
|
+
clearCache();
|
|
2044
2153
|
for (const listener of store.listeners) {
|
|
2045
2154
|
listener(store.resolved);
|
|
2046
2155
|
}
|
|
@@ -2153,8 +2262,8 @@ var InteractiveWrapper = forwardRef(
|
|
|
2153
2262
|
...restWithoutChildren,
|
|
2154
2263
|
style: finalStyle,
|
|
2155
2264
|
...isWeb && className ? { className } : {},
|
|
2156
|
-
onPressIn
|
|
2157
|
-
onPressOut: handlePressOut,
|
|
2265
|
+
// onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
|
|
2266
|
+
...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
|
|
2158
2267
|
onMouseEnter: handleMouseEnter,
|
|
2159
2268
|
onMouseLeave: handleMouseLeave,
|
|
2160
2269
|
onFocus: handleFocus,
|
package/dist/index.d.mts
CHANGED
|
@@ -139,6 +139,30 @@ 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
|
+
/**
|
|
152
|
+
* Apply opacity to any CSS color string (hex, rgb, rgba).
|
|
153
|
+
* `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
|
|
154
|
+
* `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'`
|
|
155
|
+
*/
|
|
156
|
+
alpha(color: string, opacity: number): string;
|
|
157
|
+
/**
|
|
158
|
+
* Index access returns a ColorScale for palette families or a string for flat colors.
|
|
159
|
+
* Supports shade numbers (`colors.blue[6]`), slash opacity (`colors.blue['6/50']`),
|
|
160
|
+
* and flat colors with opacity (`colors['white/20']`).
|
|
161
|
+
*/
|
|
162
|
+
[color: string]: any;
|
|
163
|
+
}
|
|
164
|
+
declare function wrapColors(rawColors: ThemeColors): ColorsAPI;
|
|
165
|
+
declare function useColors(): ColorsAPI;
|
|
142
166
|
|
|
143
167
|
interface ThemeProviderProps {
|
|
144
168
|
children: ReactNode;
|
|
@@ -249,7 +273,7 @@ declare function useResolvedStyle(classString: string | string[]): ResolvedStyle
|
|
|
249
273
|
|
|
250
274
|
/**
|
|
251
275
|
* Subscribe to the global dark-mode store with React's concurrent-safe
|
|
252
|
-
* useSyncExternalStore so that any component using className/
|
|
276
|
+
* useSyncExternalStore so that any component using className/kb re-renders
|
|
253
277
|
* immediately when the theme changes — without needing ThemeContext.
|
|
254
278
|
*/
|
|
255
279
|
declare function useGlobalDarkMode(): boolean;
|
|
@@ -272,7 +296,7 @@ interface InteractiveWrapperProps {
|
|
|
272
296
|
onBlur?: (...args: any[]) => void;
|
|
273
297
|
}
|
|
274
298
|
/**
|
|
275
|
-
* Thin wrapper rendered automatically by the JSX runtime whenever a className/
|
|
299
|
+
* Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
|
|
276
300
|
* string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
|
|
277
301
|
*
|
|
278
302
|
* Manages interaction state locally and flattens the correct style bucket on
|
|
@@ -291,20 +315,20 @@ declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<Inter
|
|
|
291
315
|
*
|
|
292
316
|
* ```ts
|
|
293
317
|
* // Inside a component use useStyles() instead.
|
|
294
|
-
* //
|
|
318
|
+
* // kb() is useful for StyleSheet.create() calls and static values.
|
|
295
319
|
*
|
|
296
320
|
* const styles = StyleSheet.create({
|
|
297
|
-
* container:
|
|
321
|
+
* container: kb('flex-1 bg-white p-4') as any,
|
|
298
322
|
* });
|
|
299
323
|
*
|
|
300
324
|
* // Web: use as className
|
|
301
|
-
* <div className={
|
|
325
|
+
* <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
|
|
302
326
|
* ```
|
|
303
327
|
*
|
|
304
328
|
* @param classString Space-separated utility classes
|
|
305
329
|
* @param isDark Whether dark mode is active (default: false)
|
|
306
330
|
*/
|
|
307
|
-
declare function
|
|
331
|
+
declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
308
332
|
/**
|
|
309
333
|
* Conditionally join class names. Falsy values are ignored.
|
|
310
334
|
*
|
|
@@ -315,4 +339,4 @@ declare function tw(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
315
339
|
*/
|
|
316
340
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
317
341
|
|
|
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,
|
|
342
|
+
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,30 @@ 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
|
+
/**
|
|
152
|
+
* Apply opacity to any CSS color string (hex, rgb, rgba).
|
|
153
|
+
* `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
|
|
154
|
+
* `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'`
|
|
155
|
+
*/
|
|
156
|
+
alpha(color: string, opacity: number): string;
|
|
157
|
+
/**
|
|
158
|
+
* Index access returns a ColorScale for palette families or a string for flat colors.
|
|
159
|
+
* Supports shade numbers (`colors.blue[6]`), slash opacity (`colors.blue['6/50']`),
|
|
160
|
+
* and flat colors with opacity (`colors['white/20']`).
|
|
161
|
+
*/
|
|
162
|
+
[color: string]: any;
|
|
163
|
+
}
|
|
164
|
+
declare function wrapColors(rawColors: ThemeColors): ColorsAPI;
|
|
165
|
+
declare function useColors(): ColorsAPI;
|
|
142
166
|
|
|
143
167
|
interface ThemeProviderProps {
|
|
144
168
|
children: ReactNode;
|
|
@@ -249,7 +273,7 @@ declare function useResolvedStyle(classString: string | string[]): ResolvedStyle
|
|
|
249
273
|
|
|
250
274
|
/**
|
|
251
275
|
* Subscribe to the global dark-mode store with React's concurrent-safe
|
|
252
|
-
* useSyncExternalStore so that any component using className/
|
|
276
|
+
* useSyncExternalStore so that any component using className/kb re-renders
|
|
253
277
|
* immediately when the theme changes — without needing ThemeContext.
|
|
254
278
|
*/
|
|
255
279
|
declare function useGlobalDarkMode(): boolean;
|
|
@@ -272,7 +296,7 @@ interface InteractiveWrapperProps {
|
|
|
272
296
|
onBlur?: (...args: any[]) => void;
|
|
273
297
|
}
|
|
274
298
|
/**
|
|
275
|
-
* Thin wrapper rendered automatically by the JSX runtime whenever a className/
|
|
299
|
+
* Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
|
|
276
300
|
* string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
|
|
277
301
|
*
|
|
278
302
|
* Manages interaction state locally and flattens the correct style bucket on
|
|
@@ -291,20 +315,20 @@ declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<Inter
|
|
|
291
315
|
*
|
|
292
316
|
* ```ts
|
|
293
317
|
* // Inside a component use useStyles() instead.
|
|
294
|
-
* //
|
|
318
|
+
* // kb() is useful for StyleSheet.create() calls and static values.
|
|
295
319
|
*
|
|
296
320
|
* const styles = StyleSheet.create({
|
|
297
|
-
* container:
|
|
321
|
+
* container: kb('flex-1 bg-white p-4') as any,
|
|
298
322
|
* });
|
|
299
323
|
*
|
|
300
324
|
* // Web: use as className
|
|
301
|
-
* <div className={
|
|
325
|
+
* <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
|
|
302
326
|
* ```
|
|
303
327
|
*
|
|
304
328
|
* @param classString Space-separated utility classes
|
|
305
329
|
* @param isDark Whether dark mode is active (default: false)
|
|
306
330
|
*/
|
|
307
|
-
declare function
|
|
331
|
+
declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
308
332
|
/**
|
|
309
333
|
* Conditionally join class names. Falsy values are ignored.
|
|
310
334
|
*
|
|
@@ -315,4 +339,4 @@ declare function tw(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
315
339
|
*/
|
|
316
340
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
317
341
|
|
|
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,
|
|
342
|
+
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 };
|