@kbach/ui 0.1.0-beta.0 → 0.1.0-beta.2
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/KBACH.md +14 -6
- package/README.md +35 -303
- package/dist/{chunk-BPCFICND.mjs → chunk-LY753XBX.mjs} +90 -18
- package/dist/{chunk-UE54W6ZG.mjs → chunk-UVXTWGHQ.mjs} +1 -1
- package/dist/core/index.d.ts +74 -16
- package/dist/core/index.js +84 -18
- package/dist/index.d.ts +5 -11
- package/dist/index.js +103 -94
- package/dist/index.mjs +14 -4
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/native.js +0 -21
- package/dist/native.mjs +49 -0
- package/dist/vite-plugin.js +105 -15
- package/dist/vite-plugin.mjs +105 -15
- package/kbach-ui.md +14 -3
- package/package.json +3 -2
- package/scripts/postinstall.js +17 -0
- package/dist/native.d.ts +0 -314
package/dist/native.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
"use strict";
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -21,31 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
20
|
// src/native/index.ts
|
|
22
21
|
var native_exports = {};
|
|
23
22
|
__export(native_exports, {
|
|
24
|
-
ThemeProvider: () => NativeThemeProvider,
|
|
25
23
|
createKbachConfig: () => createKbachConfig,
|
|
26
24
|
withKbach: () => withKbach,
|
|
27
25
|
withKbachBabel: () => withKbachBabel
|
|
28
26
|
});
|
|
29
27
|
module.exports = __toCommonJS(native_exports);
|
|
30
28
|
|
|
31
|
-
// src/native/NativeThemeProvider.tsx
|
|
32
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
-
function NativeThemeProvider(props) {
|
|
34
|
-
const { useColorScheme, useWindowDimensions } = require("react-native");
|
|
35
|
-
const { ThemeProvider } = require("@kbach/ui");
|
|
36
|
-
const raw = useColorScheme();
|
|
37
|
-
const { width } = useWindowDimensions();
|
|
38
|
-
const systemColorScheme = raw === "dark" ? "dark" : raw === "light" ? "light" : null;
|
|
39
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
40
|
-
ThemeProvider,
|
|
41
|
-
{
|
|
42
|
-
...props,
|
|
43
|
-
colorScheme: props.colorScheme ?? systemColorScheme,
|
|
44
|
-
windowWidth: props.windowWidth ?? width
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
29
|
// src/native/setup.ts
|
|
50
30
|
function warn(message) {
|
|
51
31
|
const proc = process;
|
|
@@ -92,7 +72,6 @@ function createKbachConfig(options = {}) {
|
|
|
92
72
|
}
|
|
93
73
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
74
|
0 && (module.exports = {
|
|
95
|
-
ThemeProvider,
|
|
96
75
|
createKbachConfig,
|
|
97
76
|
withKbach,
|
|
98
77
|
withKbachBabel
|
package/dist/native.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/native/setup.ts
|
|
2
|
+
function warn(message) {
|
|
3
|
+
const proc = process;
|
|
4
|
+
const useColor = !!proc.stdout?.isTTY && !proc.env.NO_COLOR;
|
|
5
|
+
const paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
6
|
+
console.warn(`${paint("1", paint("35", "[kbach]"))} ${paint("33", message)}`);
|
|
7
|
+
}
|
|
8
|
+
function withKbach(metroConfig, _options = {}) {
|
|
9
|
+
return metroConfig;
|
|
10
|
+
}
|
|
11
|
+
function withKbachBabel(babelConfig, options = {}) {
|
|
12
|
+
const {
|
|
13
|
+
configFile = "kbach.config.js",
|
|
14
|
+
attributes = ["kb", "className"],
|
|
15
|
+
debug = false
|
|
16
|
+
} = options;
|
|
17
|
+
const presets = (babelConfig.presets ?? []).map((preset) => {
|
|
18
|
+
const [name, presetOpts = {}] = Array.isArray(preset) ? preset : [preset, {}];
|
|
19
|
+
if (typeof name === "string" && (name.includes("babel-preset-expo") || name.includes("preset-react"))) {
|
|
20
|
+
const opts = presetOpts;
|
|
21
|
+
if (opts.jsxRuntime === "classic") {
|
|
22
|
+
warn(`Forcing jsxRuntime "automatic" on ${name} (was "classic") \u2014 required for Kbach`);
|
|
23
|
+
}
|
|
24
|
+
return [name, { ...opts, jsxRuntime: "automatic" }];
|
|
25
|
+
}
|
|
26
|
+
return preset;
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
...babelConfig,
|
|
30
|
+
presets: [...presets, ["@kbach/ui/babel", { configFile, attributes, debug }]]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createKbachConfig(options = {}) {
|
|
34
|
+
return {
|
|
35
|
+
presets: [
|
|
36
|
+
"babel-preset-expo",
|
|
37
|
+
["@kbach/ui/babel", {
|
|
38
|
+
configFile: options.configFile ?? "kbach.config.js",
|
|
39
|
+
attributes: options.attributes ?? ["kb", "className"],
|
|
40
|
+
debug: options.debug ?? false
|
|
41
|
+
}]
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
createKbachConfig,
|
|
47
|
+
withKbach,
|
|
48
|
+
withKbachBabel
|
|
49
|
+
};
|
package/dist/vite-plugin.js
CHANGED
|
@@ -57,7 +57,10 @@ function toNativeValue(raw) {
|
|
|
57
57
|
return raw;
|
|
58
58
|
}
|
|
59
59
|
function escapeCSSSelector(cls) {
|
|
60
|
-
|
|
60
|
+
const escaped = cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
|
|
61
|
+
if (!/^[0-9]/.test(escaped)) return escaped;
|
|
62
|
+
const hex = escaped.charCodeAt(0).toString(16);
|
|
63
|
+
return `\\${hex} ${escaped.slice(1)}`;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
// src/core/reset.ts
|
|
@@ -589,55 +592,78 @@ var defaultTheme = {
|
|
|
589
592
|
none: "none"
|
|
590
593
|
// CSS: flex: none = 0 0 auto; native: mapped to 0
|
|
591
594
|
},
|
|
595
|
+
// Each preset carries BOTH React Native's shadow*/elevation properties
|
|
596
|
+
// (used on native) and a `boxShadow` string (used on web) — see the
|
|
597
|
+
// `shadow` resolver in resolvers/effects.ts, which picks one or the other
|
|
598
|
+
// via getEffectiveIsWeb() and strips the rest. The boxShadow values are
|
|
599
|
+
// Tailwind's own default shadow scale verbatim (same offsets/blur/spread/
|
|
600
|
+
// opacity), so shadow-sm/md/lg/xl/2xl look the same as their Tailwind
|
|
601
|
+
// counterparts on web — the native shadowOffset/shadowRadius/shadowOpacity
|
|
602
|
+
// numbers were tuned independently for RN's elevation model and don't need
|
|
603
|
+
// to (and can't exactly) match the CSS values pixel-for-pixel.
|
|
592
604
|
shadow: {
|
|
593
605
|
sm: {
|
|
594
606
|
shadowColor: "#000",
|
|
595
607
|
shadowOffset: { width: 0, height: 1 },
|
|
596
608
|
shadowOpacity: 0.05,
|
|
597
609
|
shadowRadius: 2,
|
|
598
|
-
elevation: 1
|
|
610
|
+
elevation: 1,
|
|
611
|
+
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
599
612
|
},
|
|
600
613
|
DEFAULT: {
|
|
601
614
|
shadowColor: "#000",
|
|
602
615
|
shadowOffset: { width: 0, height: 2 },
|
|
603
616
|
shadowOpacity: 0.1,
|
|
604
617
|
shadowRadius: 4,
|
|
605
|
-
elevation: 2
|
|
618
|
+
elevation: 2,
|
|
619
|
+
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"
|
|
606
620
|
},
|
|
607
621
|
md: {
|
|
608
622
|
shadowColor: "#000",
|
|
609
623
|
shadowOffset: { width: 0, height: 4 },
|
|
610
624
|
shadowOpacity: 0.1,
|
|
611
625
|
shadowRadius: 8,
|
|
612
|
-
elevation: 3
|
|
626
|
+
elevation: 3,
|
|
627
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
613
628
|
},
|
|
614
629
|
lg: {
|
|
615
630
|
shadowColor: "#000",
|
|
616
631
|
shadowOffset: { width: 0, height: 8 },
|
|
617
632
|
shadowOpacity: 0.1,
|
|
618
633
|
shadowRadius: 15,
|
|
619
|
-
elevation: 4
|
|
634
|
+
elevation: 4,
|
|
635
|
+
boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
|
|
620
636
|
},
|
|
621
637
|
xl: {
|
|
622
638
|
shadowColor: "#000",
|
|
623
639
|
shadowOffset: { width: 0, height: 16 },
|
|
624
640
|
shadowOpacity: 0.1,
|
|
625
641
|
shadowRadius: 24,
|
|
626
|
-
elevation: 6
|
|
642
|
+
elevation: 6,
|
|
643
|
+
boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
627
644
|
},
|
|
628
645
|
"2xl": {
|
|
629
646
|
shadowColor: "#000",
|
|
630
647
|
shadowOffset: { width: 0, height: 24 },
|
|
631
648
|
shadowOpacity: 0.25,
|
|
632
649
|
shadowRadius: 48,
|
|
633
|
-
elevation: 8
|
|
650
|
+
elevation: 8,
|
|
651
|
+
boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
652
|
+
},
|
|
653
|
+
// Web-only — React Native has no inset-shadow equivalent (shadow*/
|
|
654
|
+
// elevation only ever draw an outer shadow), so the native side of this
|
|
655
|
+
// preset is intentionally empty rather than approximating something RN
|
|
656
|
+
// can't actually do.
|
|
657
|
+
inner: {
|
|
658
|
+
boxShadow: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)"
|
|
634
659
|
},
|
|
635
660
|
none: {
|
|
636
661
|
shadowColor: "transparent",
|
|
637
662
|
shadowOffset: { width: 0, height: 0 },
|
|
638
663
|
shadowOpacity: 0,
|
|
639
664
|
shadowRadius: 0,
|
|
640
|
-
elevation: 0
|
|
665
|
+
elevation: 0,
|
|
666
|
+
boxShadow: "0 0 #0000"
|
|
641
667
|
}
|
|
642
668
|
},
|
|
643
669
|
screens: {
|
|
@@ -2298,9 +2324,23 @@ var effectResolvers = {
|
|
|
2298
2324
|
return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
|
|
2299
2325
|
},
|
|
2300
2326
|
// ── Shadow ─────────────────────────────────────────────────────────────────
|
|
2327
|
+
// Presets carry BOTH a web `boxShadow` string and native's shadow*/elevation
|
|
2328
|
+
// properties (theme.ts) — picked apart here rather than left for
|
|
2329
|
+
// styleValueToCSS's RN_ONLY_PROPS filter to sort out, since shadowOffset is
|
|
2330
|
+
// an object (filtered out as "not a flat style value" regardless of
|
|
2331
|
+
// platform) and boxShadow would otherwise leak into native's style prop as
|
|
2332
|
+
// an RN doesn't understand (silently ignored there, but better to not hand
|
|
2333
|
+
// native a property that was never meant for it — same branch-on-platform
|
|
2334
|
+
// pattern as bg-opacity/transition/animate above).
|
|
2301
2335
|
shadow: ({ value }, { shadow }) => {
|
|
2302
2336
|
const key = value === "" ? "DEFAULT" : value;
|
|
2303
|
-
|
|
2337
|
+
const preset = shadow[key];
|
|
2338
|
+
if (!preset) return null;
|
|
2339
|
+
if (getEffectiveIsWeb()) {
|
|
2340
|
+
return preset.boxShadow !== void 0 ? { boxShadow: preset.boxShadow } : null;
|
|
2341
|
+
}
|
|
2342
|
+
const { boxShadow, ...native } = preset;
|
|
2343
|
+
return Object.keys(native).length > 0 ? native : null;
|
|
2304
2344
|
},
|
|
2305
2345
|
// ── Animations (CSS keyframe animations, web-only) ────────────────────────
|
|
2306
2346
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
@@ -2808,6 +2848,26 @@ function findOuterColon(s) {
|
|
|
2808
2848
|
return -1;
|
|
2809
2849
|
}
|
|
2810
2850
|
|
|
2851
|
+
// src/core/globalSingleton.ts
|
|
2852
|
+
function getGlobalSingleton(key, create) {
|
|
2853
|
+
const symbolKey = /* @__PURE__ */ Symbol.for(`__kbach_${key}__`);
|
|
2854
|
+
const g = globalThis;
|
|
2855
|
+
let value = g[symbolKey];
|
|
2856
|
+
if (value === void 0) {
|
|
2857
|
+
value = create();
|
|
2858
|
+
g[symbolKey] = value;
|
|
2859
|
+
}
|
|
2860
|
+
return value;
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// src/core/responsiveStore.ts
|
|
2864
|
+
var store = getGlobalSingleton("responsiveStore", () => ({
|
|
2865
|
+
width: 0,
|
|
2866
|
+
notifiedWidth: 0,
|
|
2867
|
+
screens: {},
|
|
2868
|
+
listeners: /* @__PURE__ */ new Set()
|
|
2869
|
+
}));
|
|
2870
|
+
|
|
2811
2871
|
// src/core/modeAwareColors.ts
|
|
2812
2872
|
var _modeAwareMapCache = /* @__PURE__ */ new WeakMap();
|
|
2813
2873
|
function buildModeAwareMap(colors) {
|
|
@@ -2867,9 +2927,9 @@ function expandModeAwareColorClasses(classString, colors) {
|
|
|
2867
2927
|
}
|
|
2868
2928
|
|
|
2869
2929
|
// src/core/resolver.ts
|
|
2870
|
-
var
|
|
2930
|
+
var _fontFamilyHolder = getGlobalSingleton("defaultFontFamily", () => ({ value: void 0 }));
|
|
2871
2931
|
function setDefaultFontFamily(font) {
|
|
2872
|
-
|
|
2932
|
+
_fontFamilyHolder.value = font;
|
|
2873
2933
|
}
|
|
2874
2934
|
var _styleEl = null;
|
|
2875
2935
|
var _ruleIndexByKey = /* @__PURE__ */ new Map();
|
|
@@ -2893,9 +2953,9 @@ function evictInjectedRule(rule) {
|
|
|
2893
2953
|
}
|
|
2894
2954
|
}
|
|
2895
2955
|
var _injectedRules = new LRUCache(5e4, evictInjectedRule);
|
|
2896
|
-
var
|
|
2956
|
+
var _runtimeCSSHolder = getGlobalSingleton("runtimeCSSDisabled", () => ({ value: false }));
|
|
2897
2957
|
function isRuntimeCSSDisabled() {
|
|
2898
|
-
return
|
|
2958
|
+
return _runtimeCSSHolder.value;
|
|
2899
2959
|
}
|
|
2900
2960
|
function getStyleEl() {
|
|
2901
2961
|
if (_styleEl) return _styleEl;
|
|
@@ -3118,6 +3178,10 @@ function deepMerge(base, override) {
|
|
|
3118
3178
|
}
|
|
3119
3179
|
return result;
|
|
3120
3180
|
}
|
|
3181
|
+
var configStore = getGlobalSingleton("configStore", () => ({
|
|
3182
|
+
resolved: null,
|
|
3183
|
+
listeners: /* @__PURE__ */ new Set()
|
|
3184
|
+
}));
|
|
3121
3185
|
function resolveColorRefs(colors) {
|
|
3122
3186
|
const MAX_DEPTH = 5;
|
|
3123
3187
|
function resolveChain(raw, side) {
|
|
@@ -3481,6 +3545,31 @@ function prettifyCSS(css) {
|
|
|
3481
3545
|
if (trailing) out.push(trailing);
|
|
3482
3546
|
return out.join("\n");
|
|
3483
3547
|
}
|
|
3548
|
+
var RESPONSIVE_MEDIA_RE = /^@media \(min-width: (\d+)px\) \{ (.+) \}$/;
|
|
3549
|
+
function mergeResponsiveMediaBlocks(items) {
|
|
3550
|
+
const byWidth = /* @__PURE__ */ new Map();
|
|
3551
|
+
const widthOrder = [];
|
|
3552
|
+
const unwrapped = [];
|
|
3553
|
+
for (const { raw } of items) {
|
|
3554
|
+
for (const line of raw.split("\n")) {
|
|
3555
|
+
if (!line.trim()) continue;
|
|
3556
|
+
const m = RESPONSIVE_MEDIA_RE.exec(line);
|
|
3557
|
+
if (!m) {
|
|
3558
|
+
unwrapped.push(line);
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3561
|
+
const width = Number(m[1]);
|
|
3562
|
+
if (!byWidth.has(width)) {
|
|
3563
|
+
byWidth.set(width, []);
|
|
3564
|
+
widthOrder.push(width);
|
|
3565
|
+
}
|
|
3566
|
+
byWidth.get(width).push(m[2]);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
widthOrder.sort((a, b) => a - b);
|
|
3570
|
+
const merged = widthOrder.map((w) => `@media (min-width: ${w}px) { ${byWidth.get(w).join(" ")} }`);
|
|
3571
|
+
return [...unwrapped, ...merged];
|
|
3572
|
+
}
|
|
3484
3573
|
function formatKbachCSS(tokenCSS, theme, responsiveRe) {
|
|
3485
3574
|
const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
|
|
3486
3575
|
const colorVars = buildColorVarMap(theme, allRaw);
|
|
@@ -3500,7 +3589,7 @@ function formatKbachCSS(tokenCSS, theme, responsiveRe) {
|
|
|
3500
3589
|
if (!groups.has(label)) groups.set(label, []);
|
|
3501
3590
|
const parsedToken = parseClass(token);
|
|
3502
3591
|
const bucketKey = parsedToken && parsedToken.modifiers.length > 0 ? parsedToken.modifiers.join(":") : "base";
|
|
3503
|
-
groups.get(label).push({ order: getModifierOrder(bucketKey),
|
|
3592
|
+
groups.get(label).push({ order: getModifierOrder(bucketKey), raw: applyVars(css) });
|
|
3504
3593
|
}
|
|
3505
3594
|
const out = ["/* Generated by Kbach \u2014 do not edit */"];
|
|
3506
3595
|
if (declarations.size > 0) {
|
|
@@ -3518,7 +3607,8 @@ ${prettifyCSS(globalCSS)}`);
|
|
|
3518
3607
|
const sorted = [...items].sort((a, b) => a.order - b.order);
|
|
3519
3608
|
out.push(`
|
|
3520
3609
|
/* ${label} */`);
|
|
3521
|
-
|
|
3610
|
+
const rendered = label === "Responsive" ? mergeResponsiveMediaBlocks(sorted).map(prettifyCSS) : sorted.map((it) => prettifyCSS(it.raw));
|
|
3611
|
+
out.push(rendered.join("\n\n"));
|
|
3522
3612
|
}
|
|
3523
3613
|
return out.join("\n");
|
|
3524
3614
|
}
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -22,7 +22,10 @@ function toNativeValue(raw) {
|
|
|
22
22
|
return raw;
|
|
23
23
|
}
|
|
24
24
|
function escapeCSSSelector(cls) {
|
|
25
|
-
|
|
25
|
+
const escaped = cls.replace(/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
|
|
26
|
+
if (!/^[0-9]/.test(escaped)) return escaped;
|
|
27
|
+
const hex = escaped.charCodeAt(0).toString(16);
|
|
28
|
+
return `\\${hex} ${escaped.slice(1)}`;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
// src/core/reset.ts
|
|
@@ -554,55 +557,78 @@ var defaultTheme = {
|
|
|
554
557
|
none: "none"
|
|
555
558
|
// CSS: flex: none = 0 0 auto; native: mapped to 0
|
|
556
559
|
},
|
|
560
|
+
// Each preset carries BOTH React Native's shadow*/elevation properties
|
|
561
|
+
// (used on native) and a `boxShadow` string (used on web) — see the
|
|
562
|
+
// `shadow` resolver in resolvers/effects.ts, which picks one or the other
|
|
563
|
+
// via getEffectiveIsWeb() and strips the rest. The boxShadow values are
|
|
564
|
+
// Tailwind's own default shadow scale verbatim (same offsets/blur/spread/
|
|
565
|
+
// opacity), so shadow-sm/md/lg/xl/2xl look the same as their Tailwind
|
|
566
|
+
// counterparts on web — the native shadowOffset/shadowRadius/shadowOpacity
|
|
567
|
+
// numbers were tuned independently for RN's elevation model and don't need
|
|
568
|
+
// to (and can't exactly) match the CSS values pixel-for-pixel.
|
|
557
569
|
shadow: {
|
|
558
570
|
sm: {
|
|
559
571
|
shadowColor: "#000",
|
|
560
572
|
shadowOffset: { width: 0, height: 1 },
|
|
561
573
|
shadowOpacity: 0.05,
|
|
562
574
|
shadowRadius: 2,
|
|
563
|
-
elevation: 1
|
|
575
|
+
elevation: 1,
|
|
576
|
+
boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
564
577
|
},
|
|
565
578
|
DEFAULT: {
|
|
566
579
|
shadowColor: "#000",
|
|
567
580
|
shadowOffset: { width: 0, height: 2 },
|
|
568
581
|
shadowOpacity: 0.1,
|
|
569
582
|
shadowRadius: 4,
|
|
570
|
-
elevation: 2
|
|
583
|
+
elevation: 2,
|
|
584
|
+
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"
|
|
571
585
|
},
|
|
572
586
|
md: {
|
|
573
587
|
shadowColor: "#000",
|
|
574
588
|
shadowOffset: { width: 0, height: 4 },
|
|
575
589
|
shadowOpacity: 0.1,
|
|
576
590
|
shadowRadius: 8,
|
|
577
|
-
elevation: 3
|
|
591
|
+
elevation: 3,
|
|
592
|
+
boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
|
|
578
593
|
},
|
|
579
594
|
lg: {
|
|
580
595
|
shadowColor: "#000",
|
|
581
596
|
shadowOffset: { width: 0, height: 8 },
|
|
582
597
|
shadowOpacity: 0.1,
|
|
583
598
|
shadowRadius: 15,
|
|
584
|
-
elevation: 4
|
|
599
|
+
elevation: 4,
|
|
600
|
+
boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
|
|
585
601
|
},
|
|
586
602
|
xl: {
|
|
587
603
|
shadowColor: "#000",
|
|
588
604
|
shadowOffset: { width: 0, height: 16 },
|
|
589
605
|
shadowOpacity: 0.1,
|
|
590
606
|
shadowRadius: 24,
|
|
591
|
-
elevation: 6
|
|
607
|
+
elevation: 6,
|
|
608
|
+
boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
592
609
|
},
|
|
593
610
|
"2xl": {
|
|
594
611
|
shadowColor: "#000",
|
|
595
612
|
shadowOffset: { width: 0, height: 24 },
|
|
596
613
|
shadowOpacity: 0.25,
|
|
597
614
|
shadowRadius: 48,
|
|
598
|
-
elevation: 8
|
|
615
|
+
elevation: 8,
|
|
616
|
+
boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
617
|
+
},
|
|
618
|
+
// Web-only — React Native has no inset-shadow equivalent (shadow*/
|
|
619
|
+
// elevation only ever draw an outer shadow), so the native side of this
|
|
620
|
+
// preset is intentionally empty rather than approximating something RN
|
|
621
|
+
// can't actually do.
|
|
622
|
+
inner: {
|
|
623
|
+
boxShadow: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)"
|
|
599
624
|
},
|
|
600
625
|
none: {
|
|
601
626
|
shadowColor: "transparent",
|
|
602
627
|
shadowOffset: { width: 0, height: 0 },
|
|
603
628
|
shadowOpacity: 0,
|
|
604
629
|
shadowRadius: 0,
|
|
605
|
-
elevation: 0
|
|
630
|
+
elevation: 0,
|
|
631
|
+
boxShadow: "0 0 #0000"
|
|
606
632
|
}
|
|
607
633
|
},
|
|
608
634
|
screens: {
|
|
@@ -2263,9 +2289,23 @@ var effectResolvers = {
|
|
|
2263
2289
|
return isNaN(n) ? null : { opacity: n > 1 ? n / 100 : n };
|
|
2264
2290
|
},
|
|
2265
2291
|
// ── Shadow ─────────────────────────────────────────────────────────────────
|
|
2292
|
+
// Presets carry BOTH a web `boxShadow` string and native's shadow*/elevation
|
|
2293
|
+
// properties (theme.ts) — picked apart here rather than left for
|
|
2294
|
+
// styleValueToCSS's RN_ONLY_PROPS filter to sort out, since shadowOffset is
|
|
2295
|
+
// an object (filtered out as "not a flat style value" regardless of
|
|
2296
|
+
// platform) and boxShadow would otherwise leak into native's style prop as
|
|
2297
|
+
// an RN doesn't understand (silently ignored there, but better to not hand
|
|
2298
|
+
// native a property that was never meant for it — same branch-on-platform
|
|
2299
|
+
// pattern as bg-opacity/transition/animate above).
|
|
2266
2300
|
shadow: ({ value }, { shadow }) => {
|
|
2267
2301
|
const key = value === "" ? "DEFAULT" : value;
|
|
2268
|
-
|
|
2302
|
+
const preset = shadow[key];
|
|
2303
|
+
if (!preset) return null;
|
|
2304
|
+
if (getEffectiveIsWeb()) {
|
|
2305
|
+
return preset.boxShadow !== void 0 ? { boxShadow: preset.boxShadow } : null;
|
|
2306
|
+
}
|
|
2307
|
+
const { boxShadow, ...native } = preset;
|
|
2308
|
+
return Object.keys(native).length > 0 ? native : null;
|
|
2269
2309
|
},
|
|
2270
2310
|
// ── Animations (CSS keyframe animations, web-only) ────────────────────────
|
|
2271
2311
|
// Each variant injects a @keyframes rule once via the __keyframe marker.
|
|
@@ -2773,6 +2813,26 @@ function findOuterColon(s) {
|
|
|
2773
2813
|
return -1;
|
|
2774
2814
|
}
|
|
2775
2815
|
|
|
2816
|
+
// src/core/globalSingleton.ts
|
|
2817
|
+
function getGlobalSingleton(key, create) {
|
|
2818
|
+
const symbolKey = /* @__PURE__ */ Symbol.for(`__kbach_${key}__`);
|
|
2819
|
+
const g = globalThis;
|
|
2820
|
+
let value = g[symbolKey];
|
|
2821
|
+
if (value === void 0) {
|
|
2822
|
+
value = create();
|
|
2823
|
+
g[symbolKey] = value;
|
|
2824
|
+
}
|
|
2825
|
+
return value;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
// src/core/responsiveStore.ts
|
|
2829
|
+
var store = getGlobalSingleton("responsiveStore", () => ({
|
|
2830
|
+
width: 0,
|
|
2831
|
+
notifiedWidth: 0,
|
|
2832
|
+
screens: {},
|
|
2833
|
+
listeners: /* @__PURE__ */ new Set()
|
|
2834
|
+
}));
|
|
2835
|
+
|
|
2776
2836
|
// src/core/modeAwareColors.ts
|
|
2777
2837
|
var _modeAwareMapCache = /* @__PURE__ */ new WeakMap();
|
|
2778
2838
|
function buildModeAwareMap(colors) {
|
|
@@ -2832,9 +2892,9 @@ function expandModeAwareColorClasses(classString, colors) {
|
|
|
2832
2892
|
}
|
|
2833
2893
|
|
|
2834
2894
|
// src/core/resolver.ts
|
|
2835
|
-
var
|
|
2895
|
+
var _fontFamilyHolder = getGlobalSingleton("defaultFontFamily", () => ({ value: void 0 }));
|
|
2836
2896
|
function setDefaultFontFamily(font) {
|
|
2837
|
-
|
|
2897
|
+
_fontFamilyHolder.value = font;
|
|
2838
2898
|
}
|
|
2839
2899
|
var _styleEl = null;
|
|
2840
2900
|
var _ruleIndexByKey = /* @__PURE__ */ new Map();
|
|
@@ -2858,9 +2918,9 @@ function evictInjectedRule(rule) {
|
|
|
2858
2918
|
}
|
|
2859
2919
|
}
|
|
2860
2920
|
var _injectedRules = new LRUCache(5e4, evictInjectedRule);
|
|
2861
|
-
var
|
|
2921
|
+
var _runtimeCSSHolder = getGlobalSingleton("runtimeCSSDisabled", () => ({ value: false }));
|
|
2862
2922
|
function isRuntimeCSSDisabled() {
|
|
2863
|
-
return
|
|
2923
|
+
return _runtimeCSSHolder.value;
|
|
2864
2924
|
}
|
|
2865
2925
|
function getStyleEl() {
|
|
2866
2926
|
if (_styleEl) return _styleEl;
|
|
@@ -3083,6 +3143,10 @@ function deepMerge(base, override) {
|
|
|
3083
3143
|
}
|
|
3084
3144
|
return result;
|
|
3085
3145
|
}
|
|
3146
|
+
var configStore = getGlobalSingleton("configStore", () => ({
|
|
3147
|
+
resolved: null,
|
|
3148
|
+
listeners: /* @__PURE__ */ new Set()
|
|
3149
|
+
}));
|
|
3086
3150
|
function resolveColorRefs(colors) {
|
|
3087
3151
|
const MAX_DEPTH = 5;
|
|
3088
3152
|
function resolveChain(raw, side) {
|
|
@@ -3446,6 +3510,31 @@ function prettifyCSS(css) {
|
|
|
3446
3510
|
if (trailing) out.push(trailing);
|
|
3447
3511
|
return out.join("\n");
|
|
3448
3512
|
}
|
|
3513
|
+
var RESPONSIVE_MEDIA_RE = /^@media \(min-width: (\d+)px\) \{ (.+) \}$/;
|
|
3514
|
+
function mergeResponsiveMediaBlocks(items) {
|
|
3515
|
+
const byWidth = /* @__PURE__ */ new Map();
|
|
3516
|
+
const widthOrder = [];
|
|
3517
|
+
const unwrapped = [];
|
|
3518
|
+
for (const { raw } of items) {
|
|
3519
|
+
for (const line of raw.split("\n")) {
|
|
3520
|
+
if (!line.trim()) continue;
|
|
3521
|
+
const m = RESPONSIVE_MEDIA_RE.exec(line);
|
|
3522
|
+
if (!m) {
|
|
3523
|
+
unwrapped.push(line);
|
|
3524
|
+
continue;
|
|
3525
|
+
}
|
|
3526
|
+
const width = Number(m[1]);
|
|
3527
|
+
if (!byWidth.has(width)) {
|
|
3528
|
+
byWidth.set(width, []);
|
|
3529
|
+
widthOrder.push(width);
|
|
3530
|
+
}
|
|
3531
|
+
byWidth.get(width).push(m[2]);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
widthOrder.sort((a, b) => a - b);
|
|
3535
|
+
const merged = widthOrder.map((w) => `@media (min-width: ${w}px) { ${byWidth.get(w).join(" ")} }`);
|
|
3536
|
+
return [...unwrapped, ...merged];
|
|
3537
|
+
}
|
|
3449
3538
|
function formatKbachCSS(tokenCSS, theme, responsiveRe) {
|
|
3450
3539
|
const allRaw = [...tokenCSS.values()].filter(Boolean).join("\n");
|
|
3451
3540
|
const colorVars = buildColorVarMap(theme, allRaw);
|
|
@@ -3465,7 +3554,7 @@ function formatKbachCSS(tokenCSS, theme, responsiveRe) {
|
|
|
3465
3554
|
if (!groups.has(label)) groups.set(label, []);
|
|
3466
3555
|
const parsedToken = parseClass(token);
|
|
3467
3556
|
const bucketKey = parsedToken && parsedToken.modifiers.length > 0 ? parsedToken.modifiers.join(":") : "base";
|
|
3468
|
-
groups.get(label).push({ order: getModifierOrder(bucketKey),
|
|
3557
|
+
groups.get(label).push({ order: getModifierOrder(bucketKey), raw: applyVars(css) });
|
|
3469
3558
|
}
|
|
3470
3559
|
const out = ["/* Generated by Kbach \u2014 do not edit */"];
|
|
3471
3560
|
if (declarations.size > 0) {
|
|
@@ -3483,7 +3572,8 @@ ${prettifyCSS(globalCSS)}`);
|
|
|
3483
3572
|
const sorted = [...items].sort((a, b) => a.order - b.order);
|
|
3484
3573
|
out.push(`
|
|
3485
3574
|
/* ${label} */`);
|
|
3486
|
-
|
|
3575
|
+
const rendered = label === "Responsive" ? mergeResponsiveMediaBlocks(sorted).map(prettifyCSS) : sorted.map((it) => prettifyCSS(it.raw));
|
|
3576
|
+
out.push(rendered.join("\n\n"));
|
|
3487
3577
|
}
|
|
3488
3578
|
return out.join("\n");
|
|
3489
3579
|
}
|
package/kbach-ui.md
CHANGED
|
@@ -84,10 +84,10 @@ module.exports = function (api) {
|
|
|
84
84
|
One-liner: `const { createKbachConfig } = require('@kbach/ui/native'); module.exports = createKbachConfig();`. Merge into an existing config with `withKbachBabel({ presets: [...] })` (same module). After editing this file: `npx expo start --clear`.
|
|
85
85
|
|
|
86
86
|
```jsx
|
|
87
|
-
import { ThemeProvider } from '@kbach/ui
|
|
87
|
+
import { ThemeProvider } from '@kbach/ui';
|
|
88
88
|
<ThemeProvider defaultMode="system"><App /></ThemeProvider>
|
|
89
89
|
```
|
|
90
|
-
|
|
90
|
+
`ThemeProvider` auto-detects React Native at render time and reads `useColorScheme()`/`useWindowDimensions()` automatically — same import as web, no `/native` subpath needed.
|
|
91
91
|
|
|
92
92
|
`disablePersistence` on `<ThemeProvider>` saves to `AsyncStorage` on native (vs. `localStorage` on web) — same prop, platform-appropriate storage.
|
|
93
93
|
|
|
@@ -586,8 +586,9 @@ rounded-t/r/b/l rounded-tl/tr/bl/br
|
|
|
586
586
|
|
|
587
587
|
### Shadow
|
|
588
588
|
```
|
|
589
|
-
shadow-sm / shadow / shadow-md / shadow-lg / shadow-xl / shadow-2xl / shadow-none
|
|
589
|
+
shadow-sm / shadow / shadow-md / shadow-lg / shadow-xl / shadow-2xl / shadow-inner / shadow-none
|
|
590
590
|
```
|
|
591
|
+
Web: real `box-shadow` (Tailwind's own default values). Native: RN's shadowColor/shadowOffset/shadowOpacity/shadowRadius/elevation, tuned independently for RN's elevation model — not derived from the web value, so they don't need to match pixel-for-pixel. `shadow-inner` is web-only (RN has no inset-shadow equivalent). Customize or add sizes via `theme.extend.shadow` — see [Theme Configuration](#theme-configuration) below; each preset key merges independently, so overriding one size's `boxShadow` doesn't touch its native properties or any other size.
|
|
591
592
|
|
|
592
593
|
### Opacity
|
|
593
594
|
```
|
|
@@ -740,6 +741,16 @@ module.exports = {
|
|
|
740
741
|
fontFamily: {
|
|
741
742
|
sans: 'Inter, sans-serif',
|
|
742
743
|
},
|
|
744
|
+
shadow: {
|
|
745
|
+
// Deep-merged into the existing preset, not replaced — lg keeps its
|
|
746
|
+
// native shadowColor/shadowOffset/shadowOpacity/shadowRadius/
|
|
747
|
+
// elevation, only boxShadow (the web value) changes here.
|
|
748
|
+
lg: { boxShadow: '0 10px 40px -10px rgba(99, 102, 241, 0.4)' },
|
|
749
|
+
// A brand-new key adds a new shadow-3xl utility alongside the
|
|
750
|
+
// defaults (sm/DEFAULT/md/lg/xl/2xl/inner/none) — web-only here,
|
|
751
|
+
// since it has no native shadow*/elevation properties.
|
|
752
|
+
'3xl': { boxShadow: '0 35px 60px -15px rgba(0, 0, 0, 0.3)' },
|
|
753
|
+
},
|
|
743
754
|
},
|
|
744
755
|
},
|
|
745
756
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kbach/ui",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
4
|
"description": "Tailwind-like utility classes and components for React — web, React Native, and Expo",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"source": "./src/index.ts",
|
|
20
|
+
"react-native": "./dist/index.js",
|
|
20
21
|
"import": "./dist/index.mjs",
|
|
21
22
|
"require": "./dist/index.js"
|
|
22
23
|
},
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"./native": {
|
|
45
46
|
"types": "./dist/native.d.ts",
|
|
46
47
|
"source": "./src/native/index.ts",
|
|
47
|
-
"import": "./dist/native.
|
|
48
|
+
"import": "./dist/native.mjs",
|
|
48
49
|
"require": "./dist/native.js"
|
|
49
50
|
},
|
|
50
51
|
"./babel": "./src/native/babel/index.js",
|
package/scripts/postinstall.js
CHANGED
|
@@ -12,6 +12,23 @@ if (!projectRoot) process.exit(0);
|
|
|
12
12
|
// Safety: never write outside the user's project (e.g. if somehow inside node_modules)
|
|
13
13
|
if (projectRoot.includes('node_modules')) process.exit(0);
|
|
14
14
|
|
|
15
|
+
// Skip when INIT_CWD is this very monorepo's own root — any `npm install`/
|
|
16
|
+
// `npm version`/etc. run at the repo root re-triggers this script (since
|
|
17
|
+
// @kbach/ui is a workspace dependency of the root package too), which used
|
|
18
|
+
// to keep re-creating a KBACH.md at the repo root that isn't needed there
|
|
19
|
+
// (this repo's own copy already lives in packages/ui/KBACH.md — the actual
|
|
20
|
+
// source this file gets copied FROM). Detected by package name + workspaces
|
|
21
|
+
// rather than a path comparison, since this script runs from inside
|
|
22
|
+
// node_modules/@kbach/ui in a real consumer install, where a literal path
|
|
23
|
+
// match to the monorepo source tree wouldn't exist to compare against.
|
|
24
|
+
try {
|
|
25
|
+
const rootPkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf-8'));
|
|
26
|
+
if (rootPkg.name === 'kbach' && Array.isArray(rootPkg.workspaces)) process.exit(0);
|
|
27
|
+
} catch {
|
|
28
|
+
// No readable package.json at projectRoot, or it doesn't parse — not our
|
|
29
|
+
// monorepo root either way, fall through to the normal consumer-install path.
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
// Don't overwrite an existing file — user may have customised it.
|
|
16
33
|
const dest = path.join(projectRoot, 'KBACH.md');
|
|
17
34
|
if (fs.existsSync(dest)) process.exit(0);
|