@kbach/react 0.6.51 → 1.0.0-beta.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/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  "use strict";
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
@@ -29,1163 +28,681 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
29
 
31
30
  // src/index.ts
32
- var index_exports = {};
33
- __export(index_exports, {
34
- InteractiveWrapper: () => InteractiveWrapper,
31
+ var src_exports = {};
32
+ __export(src_exports, {
35
33
  KbachReset: () => KbachReset,
36
- ThemeContext: () => ThemeContext,
37
34
  ThemeProvider: () => ThemeProvider,
38
- buildConfig: () => import_core12.buildConfig,
39
- clearCache: () => import_core12.clearCache,
40
- cx: () => cx,
41
- defaultColors: () => import_core12.defaultColors,
42
- defaultTheme: () => import_core12.defaultTheme,
43
- disableRuntimeCSS: () => import_core12.disableRuntimeCSS,
44
- flatten: () => import_core12.flatten,
45
- generateKbachTypesDts: () => import_core12.generateKbachTypesDts,
46
- getConfig: () => import_core12.getConfig,
47
- initConfig: () => import_core12.initConfig,
35
+ defaultTheme: () => defaultTheme,
36
+ disableRuntimeCSS: () => disableRuntimeCSS,
37
+ getGlobalDarkMode: () => getGlobalDarkMode,
38
+ getGlobalThemeMode: () => getGlobalThemeMode,
39
+ getTheme: () => getTheme,
40
+ initKbach: () => initKbach,
41
+ isKbachReady: () => isKbachReady,
42
+ isRuntimeCSSDisabled: () => isRuntimeCSSDisabled,
48
43
  kb: () => kb,
49
- normalizeClassString: () => import_core12.normalizeClassString,
50
- parseClass: () => import_core12.parseClass,
51
- parseClasses: () => import_core12.parseClasses,
52
- registerWebElement: () => registerWebElement,
53
- resolve: () => import_core12.resolve,
54
- setResolveTarget: () => import_core12.setResolveTarget,
55
- splitClassTokens: () => import_core12.splitClassTokens,
56
- styled: () => styled,
57
- updateConfig: () => import_core12.updateConfig,
58
- useBreakpoint: () => useBreakpoint,
59
- useColors: () => useColors,
44
+ setGlobalThemeMode: () => setGlobalThemeMode,
45
+ setTheme: () => setTheme,
46
+ subscribeGlobalDarkMode: () => subscribeGlobalDarkMode,
47
+ toggleGlobalDarkMode: () => toggleGlobalDarkMode,
60
48
  useGlobalDarkMode: () => useGlobalDarkMode,
61
- useIsDark: () => useIsDark,
62
- useResolvedStyle: () => useResolvedStyle,
63
- useResponsive: () => useResponsive,
64
- useSpacing: () => useSpacing,
65
- useStyles: () => useStyles,
66
- useTheme: () => useTheme,
67
- wrapColors: () => wrapColors,
68
- wrapSpacing: () => wrapSpacing
49
+ useTheme: () => useTheme
69
50
  });
70
- module.exports = __toCommonJS(index_exports);
51
+ module.exports = __toCommonJS(src_exports);
71
52
 
72
- // src/context.tsx
73
- var import_react = require("react");
74
- var ThemeContext = globalThis.__kbachThemeContext ?? (globalThis.__kbachThemeContext = (0, import_react.createContext)(null));
75
- function useTheme() {
76
- const ctx = (0, import_react.useContext)(ThemeContext);
77
- if (!ctx) {
78
- throw new Error(
79
- "[Kbach] useTheme() must be called inside a <ThemeProvider>. Wrap your app root with <ThemeProvider>."
80
- );
53
+ // src/wasmLoader.ts
54
+ var import_core_engine = __toESM(require("@kbach/core-engine"));
55
+ var readyPromise = null;
56
+ var ready = false;
57
+ function initKbach() {
58
+ if (!readyPromise) {
59
+ readyPromise = (0, import_core_engine.default)().then(() => {
60
+ ready = true;
61
+ });
81
62
  }
82
- return ctx;
63
+ return readyPromise;
83
64
  }
84
- function useIsDark() {
85
- return useTheme().isDark;
65
+ function isKbachReady() {
66
+ return ready;
86
67
  }
87
-
88
- // src/useColors.ts
89
- var import_react2 = require("react");
90
- var import_core = require("./core");
91
- function applyOpacity(color, opacity) {
92
- const a = Math.max(0, Math.min(1, opacity / 100));
93
- if (color.startsWith("#")) {
94
- const rgb = (0, import_core.parseHexRgb)(color);
95
- if (!rgb) return color;
96
- const [r, g, b] = rgb;
97
- return `rgba(${r},${g},${b},${a})`;
68
+ function generateCss(classString, themeJson) {
69
+ if (!ready) {
70
+ throw new Error(
71
+ "[kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
72
+ );
98
73
  }
99
- if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
100
- if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
101
- return color;
102
- }
103
- function pickSide(value, isDark) {
104
- return (0, import_core.isModeAwareColor)(value) ? isDark ? value.dark : value.light : value;
105
- }
106
- function makeShadeProxy(shades, isDark) {
107
- return new Proxy(shades, {
108
- get(target, prop) {
109
- const key = String(prop);
110
- if (key === "then") return void 0;
111
- if (key.includes("/")) {
112
- const slash = key.indexOf("/");
113
- const shade = key.slice(0, slash);
114
- const op = Number(key.slice(slash + 1));
115
- const color2 = target[shade];
116
- return color2 !== void 0 ? applyOpacity(pickSide(color2, isDark), op) : void 0;
117
- }
118
- const color = target[key];
119
- return color !== void 0 ? pickSide(color, isDark) : void 0;
120
- }
121
- });
122
- }
123
- function wrapColors(rawColors, isDark = false) {
124
- const cache = /* @__PURE__ */ new Map();
125
- const alpha = (color, opacity) => opacity === void 0 ? color : applyOpacity(color, opacity);
126
- return new Proxy({ alpha }, {
127
- get(_, prop) {
128
- const key = String(prop);
129
- if (key === "then") return void 0;
130
- if (key === "alpha") return alpha;
131
- if (key.includes("/")) {
132
- const slash = key.indexOf("/");
133
- const name = key.slice(0, slash);
134
- const op = Number(key.slice(slash + 1));
135
- const entry2 = rawColors[name];
136
- return entry2 !== void 0 && (typeof entry2 === "string" || (0, import_core.isModeAwareColor)(entry2)) ? applyOpacity(pickSide(entry2, isDark), op) : void 0;
137
- }
138
- const entry = rawColors[key];
139
- if (entry === void 0) return void 0;
140
- if (typeof entry === "string" || (0, import_core.isModeAwareColor)(entry)) return pickSide(entry, isDark);
141
- const cacheKey = `${key}:${isDark}`;
142
- if (!cache.has(cacheKey)) cache.set(cacheKey, makeShadeProxy(entry, isDark));
143
- return cache.get(cacheKey);
144
- }
145
- });
146
- }
147
- function useColors() {
148
- const { config, isDark } = useTheme();
149
- return (0, import_react2.useMemo)(() => wrapColors(config.theme.colors, isDark), [config.theme.colors, isDark]);
74
+ return (0, import_core_engine.generate_css)(classString, themeJson);
150
75
  }
151
76
 
152
- // src/useSpacing.ts
153
- var import_react3 = require("react");
154
- function wrapSpacing(rawSpacing) {
155
- return rawSpacing;
77
+ // src/generatedPalette.ts
78
+ var PALETTE = {
79
+ "amber-1": "#fffbeb",
80
+ "amber-10": "#78350f",
81
+ "amber-11": "#451a03",
82
+ "amber-12": "#291002",
83
+ "amber-2": "#fef3c7",
84
+ "amber-3": "#fde68a",
85
+ "amber-4": "#fcd34d",
86
+ "amber-5": "#fbbf24",
87
+ "amber-6": "#f59e0b",
88
+ "amber-7": "#d97706",
89
+ "amber-8": "#b45309",
90
+ "amber-9": "#92400e",
91
+ "black": "#000000",
92
+ "blue-1": "#eff6ff",
93
+ "blue-10": "#1e3a8a",
94
+ "blue-11": "#172554",
95
+ "blue-12": "#0d1633",
96
+ "blue-2": "#dbeafe",
97
+ "blue-3": "#bfdbfe",
98
+ "blue-4": "#93c5fd",
99
+ "blue-5": "#60a5fa",
100
+ "blue-6": "#3b82f6",
101
+ "blue-7": "#2563eb",
102
+ "blue-8": "#1d4ed8",
103
+ "blue-9": "#1e40af",
104
+ "bronze": "#cd7f32",
105
+ "coal": "#1c1c1c",
106
+ "copper": "#b87333",
107
+ "current": "currentColor",
108
+ "cyan-1": "#ecfeff",
109
+ "cyan-10": "#164e63",
110
+ "cyan-11": "#083344",
111
+ "cyan-12": "#041e28",
112
+ "cyan-2": "#cffafe",
113
+ "cyan-3": "#a5f3fc",
114
+ "cyan-4": "#67e8f9",
115
+ "cyan-5": "#22d3ee",
116
+ "cyan-6": "#06b6d4",
117
+ "cyan-7": "#0891b2",
118
+ "cyan-8": "#0e7490",
119
+ "cyan-9": "#155e75",
120
+ "emerald-1": "#ecfdf5",
121
+ "emerald-10": "#064e3b",
122
+ "emerald-11": "#022c22",
123
+ "emerald-12": "#011a14",
124
+ "emerald-2": "#d1fae5",
125
+ "emerald-3": "#a7f3d0",
126
+ "emerald-4": "#6ee7b7",
127
+ "emerald-5": "#34d399",
128
+ "emerald-6": "#10b981",
129
+ "emerald-7": "#059669",
130
+ "emerald-8": "#047857",
131
+ "emerald-9": "#065f46",
132
+ "fuchsia-1": "#fdf4ff",
133
+ "fuchsia-10": "#701a75",
134
+ "fuchsia-11": "#4a044e",
135
+ "fuchsia-12": "#2d022f",
136
+ "fuchsia-2": "#fae8ff",
137
+ "fuchsia-3": "#f5d0fe",
138
+ "fuchsia-4": "#f0abfc",
139
+ "fuchsia-5": "#e879f9",
140
+ "fuchsia-6": "#d946ef",
141
+ "fuchsia-7": "#c026d3",
142
+ "fuchsia-8": "#a21caf",
143
+ "fuchsia-9": "#86198f",
144
+ "gold": "#d4af37",
145
+ "gray-1": "#f9fafb",
146
+ "gray-10": "#111827",
147
+ "gray-11": "#030712",
148
+ "gray-12": "#020409",
149
+ "gray-2": "#f3f4f6",
150
+ "gray-3": "#e5e7eb",
151
+ "gray-4": "#d1d5db",
152
+ "gray-5": "#9ca3af",
153
+ "gray-6": "#6b7280",
154
+ "gray-7": "#4b5563",
155
+ "gray-8": "#374151",
156
+ "gray-9": "#1f2937",
157
+ "green-1": "#f0fdf4",
158
+ "green-10": "#14532d",
159
+ "green-11": "#052e16",
160
+ "green-12": "#031b0d",
161
+ "green-2": "#dcfce7",
162
+ "green-3": "#bbf7d0",
163
+ "green-4": "#86efac",
164
+ "green-5": "#4ade80",
165
+ "green-6": "#22c55e",
166
+ "green-7": "#16a34a",
167
+ "green-8": "#15803d",
168
+ "green-9": "#166534",
169
+ "indigo-1": "#eef2ff",
170
+ "indigo-10": "#312e81",
171
+ "indigo-11": "#1e1b4b",
172
+ "indigo-12": "#12102d",
173
+ "indigo-2": "#e0e7ff",
174
+ "indigo-3": "#c7d2fe",
175
+ "indigo-4": "#a5b4fc",
176
+ "indigo-5": "#818cf8",
177
+ "indigo-6": "#6366f1",
178
+ "indigo-7": "#4f46e5",
179
+ "indigo-8": "#4338ca",
180
+ "indigo-9": "#3730a3",
181
+ "lime-1": "#f7fee7",
182
+ "lime-10": "#365314",
183
+ "lime-11": "#1a2e05",
184
+ "lime-12": "#0f1b03",
185
+ "lime-2": "#ecfccb",
186
+ "lime-3": "#d9f99d",
187
+ "lime-4": "#bef264",
188
+ "lime-5": "#a3e635",
189
+ "lime-6": "#84cc16",
190
+ "lime-7": "#65a30d",
191
+ "lime-8": "#4d7c0f",
192
+ "lime-9": "#3f6212",
193
+ "neutral-1": "#fafafa",
194
+ "neutral-10": "#171717",
195
+ "neutral-11": "#0a0a0a",
196
+ "neutral-12": "#050505",
197
+ "neutral-2": "#f5f5f5",
198
+ "neutral-3": "#e5e5e5",
199
+ "neutral-4": "#d4d4d4",
200
+ "neutral-5": "#a3a3a3",
201
+ "neutral-6": "#737373",
202
+ "neutral-7": "#525252",
203
+ "neutral-8": "#404040",
204
+ "neutral-9": "#262626",
205
+ "orange-1": "#fff7ed",
206
+ "orange-10": "#7c2d12",
207
+ "orange-11": "#431407",
208
+ "orange-12": "#270c04",
209
+ "orange-2": "#ffedd5",
210
+ "orange-3": "#fed7aa",
211
+ "orange-4": "#fdba74",
212
+ "orange-5": "#fb923c",
213
+ "orange-6": "#f97316",
214
+ "orange-7": "#ea580c",
215
+ "orange-8": "#c2410c",
216
+ "orange-9": "#9a3412",
217
+ "pink-1": "#fdf2f8",
218
+ "pink-10": "#831843",
219
+ "pink-11": "#500724",
220
+ "pink-12": "#300415",
221
+ "pink-2": "#fce7f3",
222
+ "pink-3": "#fbcfe8",
223
+ "pink-4": "#f9a8d4",
224
+ "pink-5": "#f472b6",
225
+ "pink-6": "#ec4899",
226
+ "pink-7": "#db2777",
227
+ "pink-8": "#be185d",
228
+ "pink-9": "#9d174d",
229
+ "purple-1": "#faf5ff",
230
+ "purple-10": "#581c87",
231
+ "purple-11": "#3b0764",
232
+ "purple-12": "#23043c",
233
+ "purple-2": "#f3e8ff",
234
+ "purple-3": "#e9d5ff",
235
+ "purple-4": "#d8b4fe",
236
+ "purple-5": "#c084fc",
237
+ "purple-6": "#a855f7",
238
+ "purple-7": "#9333ea",
239
+ "purple-8": "#7e22ce",
240
+ "purple-9": "#6b21a8",
241
+ "red-1": "#fef2f2",
242
+ "red-10": "#7f1d1d",
243
+ "red-11": "#450a0a",
244
+ "red-12": "#280606",
245
+ "red-2": "#fee2e2",
246
+ "red-3": "#fecaca",
247
+ "red-4": "#fca5a5",
248
+ "red-5": "#f87171",
249
+ "red-6": "#ef4444",
250
+ "red-7": "#dc2626",
251
+ "red-8": "#b91c1c",
252
+ "red-9": "#991b1b",
253
+ "rose-1": "#fff1f2",
254
+ "rose-10": "#881337",
255
+ "rose-11": "#4c0519",
256
+ "rose-12": "#2d030e",
257
+ "rose-2": "#ffe4e6",
258
+ "rose-3": "#fecdd3",
259
+ "rose-4": "#fda4af",
260
+ "rose-5": "#fb7185",
261
+ "rose-6": "#f43f5e",
262
+ "rose-7": "#e11d48",
263
+ "rose-8": "#be123c",
264
+ "rose-9": "#9f1239",
265
+ "silver": "#c0c0c0",
266
+ "sky-1": "#f0f9ff",
267
+ "sky-10": "#0c4a6e",
268
+ "sky-11": "#082f49",
269
+ "sky-12": "#041b2b",
270
+ "sky-2": "#e0f2fe",
271
+ "sky-3": "#bae6fd",
272
+ "sky-4": "#7dd3fc",
273
+ "sky-5": "#38bdf8",
274
+ "sky-6": "#0ea5e9",
275
+ "sky-7": "#0284c7",
276
+ "sky-8": "#0369a1",
277
+ "sky-9": "#075985",
278
+ "slate-1": "#f8fafc",
279
+ "slate-10": "#0f172a",
280
+ "slate-11": "#020617",
281
+ "slate-12": "#01020a",
282
+ "slate-2": "#f1f5f9",
283
+ "slate-3": "#e2e8f0",
284
+ "slate-4": "#cbd5e1",
285
+ "slate-5": "#94a3b8",
286
+ "slate-6": "#64748b",
287
+ "slate-7": "#475569",
288
+ "slate-8": "#334155",
289
+ "slate-9": "#1e293b",
290
+ "stone-1": "#fafaf9",
291
+ "stone-10": "#1c1917",
292
+ "stone-11": "#0c0a09",
293
+ "stone-12": "#070605",
294
+ "stone-2": "#f5f5f4",
295
+ "stone-3": "#e7e5e4",
296
+ "stone-4": "#d6d3d1",
297
+ "stone-5": "#a8a29e",
298
+ "stone-6": "#78716c",
299
+ "stone-7": "#57534e",
300
+ "stone-8": "#44403c",
301
+ "stone-9": "#292524",
302
+ "teal-1": "#f0fdfa",
303
+ "teal-10": "#134e4a",
304
+ "teal-11": "#042f2e",
305
+ "teal-12": "#021c1b",
306
+ "teal-2": "#ccfbf1",
307
+ "teal-3": "#99f6e4",
308
+ "teal-4": "#5eead4",
309
+ "teal-5": "#2dd4bf",
310
+ "teal-6": "#14b8a6",
311
+ "teal-7": "#0d9488",
312
+ "teal-8": "#0f766e",
313
+ "teal-9": "#115e59",
314
+ "transparent": "transparent",
315
+ "violet-1": "#f5f3ff",
316
+ "violet-10": "#4c1d95",
317
+ "violet-11": "#2e1065",
318
+ "violet-12": "#1c0a3d",
319
+ "violet-2": "#ede9fe",
320
+ "violet-3": "#ddd6fe",
321
+ "violet-4": "#c4b5fd",
322
+ "violet-5": "#a78bfa",
323
+ "violet-6": "#8b5cf6",
324
+ "violet-7": "#7c3aed",
325
+ "violet-8": "#6d28d9",
326
+ "violet-9": "#5b21b6",
327
+ "white": "#ffffff",
328
+ "yellow-1": "#fefce8",
329
+ "yellow-10": "#713f12",
330
+ "yellow-11": "#422006",
331
+ "yellow-12": "#271304",
332
+ "yellow-2": "#fef9c3",
333
+ "yellow-3": "#fef08a",
334
+ "yellow-4": "#fde047",
335
+ "yellow-5": "#facc15",
336
+ "yellow-6": "#eab308",
337
+ "yellow-7": "#ca8a04",
338
+ "yellow-8": "#a16207",
339
+ "yellow-9": "#854d0e",
340
+ "zinc-1": "#fafafa",
341
+ "zinc-10": "#18181b",
342
+ "zinc-11": "#09090b",
343
+ "zinc-12": "#050506",
344
+ "zinc-2": "#f4f4f5",
345
+ "zinc-3": "#e4e4e7",
346
+ "zinc-4": "#d4d4d8",
347
+ "zinc-5": "#a1a1aa",
348
+ "zinc-6": "#71717a",
349
+ "zinc-7": "#52525b",
350
+ "zinc-8": "#3f3f46",
351
+ "zinc-9": "#27272a"
352
+ };
353
+
354
+ // src/theme.ts
355
+ var defaultTheme = {
356
+ colors: {
357
+ ...PALETTE,
358
+ surface: { light: "#f9fafb", dark: "#111827" }
359
+ },
360
+ spacing: {
361
+ // "0" is always a hardcoded zero in real Tailwind's own scale too,
362
+ // regardless of the base spacing unit — needed for `p-0`/`m-0`/
363
+ // `top-0`/`inset-0`/etc., and specifically for `sticky top-0` (a
364
+ // sticky element needs an explicit offset to have any stick point at
365
+ // all; without one it behaves just like `relative`, no different from
366
+ // omitting `top-0` outright). Confirmed missing by hand: this
367
+ // sandbox's own header used `sticky top-0`, which silently never
368
+ // resolved (no "0" spacing key existed) until this was added.
369
+ "0": 0,
370
+ "1": 4,
371
+ "2": 8,
372
+ "4": 16,
373
+ "6": 24,
374
+ "8": 32
375
+ },
376
+ screens: {
377
+ sm: 640,
378
+ md: 768,
379
+ lg: 1024,
380
+ xl: 1280,
381
+ "2xl": 1536
382
+ },
383
+ darkMode: "attribute"
384
+ };
385
+ var activeTheme = defaultTheme;
386
+ var activeThemeJson = JSON.stringify(defaultTheme);
387
+ function setTheme(theme) {
388
+ activeTheme = theme;
389
+ activeThemeJson = JSON.stringify(theme);
156
390
  }
157
- function useSpacing() {
158
- const { config } = useTheme();
159
- return (0, import_react3.useMemo)(() => wrapSpacing(config.theme.spacing), [config.theme.spacing]);
391
+ function getTheme() {
392
+ return activeTheme;
393
+ }
394
+ function getThemeJson() {
395
+ return activeThemeJson;
160
396
  }
161
397
 
162
- // src/ThemeProvider.tsx
163
- var import_react5 = require("react");
164
-
165
- // src/useSyncExternalStoreShim.ts
166
- var import_react4 = __toESM(require("react"));
167
- var useSyncExternalStore = import_react4.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, getServerSnapshot) {
168
- const isServer = typeof window === "undefined";
169
- const [, forceUpdate] = import_react4.default.useReducer((n) => n + 1, 0);
170
- const value = isServer && getServerSnapshot ? getServerSnapshot() : getSnapshot();
171
- import_react4.default.useEffect(() => {
172
- if (getSnapshot() !== value) forceUpdate();
173
- return subscribe(forceUpdate);
174
- }, [subscribe]);
175
- return value;
176
- };
398
+ // src/reset.ts
399
+ var RESET_STYLE_ID = "kbach-reset";
400
+ var RESET_RULES = [
401
+ // border-style: solid means border-N utilities show a visible border without an extra border-solid class.
402
+ // border-width: 0 keeps all elements borderless by default.
403
+ { css: "*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }" },
404
+ { css: "body { margin: 0; padding: 0; }" },
405
+ { css: "h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }", tags: ["h1", "h2", "h3", "h4", "h5", "h6"] },
406
+ { css: "p { margin: 0; }", tags: ["p"] },
407
+ { css: "a { color: inherit; text-decoration: none; }", tags: ["a"] },
408
+ { css: "ul, ol { margin: 0; padding: 0; list-style: none; }", tags: ["ul", "ol"] },
409
+ // svg here only covers a literal <svg> tag in JSX — an icon imported as
410
+ // a component (e.g. `<ArrowIcon />`) never appears as the text "svg" in
411
+ // source, so this rule won't be pulled in by that usage alone. Same
412
+ // caveat applies to every tag-gated rule below: detection is a literal
413
+ // source-text scan (see vite-plugin/scan.ts's scanUsedTags), not a real
414
+ // DOM/type check.
415
+ { css: "img, video, svg { display: block; max-width: 100%; }", tags: ["img", "video", "svg"] },
416
+ // appearance: none is deliberately NOT applied to checkbox/radio/select below —
417
+ // stripping it hides their native checkmark/arrow with nothing rendered in its
418
+ // place, leaving an invisible checkbox or an arrow-less <select> that looks like
419
+ // plain text. Text-like inputs, textarea, and button don't have that problem
420
+ // (their native chrome is just a skin around content utilities can fully
421
+ // restyle), so they keep the blank-canvas treatment.
422
+ // :where() wraps the :not() exclusions so they contribute ZERO specificity
423
+ // (unlike a bare `input:not([type='checkbox']):not([type='radio'])`, whose
424
+ // two :not([attr]) clauses each add a class-level specificity point — (0,2,1)
425
+ // total, MORE than any single utility class (0,1,0)). Without :where(), this
426
+ // reset's `color: inherit` always won the cascade over a text-* color
427
+ // utility applied directly to a <input>/<textarea> regardless of source
428
+ // order, since author rules only override on a tie or higher specificity —
429
+ // confirmed on a real app: a TextInput's own text color utility resolved
430
+ // and injected correctly, class and all, but silently never painted.
431
+ {
432
+ css: "input:where(:not([type='checkbox']):not([type='radio'])), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
433
+ tags: ["input", "textarea"]
434
+ },
435
+ // Native checkbox/radio/range still get typography + spacing normalized, and
436
+ // accent-color re-themes their native indicator to the current text color
437
+ // instead of the browser/OS default blue, so they stay on-brand without
438
+ // needing to be rebuilt from scratch. Wrapped in :where() for the exact
439
+ // same reason as the input:not(...) exclusions above — a bare
440
+ // `input[type='range']` attribute selector has specificity (0,1,1),
441
+ // MORE than any single utility class (0,1,0), so an `accent-*` utility
442
+ // (Phase 23) applied directly to a checkbox/radio/range would resolve and
443
+ // inject correctly but silently never paint, losing the cascade to this
444
+ // reset regardless of source order — confirmed by hand via
445
+ // getComputedStyle on a real `accent-red-6` range input, which reported
446
+ // `currentColor`'s inherited value instead. `:where()` zeroes this
447
+ // selector's specificity contribution entirely, so any `accent-*`
448
+ // utility trivially wins once applied.
449
+ {
450
+ css: ":where(input[type='checkbox'], input[type='radio'], input[type='range']) { margin: 0; font: inherit; accent-color: currentColor; }",
451
+ tags: ["input"]
452
+ },
453
+ // select keeps its native chrome (border/background/arrow are all part of the
454
+ // same OS-drawn widget that appearance: none would blank out) — only typography
455
+ // and spacing are normalized so it still matches surrounding text.
456
+ { css: "select { margin: 0; font: inherit; color: inherit; line-height: inherit; }", tags: ["select"] },
457
+ { css: "button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }", tags: ["button"] },
458
+ // Also covers any element opted into the button role (`role="button"` on
459
+ // a <div>/<span>) — but only when a literal <button> tag is ALSO
460
+ // somewhere in the scanned source, since detection here is purely
461
+ // tag-name-based; a project using role="button" exclusively, with no
462
+ // real <button> anywhere, won't pull this rule in. Acceptable tradeoff
463
+ // for this project's scope (rare pattern) — cheap enough that scanning
464
+ // for it separately isn't worth the added complexity.
465
+ { css: "button, [role='button'] { cursor: pointer; }", tags: ["button"] },
466
+ { css: ":disabled { cursor: default; }", tags: ["button", "input", "select", "textarea"] },
467
+ { css: "textarea { resize: vertical; }", tags: ["textarea"] },
468
+ // Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
469
+ // normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
470
+ { css: "::placeholder { opacity: 1; }", tags: ["input", "textarea"] },
471
+ { css: "input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }", tags: ["input"] },
472
+ { css: "input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }", tags: ["input"] },
473
+ { css: "fieldset { padding: 0; margin: 0; }", tags: ["fieldset"] },
474
+ { css: "table { border-collapse: collapse; border-spacing: 0; }", tags: ["table"] }
475
+ ];
476
+ var BASE_RESET = RESET_RULES.map((r) => r.css).join("\n");
177
477
 
178
- // src/ThemeProvider.tsx
179
- var import_core2 = require("./core");
180
- var import_jsx_runtime = require("react/jsx-runtime");
181
- var useIsomorphicLayoutEffect = import_core2.isWeb || import_core2.isNative ? import_react5.useLayoutEffect : import_react5.useEffect;
182
- var STORAGE_KEY = "kbach-theme";
183
- var _mountedProviderCount = 0;
184
- var _warnedMultipleProviders = false;
185
- var _mountedConfigOverrideCount = 0;
186
- var _warnedConfigOverride = false;
187
- var _warnedMediaModeToggle = false;
188
- function loadPersistedMode() {
189
- try {
190
- if (import_core2.isWeb) return localStorage.getItem(STORAGE_KEY);
191
- return null;
192
- } catch {
193
- return null;
478
+ // src/kb.ts
479
+ var runtimeCSSDisabled = false;
480
+ function disableRuntimeCSS() {
481
+ runtimeCSSDisabled = true;
482
+ }
483
+ function isRuntimeCSSDisabled() {
484
+ return runtimeCSSDisabled;
485
+ }
486
+ var styleEl = null;
487
+ var sheetKeys = [];
488
+ var ruleOrderByKey = /* @__PURE__ */ new Map();
489
+ function getStyleEl() {
490
+ if (styleEl) return styleEl;
491
+ styleEl = document.createElement("style");
492
+ styleEl.setAttribute("data-kbach", "");
493
+ document.head.appendChild(styleEl);
494
+ return styleEl;
495
+ }
496
+ function findInsertionIndex(order) {
497
+ let lo = 0;
498
+ let hi = sheetKeys.length;
499
+ while (lo < hi) {
500
+ const mid = lo + hi >>> 1;
501
+ if (ruleOrderByKey.get(sheetKeys[mid]) > order) hi = mid;
502
+ else lo = mid + 1;
194
503
  }
504
+ return lo;
195
505
  }
196
- function persistMode(mode) {
506
+ function injectRule(rule, order) {
507
+ if (ruleOrderByKey.has(rule)) return;
508
+ const sheet = getStyleEl().sheet;
509
+ if (!sheet) return;
510
+ const idx = findInsertionIndex(order);
197
511
  try {
198
- if (import_core2.isWeb) localStorage.setItem(STORAGE_KEY, mode);
512
+ sheet.insertRule(rule, idx);
199
513
  } catch {
514
+ return;
200
515
  }
201
- }
202
- function subscribeSystemScheme(callback) {
203
- if (!import_core2.isWeb || typeof window === "undefined") return () => {
204
- };
205
- const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
206
- if (!mq) return () => {
207
- };
208
- mq.addEventListener("change", callback);
209
- return () => mq.removeEventListener("change", callback);
210
- }
211
- function getSystemScheme() {
212
- if (!import_core2.isWeb || typeof window === "undefined") return "light";
213
- return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
214
- }
215
- function getSystemSchemeServerSnapshot() {
216
- return "light";
217
- }
218
- function applyWebTheme(resolvedMode, strategy) {
219
- if (!import_core2.isWeb) return;
220
- const root = document.documentElement;
221
- if (strategy === "attribute") {
222
- root.setAttribute("data-theme", resolvedMode);
223
- } else if (strategy === "class") {
224
- root.classList.toggle("dark", resolvedMode === "dark");
225
- root.classList.toggle("light", resolvedMode === "light");
226
- }
227
- }
228
- function ThemeProvider({
229
- children,
230
- defaultMode = "system",
231
- colorScheme,
232
- windowWidth: windowWidthProp,
233
- config: configOverride,
234
- disablePersistence = false
235
- }) {
236
- const [resolvedConfig, setResolvedConfig] = (0, import_react5.useState)(
237
- () => configOverride ? (0, import_core2.buildConfig)(configOverride) : (0, import_core2.getConfig)()
238
- );
239
- const _prevConfigOverrideRef = (0, import_react5.useRef)(void 0);
240
- if (configOverride && _prevConfigOverrideRef.current !== configOverride) {
241
- _prevConfigOverrideRef.current = configOverride;
242
- (0, import_core2.updateConfig)(configOverride);
243
- const fresh = (0, import_core2.getConfig)();
244
- if (fresh !== resolvedConfig) setResolvedConfig(fresh);
245
- }
246
- (0, import_react5.useEffect)(() => {
247
- _mountedProviderCount++;
248
- if (configOverride) _mountedConfigOverrideCount++;
249
- if (process.env.NODE_ENV !== "production") {
250
- if (_mountedProviderCount > 1 && !_warnedMultipleProviders) {
251
- _warnedMultipleProviders = true;
252
- (0, import_core2.kbachWarn)(
253
- "Multiple <ThemeProvider> instances are mounted at once. Dark mode and responsive width are shared through one global store, so whichever provider rendered most recently wins for every consumer \u2014 nested or per-section theming is not isolated between providers."
254
- );
255
- }
256
- if (_mountedConfigOverrideCount > 1 && !_warnedConfigOverride) {
257
- _warnedConfigOverride = true;
258
- (0, import_core2.kbachWarn)(
259
- "Multiple <ThemeProvider config={...}> overrides are mounted at once. The resolved config is shared through the same global store as dark mode/width (see the multiple-providers warning), so per-tree config overrides are not actually isolated between providers \u2014 plain className/kb elements with no hover:/dark:/sm: modifier resolve against whichever override last committed, not necessarily their nearest ancestor <ThemeProvider>."
260
- );
261
- }
516
+ sheetKeys.splice(idx, 0, rule);
517
+ ruleOrderByKey.set(rule, order);
518
+ }
519
+ var resetChecked = false;
520
+ function ensureResetInjected() {
521
+ if (resetChecked) return;
522
+ resetChecked = true;
523
+ if (document.getElementById(RESET_STYLE_ID)) return;
524
+ const el = document.createElement("style");
525
+ el.id = RESET_STYLE_ID;
526
+ el.textContent = BASE_RESET;
527
+ document.head.insertBefore(el, document.head.firstChild);
528
+ }
529
+ function kb(classString) {
530
+ const json = generateCss(classString, getThemeJson());
531
+ const { className, rules } = JSON.parse(json);
532
+ if (!runtimeCSSDisabled) {
533
+ ensureResetInjected();
534
+ for (const { rule, order } of rules) {
535
+ injectRule(rule, order);
262
536
  }
263
- return () => {
264
- _mountedProviderCount--;
265
- if (configOverride) _mountedConfigOverrideCount--;
266
- if (_prevConfigOverrideRef.current !== void 0 && _mountedProviderCount === 0) {
267
- (0, import_core2.resetConfig)();
268
- }
269
- };
270
- }, []);
271
- const [webWidth, setWebWidth] = (0, import_react5.useState)(0);
272
- const effectiveWidth = (0, import_core2.getEffectiveIsWeb)() ? webWidth : windowWidthProp ?? 0;
273
- const numericScreens = (0, import_react5.useMemo)(() => {
274
- const out = {};
275
- for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
276
- out[k] = typeof v === "number" ? v : parseInt(String(v), 10);
277
- }
278
- return out;
279
- }, [resolvedConfig.theme.screens]);
280
- const _prevWidthRef = (0, import_react5.useRef)(-1);
281
- if (_prevWidthRef.current !== effectiveWidth) {
282
- _prevWidthRef.current = effectiveWidth;
283
- (0, import_core2.syncGlobalWidth)(effectiveWidth);
284
- }
285
- const _prevScreensRef = (0, import_react5.useRef)(null);
286
- if (_prevScreensRef.current !== numericScreens) {
287
- _prevScreensRef.current = numericScreens;
288
- (0, import_core2.syncGlobalScreens)(numericScreens);
289
537
  }
290
- (0, import_react5.useEffect)(() => {
291
- (0, import_core2.setGlobalWidth)(effectiveWidth);
292
- }, [effectiveWidth]);
293
- (0, import_react5.useEffect)(() => {
294
- if (!import_core2.isWeb || typeof window === "undefined") return;
295
- setWebWidth(window.innerWidth);
296
- let raf = 0;
297
- const update = () => {
298
- cancelAnimationFrame(raf);
299
- raf = requestAnimationFrame(() => {
300
- const w = window.innerWidth;
301
- setWebWidth(w);
302
- (0, import_core2.setGlobalWidth)(w);
303
- });
304
- };
305
- window.addEventListener("resize", update);
306
- return () => {
307
- window.removeEventListener("resize", update);
308
- cancelAnimationFrame(raf);
309
- };
310
- }, []);
311
- const webScheme = useSyncExternalStore(
312
- subscribeSystemScheme,
313
- getSystemScheme,
314
- getSystemSchemeServerSnapshot
315
- );
316
- const systemScheme = (0, import_core2.getEffectiveIsWeb)() ? webScheme : colorScheme === "dark" ? "dark" : "light";
317
- const [mode, _setMode] = (0, import_react5.useState)(defaultMode);
318
- (0, import_react5.useEffect)(() => {
319
- if (disablePersistence) return;
320
- const persisted = loadPersistedMode();
321
- if (persisted) _setMode(persisted);
322
- }, []);
323
- (0, import_react5.useEffect)(() => {
324
- if (disablePersistence || !import_core2.isWeb || typeof window === "undefined") return;
325
- const onStorage = (e) => {
326
- if (e.key !== STORAGE_KEY || e.newValue == null) return;
327
- if (e.newValue === "light" || e.newValue === "dark" || e.newValue === "system") {
328
- _setMode(e.newValue);
329
- }
330
- };
331
- window.addEventListener("storage", onStorage);
332
- return () => window.removeEventListener("storage", onStorage);
333
- }, [disablePersistence]);
334
- const setMode = (0, import_react5.useCallback)((next) => {
335
- if (process.env.NODE_ENV !== "production" && import_core2.isWeb && resolvedConfig.darkMode === "media" && !_warnedMediaModeToggle) {
336
- _warnedMediaModeToggle = true;
337
- (0, import_core2.kbachWarn)(
338
- 'setMode()/toggle() was called while darkMode is "media" \u2014 the resolved mode/isDark from useTheme()/useIsDark() will update, but the actual CSS stays driven by prefers-color-scheme and will not change to match. Use darkMode: "attribute" or "class" if the app needs manual toggling.'
339
- );
340
- }
341
- _setMode(next);
342
- if (!disablePersistence) persistMode(next);
343
- }, [disablePersistence, resolvedConfig.darkMode]);
344
- const toggle = (0, import_react5.useCallback)(() => {
345
- setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
346
- }, [mode, systemScheme, setMode]);
347
- const resolvedMode = mode === "system" ? systemScheme : mode;
348
- const isDark = resolvedMode === "dark";
349
- (0, import_core2.syncGlobalDarkMode)(isDark);
350
- useIsomorphicLayoutEffect(() => {
351
- applyWebTheme(resolvedMode, resolvedConfig.darkMode);
352
- (0, import_core2.setGlobalDarkMode)(isDark);
353
- }, [isDark, resolvedMode, resolvedConfig.darkMode]);
354
- (0, import_react5.useEffect)(() => {
355
- if (configOverride) return;
356
- let mounted = true;
357
- const unsub = (0, import_core2.onConfigChange)((config) => {
358
- if (mounted) setResolvedConfig(config);
359
- });
360
- return () => {
361
- mounted = false;
362
- unsub();
363
- };
364
- }, [configOverride]);
365
- const contextValue = (0, import_react5.useMemo)(
366
- () => ({ mode, resolvedMode, isDark, setMode, toggle, config: resolvedConfig }),
367
- [mode, resolvedMode, isDark, setMode, toggle, resolvedConfig]
368
- );
369
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: contextValue, children });
538
+ return className;
370
539
  }
371
540
 
372
541
  // src/KbachReset.tsx
373
- var import_core3 = require("./core");
374
- var import_jsx_runtime2 = require("react/jsx-runtime");
542
+ var import_jsx_runtime = require("react/jsx-runtime");
375
543
  function KbachReset() {
376
- if (import_core3.isNative) return null;
377
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { id: import_core3.RESET_STYLE_ID, children: import_core3.BASE_RESET });
544
+ if (isRuntimeCSSDisabled()) return null;
545
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { id: RESET_STYLE_ID, children: BASE_RESET });
378
546
  }
379
547
 
380
- // src/styled.tsx
381
- var import_react6 = __toESM(require("react"));
382
- var import_core7 = require("./core");
383
-
384
- // src/useGlobalDarkMode.ts
385
- var import_core4 = require("./core");
386
- var NOOP_SUB = (_) => () => {
387
- };
388
- var FALSE_SNAP = () => false;
389
- function useGlobalDarkMode() {
390
- return useSyncExternalStore(
391
- import_core4.subscribeGlobalDarkMode,
392
- import_core4.getGlobalDarkMode,
393
- () => false
394
- // SSR: default light
395
- );
548
+ // src/darkModeStore.ts
549
+ var STORAGE_KEY = "kbach-theme";
550
+ var listeners = /* @__PURE__ */ new Set();
551
+ function systemPrefersDark() {
552
+ if (typeof window === "undefined" || !window.matchMedia) return false;
553
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
396
554
  }
397
- function useConditionalGlobalDarkMode(active) {
398
- return useSyncExternalStore(
399
- active ? import_core4.subscribeGlobalDarkMode : NOOP_SUB,
400
- active ? import_core4.getGlobalDarkMode : FALSE_SNAP,
401
- FALSE_SNAP
402
- );
555
+ function storage() {
556
+ return typeof window === "undefined" ? null : window.localStorage;
403
557
  }
404
-
405
- // src/useGlobalWidth.ts
406
- var import_core5 = require("./core");
407
- var NOOP_SUB2 = (_) => () => {
408
- };
409
- var ZERO_SNAP = () => 0;
410
- var EMPTY_BREAKPOINTS = /* @__PURE__ */ new Set();
411
- var _webListeners = null;
412
- var _webRaf = 0;
413
- function _webResizeHandler() {
414
- cancelAnimationFrame(_webRaf);
415
- _webRaf = requestAnimationFrame(() => {
416
- if (_webListeners) for (const cb of _webListeners) cb();
417
- });
418
- }
419
- function subscribeWebWidth(cb) {
420
- if (typeof window === "undefined") return () => {
421
- };
422
- if (!_webListeners) {
423
- _webListeners = /* @__PURE__ */ new Set();
424
- window.addEventListener("resize", _webResizeHandler);
558
+ function readPersistedMode() {
559
+ const ls = storage();
560
+ if (!ls) return null;
561
+ try {
562
+ const raw = ls.getItem(STORAGE_KEY);
563
+ return raw === "light" || raw === "dark" || raw === "system" ? raw : null;
564
+ } catch {
565
+ return null;
425
566
  }
426
- _webListeners.add(cb);
427
- return () => {
428
- _webListeners.delete(cb);
429
- if (_webListeners.size === 0) {
430
- window.removeEventListener("resize", _webResizeHandler);
431
- cancelAnimationFrame(_webRaf);
432
- _webListeners = null;
433
- }
434
- };
435
- }
436
- function getWebWidth() {
437
- return typeof window !== "undefined" ? window.innerWidth : 0;
438
- }
439
- function useGlobalWidth() {
440
- return useSyncExternalStore(
441
- import_core5.isWeb ? subscribeWebWidth : import_core5.subscribeGlobalWidth,
442
- import_core5.isWeb ? getWebWidth : import_core5.getGlobalWidth,
443
- ZERO_SNAP
444
- );
445
567
  }
446
- function useConditionalWidth(active) {
447
- return useSyncExternalStore(
448
- active ? import_core5.isWeb ? subscribeWebWidth : import_core5.subscribeGlobalWidth : NOOP_SUB2,
449
- active ? import_core5.isWeb ? getWebWidth : import_core5.getGlobalWidth : ZERO_SNAP,
450
- ZERO_SNAP
451
- );
568
+ function resolveIsDark(m) {
569
+ return m === "system" ? systemPrefersDark() : m === "dark";
452
570
  }
453
-
454
- // src/shared-utils.ts
455
- var import_core6 = require("./core");
456
- function hasResponsiveBuckets(resolved) {
457
- const responsiveMods = (0, import_core6.getResponsiveModifiers)();
458
- for (const key of Object.keys(resolved)) {
459
- if (key === "base") continue;
460
- for (const mod of key.split(":")) {
461
- if (responsiveMods.has(mod)) return true;
462
- }
463
- }
464
- return false;
465
- }
466
- function hasInteractiveBuckets(resolved) {
467
- const interactiveMods = (0, import_core6.getInteractiveModifiers)();
468
- for (const key of Object.keys(resolved)) {
469
- if (key === "base") continue;
470
- for (const mod of key.split(":")) {
471
- if (interactiveMods.has(mod)) return true;
472
- }
571
+ function applyToDom(dark) {
572
+ if (typeof document === "undefined") return;
573
+ const root = document.documentElement;
574
+ const strategy = getTheme().darkMode;
575
+ if (strategy === "class") {
576
+ root.classList.toggle("dark", dark);
577
+ root.classList.toggle("light", !dark);
578
+ } else if (strategy === "attribute") {
579
+ root.setAttribute("data-theme", dark ? "dark" : "light");
473
580
  }
474
- return false;
475
581
  }
476
- function chain(original, extra) {
477
- return (...args) => {
478
- original?.(...args);
479
- extra();
582
+ var persistedMode = readPersistedMode();
583
+ var hadPersistedMode = persistedMode !== null;
584
+ var mode = persistedMode ?? "system";
585
+ var isDark = resolveIsDark(mode);
586
+ applyToDom(isDark);
587
+ if (typeof window !== "undefined" && window.matchMedia) {
588
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
589
+ const onSystemChange = (event) => {
590
+ if (mode !== "system") return;
591
+ isDark = event.matches;
592
+ applyToDom(isDark);
593
+ notify();
480
594
  };
481
- }
482
- function stripInternalMarkers(s) {
483
- delete s.__divideX;
484
- delete s.__divideY;
485
- delete s.__divideColor;
486
- delete s.__divideStyle;
487
- delete s.__keyframe;
488
- }
489
- function stripWebOnlyProps(s) {
490
- if (s.display === "grid" || s.display === "inline-grid") delete s.display;
491
- delete s.gridTemplateColumns;
492
- delete s.gridTemplateRows;
493
- delete s.gridColumn;
494
- delete s.gridRow;
495
- delete s.gridArea;
496
- delete s.gridColumnStart;
497
- delete s.gridColumnEnd;
498
- delete s.gridRowStart;
499
- delete s.gridRowEnd;
500
- delete s.gridAutoFlow;
501
- delete s.gridAutoColumns;
502
- delete s.gridAutoRows;
503
- delete s.placeItems;
504
- delete s.placeContent;
505
- delete s.justifyItems;
506
- delete s.placeSelf;
507
- delete s.justifySelf;
508
- if (s.position === "sticky" || s.position === "fixed" || s.position === "static") {
509
- delete s.position;
510
- }
511
- }
512
- function composeNativeStyle(computed, userStyle) {
513
- if (!userStyle) return computed;
514
- return Array.isArray(userStyle) ? [computed, ...userStyle] : [computed, userStyle];
515
- }
516
-
517
- // src/web-substitute.ts
518
- var _rnToHtml = {
519
- View: "div",
520
- SafeAreaView: "div",
521
- KeyboardAvoidingView: "div",
522
- ScrollView: "div",
523
- VirtualizedList: "div",
524
- FlatList: "div",
525
- SectionList: "div",
526
- Text: "span",
527
- TextInput: "input",
528
- Image: "img",
529
- ImageBackground: "div",
530
- Pressable: "div",
531
- TouchableOpacity: "div",
532
- TouchableHighlight: "div",
533
- TouchableWithoutFeedback: "div",
534
- TouchableNativeFeedback: "div"
535
- };
536
- var _userMap = /* @__PURE__ */ new Map();
537
- var _cache = /* @__PURE__ */ new WeakMap();
538
- function registerWebElement(rnComponent, htmlTag) {
539
- if (typeof rnComponent !== "object" && typeof rnComponent !== "function") return;
540
- _userMap.set(rnComponent, htmlTag);
541
- _cache.delete(rnComponent);
542
- }
543
- var _forwardRefOrMemoType = /* @__PURE__ */ new Set([
544
- /* @__PURE__ */ Symbol.for("react.forward_ref"),
545
- /* @__PURE__ */ Symbol.for("react.memo")
546
- ]);
547
- function looksLikeRealRNPrimitive(type) {
548
- const t = type.$$typeof;
549
- if (t !== void 0) return _forwardRefOrMemoType.has(t);
550
- return !!type.prototype?.isReactComponent;
551
- }
552
- function getWebTag(type, props) {
553
- if (typeof type === "string") return null;
554
- if (type === null || typeof type !== "function" && typeof type !== "object") return null;
555
- const obj = type;
556
- if (_userMap.has(obj)) return _userMap.get(obj);
557
- if (!looksLikeRealRNPrimitive(obj)) return null;
558
- const name = obj.displayName ?? obj.name;
559
- if (!name) {
560
- _cache.set(obj, null);
561
- return null;
562
- }
563
- if (name === "TextInput") {
564
- const multiline = !!props && (props.multiline === true || Number(props.numberOfLines) > 1);
565
- return multiline ? "textarea" : "input";
595
+ if (mq.addEventListener) mq.addEventListener("change", onSystemChange);
596
+ else mq.addListener(onSystemChange);
597
+ }
598
+ function notify() {
599
+ for (const listener of listeners) listener();
600
+ }
601
+ function getGlobalDarkMode() {
602
+ return isDark;
603
+ }
604
+ function getGlobalThemeMode() {
605
+ return mode;
606
+ }
607
+ function setGlobalThemeMode(nextMode) {
608
+ mode = nextMode;
609
+ isDark = resolveIsDark(nextMode);
610
+ applyToDom(isDark);
611
+ const ls = storage();
612
+ if (ls) {
613
+ try {
614
+ ls.setItem(STORAGE_KEY, nextMode);
615
+ } catch {
616
+ }
566
617
  }
567
- if (_cache.has(obj)) return _cache.get(obj);
568
- const tag = _rnToHtml[name] ?? null;
569
- _cache.set(obj, tag);
570
- return tag;
618
+ notify();
571
619
  }
572
- function getImpliedRNClasses(webTag, resolvedBase) {
573
- if (!webTag || !resolvedBase) return void 0;
574
- const classes = [];
575
- if (resolvedBase.position === void 0) classes.push("relative");
576
- const explicitDisplay = resolvedBase.display;
577
- const hasFlexItemProps = "flexGrow" in resolvedBase || "flexShrink" in resolvedBase || "flex" in resolvedBase || "gap" in resolvedBase || "columnGap" in resolvedBase || "rowGap" in resolvedBase;
578
- const willBeFlex = explicitDisplay === "flex" || explicitDisplay === void 0 && hasFlexItemProps;
579
- if (willBeFlex && (explicitDisplay === void 0 || resolvedBase.flexDirection === void 0)) {
580
- classes.push("flex-col");
581
- }
582
- return classes.length > 0 ? classes.join(" ") : void 0;
620
+ function toggleGlobalDarkMode() {
621
+ setGlobalThemeMode(isDark ? "light" : "dark");
583
622
  }
584
- var _rnOnlyProps = /* @__PURE__ */ new Set([
585
- // Interaction
586
- "onLongPress",
587
- "delayLongPress",
588
- "activeOpacity",
589
- "underlayColor",
590
- "hitSlop",
591
- "pressRetentionOffset",
592
- "android_ripple",
593
- "android_disableSound",
594
- "onHoverIn",
595
- "onHoverOut",
596
- "onHoverStart",
597
- "onHoverEnd",
598
- // Layout event
599
- "onLayout",
600
- // Accessibility
601
- "accessible",
602
- "accessibilityState",
603
- "accessibilityLiveRegion",
604
- "importantForAccessibility",
605
- // Platform
606
- "nativeID",
607
- "collapsable",
608
- "needsOffscreenAlphaCompositing",
609
- "renderToHardwareTextureAndroid",
610
- "shouldRasterizeIOS",
611
- "focusable",
612
- "hasTVPreferredFocus",
613
- // 'pointerEvents' is handled in transformToWebProps (mapped to CSS style)
614
- // Text
615
- "selectable",
616
- "allowFontScaling",
617
- "adjustsFontSizeToFit",
618
- "minimumFontScale",
619
- "ellipsizeMode",
620
- "numberOfLines",
621
- "onTextLayout",
622
- "textBreakStrategy",
623
- "lineBreakStrategyIOS",
624
- // TextInput
625
- "multiline",
626
- // NOTE: 'resizeMode' (Image's prop, not TextInput's) deliberately does NOT
627
- // go here — it's handled below in the isImage branch (mapped to CSS
628
- // object-fit). Blacklisting it here would make that branch unreachable.
629
- "blurOnSubmit",
630
- "clearButtonMode",
631
- "clearTextOnFocus",
632
- "enablesReturnKeyAutomatically",
633
- "returnKeyType",
634
- "spellCheck",
635
- // ScrollView
636
- "scrollEnabled",
637
- "showsVerticalScrollIndicator",
638
- "showsHorizontalScrollIndicator",
639
- "contentContainerStyle",
640
- // 'horizontal' is handled in transformToWebProps (converted to CSS overflow-x).
641
- "keyboardShouldPersistTaps",
642
- "keyboardDismissMode",
643
- "pagingEnabled",
644
- "scrollEventThrottle",
645
- "decelerationRate",
646
- "bounces",
647
- "alwaysBounceHorizontal",
648
- "alwaysBounceVertical",
649
- "snapToAlignment",
650
- "snapToInterval",
651
- "snapToOffsets",
652
- "removeClippedSubviews",
653
- "overScrollMode",
654
- "stickyHeaderIndices",
655
- "invertStickyHeaders",
656
- "onScrollBeginDrag",
657
- "onScrollEndDrag",
658
- "onMomentumScrollBegin",
659
- "onMomentumScrollEnd",
660
- "contentInset",
661
- "contentInsetAdjustmentBehavior",
662
- "automaticallyAdjustContentInsets",
663
- "automaticallyAdjustsScrollIndicatorInsets",
664
- // expo-image
665
- "contentPosition",
666
- "cachePolicy",
667
- "recyclingKey",
668
- "blurRadius",
669
- "fadeDuration",
670
- "responsivePolicy",
671
- "tintColor",
672
- "allowDownscaling",
673
- "placeholderContentFit",
674
- // FlatList / SectionList
675
- "data",
676
- "renderItem",
677
- "keyExtractor",
678
- "getItemLayout",
679
- "initialScrollIndex",
680
- "initialNumToRender",
681
- "maxToRenderPerBatch",
682
- "windowSize",
683
- "updateCellsBatchingPeriod",
684
- "onEndReached",
685
- "onEndReachedThreshold",
686
- "ListHeaderComponent",
687
- "ListFooterComponent",
688
- "ListEmptyComponent",
689
- "ListHeaderComponentStyle",
690
- "ListFooterComponentStyle",
691
- "ItemSeparatorComponent",
692
- "SectionSeparatorComponent",
693
- "inverted",
694
- "getItem",
695
- "getItemCount"
696
- ]);
697
- var _pressableNames = /* @__PURE__ */ new Set([
698
- "Pressable",
699
- "TouchableOpacity",
700
- "TouchableHighlight",
701
- "TouchableWithoutFeedback",
702
- "TouchableNativeFeedback"
703
- ]);
704
- var _keyboardTypeMap = {
705
- "numeric": "number",
706
- "number-pad": "number",
707
- "decimal-pad": "decimal",
708
- "email-address": "email",
709
- "phone-pad": "tel",
710
- "url": "url"
711
- };
712
- var _resizeModeMap = {
713
- "contain": "contain",
714
- "cover": "cover",
715
- "stretch": "fill",
716
- "center": "none",
717
- "repeat": "none"
718
- };
719
- var PLACEHOLDER_RULE_ATTR = "data-kbach-ph";
720
- var _placeholderRuleInjected = false;
721
- function ensurePlaceholderColorRuleInjected() {
722
- if (_placeholderRuleInjected || typeof document === "undefined") return;
723
- _placeholderRuleInjected = true;
724
- const style = document.createElement("style");
725
- style.setAttribute("data-kbach-placeholder", "");
726
- style.textContent = `[${PLACEHOLDER_RULE_ATTR}]::placeholder{color:var(--kbach-ph-color)}`;
727
- document.head.appendChild(style);
623
+ function subscribeGlobalDarkMode(listener) {
624
+ listeners.add(listener);
625
+ return () => listeners.delete(listener);
728
626
  }
729
- function transformToWebProps(originalName, tag, props) {
730
- const out = {};
731
- const isPressable = _pressableNames.has(originalName);
732
- const isTextInput = originalName === "TextInput";
733
- const isImage = originalName === "Image" || originalName === "ImageBackground";
734
- const isScrollable = originalName === "ScrollView" || originalName === "FlatList" || originalName === "SectionList";
735
- let pendingStyle = null;
736
- for (const [k, v] of Object.entries(props)) {
737
- if (_rnOnlyProps.has(k)) continue;
738
- if (k === "onPress") {
739
- if (!("onClick" in props)) out.onClick = v;
740
- continue;
741
- }
742
- if (k === "accessibilityLabel") {
743
- if (out["aria-label"] == null) out["aria-label"] = v;
744
- continue;
745
- }
746
- if (k === "accessibilityRole") {
747
- if (out.role == null) out.role = v;
748
- continue;
749
- }
750
- if (k === "testID") {
751
- if (out["data-testid"] == null) out["data-testid"] = v;
752
- continue;
753
- }
754
- if (isTextInput) {
755
- if (k === "onChangeText") {
756
- if (!("onChange" in props)) out.onChange = (e) => v(e.target.value);
757
- continue;
758
- }
759
- if (k === "onSubmitEditing") {
760
- if (!("onKeyDown" in props)) {
761
- out.onKeyDown = (e) => {
762
- if (e.key === "Enter") v();
763
- };
764
- }
765
- continue;
766
- }
767
- if (k === "placeholderTextColor") {
768
- if (v) {
769
- ensurePlaceholderColorRuleInjected();
770
- out[PLACEHOLDER_RULE_ATTR] = "";
771
- pendingStyle = { ...pendingStyle ?? {}, "--kbach-ph-color": v };
772
- }
773
- continue;
774
- }
775
- if (k === "secureTextEntry") {
776
- if (v && !("type" in props)) out.type = "password";
777
- continue;
778
- }
779
- if (k === "keyboardType") {
780
- if (!("type" in props) && !props.secureTextEntry) {
781
- const mapped = _keyboardTypeMap[v];
782
- if (mapped) out.type = mapped;
783
- }
784
- continue;
785
- }
786
- if (k === "editable") {
787
- if (v === false) out.readOnly = true;
788
- continue;
789
- }
790
- if (k === "maxLength") {
791
- out.maxLength = v;
792
- continue;
793
- }
794
- }
795
- if (isScrollable && k === "horizontal") {
796
- if (v) pendingStyle = { ...pendingStyle ?? {}, display: "flex", flexDirection: "row", overflowX: "auto" };
797
- continue;
798
- }
799
- if (k === "pointerEvents") {
800
- if (v === "none" || v === "auto") pendingStyle = { ...pendingStyle ?? {}, pointerEvents: v };
801
- continue;
802
- }
803
- if (isImage) {
804
- if (k === "source") {
805
- if (typeof v === "string") {
806
- out.src = v;
807
- } else if (v && typeof v === "object" && "uri" in v) {
808
- out.src = v.uri;
809
- if (v.headers) out["crossOrigin"] = "anonymous";
810
- }
811
- continue;
812
- }
813
- if (k === "resizeMode") {
814
- pendingStyle = { ...pendingStyle ?? {}, objectFit: _resizeModeMap[v] ?? "cover" };
815
- continue;
816
- }
817
- if (k === "contentFit") {
818
- pendingStyle = { ...pendingStyle ?? {}, objectFit: v };
819
- continue;
820
- }
821
- if (k === "defaultSource") continue;
822
- }
823
- if (k === "style") {
824
- if (Array.isArray(v)) {
825
- out.style = Object.assign({}, ...v.filter(Boolean));
826
- } else if (v != null) {
827
- out.style = v;
828
- }
829
- continue;
830
- }
831
- out[k] = v;
832
- }
833
- if (pendingStyle) {
834
- out.style = out.style ? { ...pendingStyle, ...out.style } : pendingStyle;
835
- }
836
- if (isPressable && !out.role) out.role = "button";
837
- if (isImage && tag === "img" && out.alt == null) out.alt = "";
838
- return out;
627
+ function seedDefaultMode(fallback) {
628
+ if (hadPersistedMode || mode === fallback) return;
629
+ mode = fallback;
630
+ isDark = resolveIsDark(fallback);
631
+ applyToDom(isDark);
632
+ notify();
839
633
  }
840
634
 
841
- // src/styled.tsx
842
- function styled(Component, baseClasses = "") {
843
- const Styled = (0, import_react6.forwardRef)(
844
- (props, ref) => {
845
- const {
846
- kb: extraClasses,
847
- style: styleProp,
848
- onPressIn,
849
- onPressOut,
850
- onPointerDown,
851
- onPointerUp,
852
- onPointerLeave,
853
- onPointerCancel,
854
- onMouseEnter,
855
- onMouseLeave,
856
- onFocus,
857
- onBlur,
858
- ...rest
859
- } = props;
860
- const disabled = !!props.disabled;
861
- const checked = !!props.checked;
862
- const { config } = useTheme();
863
- const isWebPlatform = (0, import_core7.getEffectiveIsWeb)();
864
- const isDark = useConditionalGlobalDarkMode(!isWebPlatform);
865
- const [pressed, setPressed] = (0, import_react6.useState)(false);
866
- const [hovered, setHovered] = (0, import_react6.useState)(false);
867
- const [focused, setFocused] = (0, import_react6.useState)(false);
868
- const handlePressIn = (0, import_react6.useCallback)(chain(onPressIn, () => {
869
- if (!isWebPlatform) setPressed(true);
870
- }), [onPressIn, isWebPlatform]);
871
- const handlePressOut = (0, import_react6.useCallback)(chain(onPressOut, () => {
872
- if (!isWebPlatform) setPressed(false);
873
- }), [onPressOut, isWebPlatform]);
874
- const handlePointerDown = (0, import_react6.useCallback)(chain(onPointerDown, () => {
875
- if (!isWebPlatform) setPressed(true);
876
- }), [onPointerDown, isWebPlatform]);
877
- const handlePointerUp = (0, import_react6.useCallback)(chain(onPointerUp, () => {
878
- if (!isWebPlatform) setPressed(false);
879
- }), [onPointerUp, isWebPlatform]);
880
- const handlePointerLeave = (0, import_react6.useCallback)(chain(onPointerLeave, () => {
881
- if (!isWebPlatform) setPressed(false);
882
- }), [onPointerLeave, isWebPlatform]);
883
- const handlePointerCancel = (0, import_react6.useCallback)(chain(onPointerCancel, () => {
884
- if (!isWebPlatform) setPressed(false);
885
- }), [onPointerCancel, isWebPlatform]);
886
- const handleMouseEnter = (0, import_react6.useCallback)(chain(onMouseEnter, () => {
887
- if (!isWebPlatform) setHovered(true);
888
- }), [onMouseEnter, isWebPlatform]);
889
- const handleMouseLeave = (0, import_react6.useCallback)(chain(onMouseLeave, () => {
890
- if (!isWebPlatform) setHovered(false);
891
- }), [onMouseLeave, isWebPlatform]);
892
- const handleFocus = (0, import_react6.useCallback)(chain(onFocus, () => {
893
- if (!isWebPlatform) setFocused(true);
894
- }), [onFocus, isWebPlatform]);
895
- const handleBlur = (0, import_react6.useCallback)(chain(onBlur, () => {
896
- if (!isWebPlatform) setFocused(false);
897
- }), [onBlur, isWebPlatform]);
898
- const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
899
- const firstPassResolved = (0, import_react6.useMemo)(() => (0, import_core7.resolve)(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
900
- const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
901
- const impliedClasses = getImpliedRNClasses(webTag, firstPassResolved.base);
902
- const finalClassStr = impliedClasses ? `${combined} ${impliedClasses}` : combined;
903
- const resolved = (0, import_react6.useMemo)(() => (0, import_core7.resolve)(finalClassStr, config.theme, config.darkMode), [finalClassStr, config.theme, config.darkMode]);
904
- const hasInteractive = (0, import_react6.useMemo)(() => hasInteractiveBuckets(resolved), [resolved]);
905
- const needsWidth = hasResponsiveBuckets(resolved);
906
- const width = useConditionalWidth(needsWidth && !isWebPlatform);
907
- const breakpoints = needsWidth ? (0, import_core7.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
908
- const screens = (0, import_core7.getGlobalScreens)();
909
- const computedStyle = (0, import_react6.useMemo)(
910
- () => isWebPlatform ? {} : (0, import_core7.flatten)(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
911
- [resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
912
- // eslint-disable-line react-hooks/exhaustive-deps
913
- );
914
- const finalStyle = isWebPlatform ? (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0 : composeNativeStyle(computedStyle, styleProp);
915
- const effectiveComponent = webTag ?? Component;
916
- const componentName2 = Component.displayName ?? Component.name ?? "";
917
- const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
918
- return import_react6.default.createElement(effectiveComponent, {
919
- ref,
920
- ...effectiveRest,
921
- style: finalStyle,
922
- // className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
923
- ...isWebPlatform && finalClassStr ? { className: (0, import_core7.normalizeClassString)(finalClassStr) } : {},
924
- // Only attach state-tracking handlers when interactive modifiers are present.
925
- // Always forward user-provided handlers to avoid silently swallowing them.
926
- // Gated on isNative, not isWeb: isWeb is false during SSR too (no `window` there),
927
- // and onPressIn/onPressOut must stay excluded there as well — SSR is exactly where
928
- // getWebTag() above is skipped (it's isWeb-gated), so effectiveComponent may still be
929
- // a non-string RN-style reference server-side, and this used to forward onPressIn/
930
- // onPressOut to it unconditionally in that case (isWeb ? ... : ...'s false branch
931
- // covered both native AND SSR). onPressIn/onPressOut are RN-only prop names — a
932
- // non-string effectiveComponent on the web/SSR side is just as likely to be an
933
- // ordinary web component (React Router's <Link>, Next.js's <Link>, any custom
934
- // wrapper) as an actual react-native-web primitive, and "not a string" alone isn't a
935
- // reliable signal either way. Forwarding them made React DOM warn "Unknown event
936
- // handler property" the moment any such component got an interactive modifier
937
- // (hover:, active:, …), which is the common case, not the exception.
938
- ...!import_core7.isNative ? hasInteractive ? {
939
- onPointerDown: handlePointerDown,
940
- onPointerUp: handlePointerUp,
941
- onPointerLeave: handlePointerLeave,
942
- onPointerCancel: handlePointerCancel
943
- } : {
944
- onPointerDown,
945
- onPointerUp,
946
- onPointerLeave,
947
- onPointerCancel
948
- } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
949
- onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
950
- onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
951
- onFocus: hasInteractive ? handleFocus : onFocus,
952
- onBlur: hasInteractive ? handleBlur : onBlur
953
- });
954
- }
955
- );
956
- const componentName = Component.displayName ?? Component.name ?? "Component";
957
- Styled.displayName = `Styled(${componentName})`;
958
- return Styled;
635
+ // src/useGlobalDarkMode.ts
636
+ var import_react = require("react");
637
+ var getServerSnapshot = () => false;
638
+ function useGlobalDarkMode() {
639
+ return (0, import_react.useSyncExternalStore)(subscribeGlobalDarkMode, getGlobalDarkMode, getServerSnapshot);
959
640
  }
960
641
 
961
- // src/useStyles.ts
962
- var import_react7 = require("react");
963
- var import_core8 = require("./core");
964
- function useStyles(classString, state = {}) {
965
- const { isDark, config } = useTheme();
966
- const width = useGlobalWidth();
967
- const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
968
- return (0, import_react7.useMemo)(() => {
969
- const resolved = (0, import_core8.resolve)(normalised, config.theme, config.darkMode);
970
- const breakpoints = (0, import_core8.getActiveBreakpoints)(width);
971
- return (0, import_core8.flatten)(resolved, isDark, state, breakpoints);
972
- }, [normalised, isDark, config.theme, config.darkMode, width, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
973
- }
974
- function useResolvedStyle(classString) {
975
- const { config } = useTheme();
976
- const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
977
- return (0, import_react7.useMemo)(
978
- () => (0, import_core8.resolve)(normalised, config.theme, config.darkMode),
979
- [normalised, config]
980
- );
981
- }
642
+ // src/ThemeProvider.tsx
643
+ var import_react3 = require("react");
982
644
 
983
- // src/useBreakpoint.ts
984
- var import_react8 = require("react");
985
- var import_core9 = require("./core");
986
- function toNumericScreens(screens) {
987
- const out = {};
988
- for (const [name, v] of Object.entries(screens)) {
989
- const n = typeof v === "number" ? v : parseInt(String(v), 10);
990
- if (!Number.isNaN(n)) out[name] = n;
645
+ // src/ThemeContext.tsx
646
+ var import_react2 = require("react");
647
+ var ThemeContext = (0, import_react2.createContext)(null);
648
+ function useTheme() {
649
+ const ctx = (0, import_react2.useContext)(ThemeContext);
650
+ if (!ctx) {
651
+ throw new Error(
652
+ 'useTheme() must be called within a <ThemeProvider>. For dark-mode state without a provider, use useGlobalDarkMode() (read) or toggleGlobalDarkMode()/setGlobalThemeMode() (write) from "@kbach/react" instead.'
653
+ );
991
654
  }
992
- return out;
993
- }
994
- function useBreakpoint() {
995
- const { config } = useTheme();
996
- const width = useGlobalWidth();
997
- const screens = config.theme.screens;
998
- const numericScreens = (0, import_react8.useMemo)(() => toNumericScreens(screens), [screens]);
999
- return (0, import_react8.useMemo)(() => {
1000
- const active = (0, import_core9.getActiveBreakpoints)(width, numericScreens);
1001
- let best = null;
1002
- let bestMinW = -Infinity;
1003
- for (const name of active) {
1004
- const minW = numericScreens[name];
1005
- if (minW > bestMinW) {
1006
- bestMinW = minW;
1007
- best = name;
1008
- }
1009
- }
1010
- return best ?? "xs";
1011
- }, [numericScreens, width]);
1012
- }
1013
- function useResponsive() {
1014
- const { config } = useTheme();
1015
- const width = useGlobalWidth();
1016
- const screens = config.theme.screens;
1017
- const numericScreens = (0, import_react8.useMemo)(() => toNumericScreens(screens), [screens]);
1018
- return (0, import_react8.useMemo)(() => {
1019
- const active = (0, import_core9.getActiveBreakpoints)(width, numericScreens);
1020
- const result = {};
1021
- for (const name of Object.keys(numericScreens)) {
1022
- result[name] = active.has(name);
1023
- }
1024
- return result;
1025
- }, [numericScreens, width]);
655
+ return ctx;
1026
656
  }
1027
657
 
1028
- // src/InteractiveWrapper.tsx
1029
- var import_react9 = __toESM(require("react"));
1030
- var import_core10 = require("./core");
1031
- var InteractiveWrapper = (0, import_react9.forwardRef)(
1032
- function InteractiveWrapper2({
1033
- Component,
1034
- resolvedStyle,
1035
- className,
1036
- style: styleProp,
1037
- onPressIn,
1038
- onPressOut,
1039
- onPointerDown,
1040
- onPointerUp,
1041
- onPointerLeave,
1042
- onPointerCancel,
1043
- onMouseEnter,
1044
- onMouseLeave,
1045
- onFocus,
1046
- onBlur,
1047
- ...rest
1048
- }, ref) {
1049
- const isWebPlatform = (0, import_core10.getEffectiveIsWeb)();
1050
- const isDark = useConditionalGlobalDarkMode(!isWebPlatform);
1051
- const needsWidth = hasResponsiveBuckets(resolvedStyle);
1052
- const width = useConditionalWidth(needsWidth && !isWebPlatform);
1053
- const breakpoints = needsWidth ? (0, import_core10.getActiveBreakpoints)(width) : EMPTY_BREAKPOINTS;
1054
- const screens = (0, import_core10.getGlobalScreens)();
1055
- const [pressed, setPressed] = (0, import_react9.useState)(false);
1056
- const [hovered, setHovered] = (0, import_react9.useState)(false);
1057
- const [focused, setFocused] = (0, import_react9.useState)(false);
1058
- const handlePressIn = (0, import_react9.useCallback)(chain(onPressIn, () => {
1059
- if (!isWebPlatform) setPressed(true);
1060
- }), [onPressIn, isWebPlatform]);
1061
- const handlePressOut = (0, import_react9.useCallback)(chain(onPressOut, () => {
1062
- if (!isWebPlatform) setPressed(false);
1063
- }), [onPressOut, isWebPlatform]);
1064
- const handlePointerDown = (0, import_react9.useCallback)(chain(onPointerDown, () => {
1065
- if (!isWebPlatform) setPressed(true);
1066
- }), [onPointerDown, isWebPlatform]);
1067
- const handlePointerUp = (0, import_react9.useCallback)(chain(onPointerUp, () => {
1068
- if (!isWebPlatform) setPressed(false);
1069
- }), [onPointerUp, isWebPlatform]);
1070
- const handlePointerLeave = (0, import_react9.useCallback)(chain(onPointerLeave, () => {
1071
- if (!isWebPlatform) setPressed(false);
1072
- }), [onPointerLeave, isWebPlatform]);
1073
- const handlePointerCancel = (0, import_react9.useCallback)(chain(onPointerCancel, () => {
1074
- if (!isWebPlatform) setPressed(false);
1075
- }), [onPointerCancel, isWebPlatform]);
1076
- const handleMouseEnter = (0, import_react9.useCallback)(chain(onMouseEnter, () => {
1077
- if (!isWebPlatform) setHovered(true);
1078
- }), [onMouseEnter, isWebPlatform]);
1079
- const handleMouseLeave = (0, import_react9.useCallback)(chain(onMouseLeave, () => {
1080
- if (!isWebPlatform) setHovered(false);
1081
- }), [onMouseLeave, isWebPlatform]);
1082
- const handleFocus = (0, import_react9.useCallback)(chain(onFocus, () => {
1083
- if (!isWebPlatform) setFocused(true);
1084
- }), [onFocus, isWebPlatform]);
1085
- const handleBlur = (0, import_react9.useCallback)(chain(onBlur, () => {
1086
- if (!isWebPlatform) setFocused(false);
1087
- }), [onBlur, isWebPlatform]);
1088
- const { children, disabled, checked, ...restForComponent } = rest;
1089
- const isNonStringComponent = typeof Component !== "string";
1090
- const computedStyle = (0, import_react9.useMemo)(
1091
- () => {
1092
- if (isWebPlatform) return {};
1093
- const s = (0, import_core10.flatten)(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused, disabled: !!disabled, checked: !!checked }, breakpoints);
1094
- stripInternalMarkers(s);
1095
- if (isNonStringComponent) stripWebOnlyProps(s);
1096
- return s;
1097
- },
1098
- [resolvedStyle, isDark, pressed, hovered, focused, width, screens, disabled, checked, isNonStringComponent]
1099
- );
1100
- const skipComputedInline = isWebPlatform;
1101
- const finalStyle = skipComputedInline ? styleProp ?? void 0 : composeNativeStyle(computedStyle, styleProp);
1102
- const componentProps = {
1103
- ref,
1104
- ...restForComponent,
1105
- ...disabled !== void 0 ? { disabled } : {},
1106
- ...checked !== void 0 ? { checked } : {},
1107
- style: finalStyle,
1108
- ...!import_core10.isNative && className ? { className } : {},
1109
- // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch) —
1110
- // onPressIn/onPressOut are RN-only prop names and are never forwarded here, even when
1111
- // Component isn't a literal HTML tag string. A non-string Component on web is just as
1112
- // likely to be an ordinary web component (React Router's <Link>, Next.js's <Link>, any
1113
- // custom wrapper) as an actual react-native-web primitive — "not a string" alone was never
1114
- // a reliable signal that onPressIn/onPressOut are wanted, and forwarding them
1115
- // unconditionally made React DOM warn "Unknown event handler property" the moment any web
1116
- // component got an interactive modifier (hover:, active:, …), which is the common case,
1117
- // not the exception. (A real react-native-web primitive would additionally have this pair
1118
- // of props silently vanish after hydration anyway, once the browser-only web-substitution
1119
- // in jsx-runtime.tsx swaps it for a plain host tag — so keeping them pre-hydration would
1120
- // only have traded one prop-mismatch warning for another.) Genuinely native code paths
1121
- // still get them via the isNative branch below.
1122
- ...!import_core10.isNative ? {
1123
- onPointerDown: handlePointerDown,
1124
- onPointerUp: handlePointerUp,
1125
- onPointerLeave: handlePointerLeave,
1126
- onPointerCancel: handlePointerCancel
1127
- } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
1128
- onMouseEnter: handleMouseEnter,
1129
- onMouseLeave: handleMouseLeave,
1130
- onFocus: handleFocus,
1131
- onBlur: handleBlur
658
+ // src/ThemeProvider.tsx
659
+ var import_jsx_runtime2 = require("react/jsx-runtime");
660
+ var mountedProviderCount = 0;
661
+ var warnedMultipleProviders = false;
662
+ function ThemeProvider({ children, defaultMode = "system" }) {
663
+ (0, import_react3.useEffect)(() => {
664
+ mountedProviderCount++;
665
+ if (mountedProviderCount > 1 && !warnedMultipleProviders) {
666
+ warnedMultipleProviders = true;
667
+ console.warn(
668
+ "[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store (the same one useGlobalDarkMode()/toggleGlobalDarkMode() read and write), not scoped per-provider, so they will fight over the same state instead of each getting their own."
669
+ );
670
+ }
671
+ return () => {
672
+ mountedProviderCount--;
1132
673
  };
1133
- return Array.isArray(children) ? import_react9.default.createElement(Component, componentProps, ...children) : import_react9.default.createElement(Component, componentProps, children);
1134
- }
1135
- );
1136
- InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
1137
-
1138
- // src/kb.ts
1139
- var import_core11 = require("./core");
1140
- function kb(classString, isDark = false) {
1141
- const config = (0, import_core11.getConfig)();
1142
- const resolved = (0, import_core11.resolve)(classString, config.theme, config.darkMode);
1143
- if ((0, import_core11.getEffectiveIsWeb)()) {
1144
- return classString;
1145
- }
1146
- return (0, import_core11.flatten)(resolved, isDark);
1147
- }
1148
- function cx(...classes) {
1149
- return classes.filter(Boolean).join(" ");
674
+ }, []);
675
+ const seeded = (0, import_react3.useRef)(false);
676
+ (0, import_react3.useEffect)(() => {
677
+ if (seeded.current) return;
678
+ seeded.current = true;
679
+ seedDefaultMode(defaultMode);
680
+ }, []);
681
+ const isDark2 = (0, import_react3.useSyncExternalStore)(subscribeGlobalDarkMode, getGlobalDarkMode, () => false);
682
+ const mode2 = (0, import_react3.useSyncExternalStore)(subscribeGlobalDarkMode, getGlobalThemeMode, () => defaultMode);
683
+ const value = (0, import_react3.useMemo)(
684
+ () => ({ mode: mode2, isDark: isDark2, setMode: setGlobalThemeMode, toggle: toggleGlobalDarkMode }),
685
+ [mode2, isDark2]
686
+ );
687
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeContext.Provider, { value, children });
1150
688
  }
1151
-
1152
- // src/index.ts
1153
- var import_core12 = require("./core");
1154
689
  // Annotate the CommonJS export names for ESM import in node:
1155
690
  0 && (module.exports = {
1156
- InteractiveWrapper,
1157
691
  KbachReset,
1158
- ThemeContext,
1159
692
  ThemeProvider,
1160
- buildConfig,
1161
- clearCache,
1162
- cx,
1163
- defaultColors,
1164
693
  defaultTheme,
1165
694
  disableRuntimeCSS,
1166
- flatten,
1167
- generateKbachTypesDts,
1168
- getConfig,
1169
- initConfig,
695
+ getGlobalDarkMode,
696
+ getGlobalThemeMode,
697
+ getTheme,
698
+ initKbach,
699
+ isKbachReady,
700
+ isRuntimeCSSDisabled,
1170
701
  kb,
1171
- normalizeClassString,
1172
- parseClass,
1173
- parseClasses,
1174
- registerWebElement,
1175
- resolve,
1176
- setResolveTarget,
1177
- splitClassTokens,
1178
- styled,
1179
- updateConfig,
1180
- useBreakpoint,
1181
- useColors,
702
+ setGlobalThemeMode,
703
+ setTheme,
704
+ subscribeGlobalDarkMode,
705
+ toggleGlobalDarkMode,
1182
706
  useGlobalDarkMode,
1183
- useIsDark,
1184
- useResolvedStyle,
1185
- useResponsive,
1186
- useSpacing,
1187
- useStyles,
1188
- useTheme,
1189
- wrapColors,
1190
- wrapSpacing
707
+ useTheme
1191
708
  });