@kbach/react 1.0.0-beta.16 → 1.0.0-beta.18

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.d.mts CHANGED
@@ -145,4 +145,52 @@ type ColorsAPI = {
145
145
  */
146
146
  declare function useColors(): ColorsAPI;
147
147
 
148
- export { type ColorScale, type ColorsAPI, KbachReset, type ThemeMode, ThemeProvider, type ThemeProviderProps, type ThemeState, disableRuntimeCSS, getGlobalDarkMode, getGlobalThemeMode, initKbach, isKbachReady, isRuntimeCSSDisabled, kb, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode, useColors, useTheme };
148
+ /**
149
+ * A tiny class-name composer — combine strings, arrays, and objects (with
150
+ * boolean values) into one space-separated className, skipping anything
151
+ * falsy. Matches the widely-used `clsx` npm package's public API and
152
+ * behavior exactly (a from-scratch reimplementation, not a fork or a thin
153
+ * wrapper around it) — this package ships its own copy so using it adds
154
+ * zero runtime dependencies, and so it works identically whether or not
155
+ * `clsx`/`classnames` happens to already be installed.
156
+ *
157
+ * Kbach class strings never NEED this — a Kbach `className` is just a
158
+ * plain string, composable with plain JS (`"bg-" + color + "-6"`,
159
+ * `[base, active && "opacity-100"].join(" ")`, see this package's own
160
+ * README) with no special API required. This exists purely as an
161
+ * ergonomic convenience for the common "several conditional classes on one
162
+ * element" shape, for anyone who'd rather write it declaratively:
163
+ *
164
+ * ```ts
165
+ * clsx("px-4 py-2", isActive && "bg-blue-6", { "opacity-50": disabled });
166
+ * // "px-4 py-2 bg-blue-6" (when isActive is true and disabled is false)
167
+ * ```
168
+ *
169
+ * The static build-time scanner (vite-plugin/postcss-plugin) already
170
+ * recognizes `clsx(...)` call sites (alongside `cn`/`classnames`/`cx`/
171
+ * `kb`) and extracts every string literal inside them on its own — using
172
+ * this function needs no extra plugin configuration to keep working with
173
+ * the static CSS build.
174
+ *
175
+ * Identical to `@kbach/react-native`'s own `clsx.ts` — deliberately
176
+ * duplicated rather than factored into a shared package, since this is
177
+ * genuinely all there is to it and a whole extra published package for one
178
+ * ~20-line function would be more ceremony than the thing itself.
179
+ */
180
+ /** Anything `clsx` accepts as one argument — recursively, for arrays. */
181
+ type ClassValue = ClassArray | ClassDictionary | string | number | bigint | boolean | null | undefined;
182
+ /** A key is kept (as a literal class name) only when its value is truthy — `{ "opacity-50": disabled }`. */
183
+ interface ClassDictionary {
184
+ [className: string]: unknown;
185
+ }
186
+ type ClassArray = ClassValue[];
187
+ /**
188
+ * Joins every truthy piece across all arguments with a single space,
189
+ * skipping falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`)
190
+ * entirely — they contribute nothing, not even an extra space. Arrays are
191
+ * flattened (recursively, to any depth); a plain object keeps only the
192
+ * keys whose value is truthy.
193
+ */
194
+ declare function clsx(...inputs: ClassValue[]): string;
195
+
196
+ export { type ClassArray, type ClassDictionary, type ClassValue, type ColorScale, type ColorsAPI, KbachReset, type ThemeMode, ThemeProvider, type ThemeProviderProps, type ThemeState, clsx, disableRuntimeCSS, getGlobalDarkMode, getGlobalThemeMode, initKbach, isKbachReady, isRuntimeCSSDisabled, kb, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode, useColors, useTheme };
package/dist/index.d.ts CHANGED
@@ -145,4 +145,52 @@ type ColorsAPI = {
145
145
  */
146
146
  declare function useColors(): ColorsAPI;
147
147
 
148
- export { type ColorScale, type ColorsAPI, KbachReset, type ThemeMode, ThemeProvider, type ThemeProviderProps, type ThemeState, disableRuntimeCSS, getGlobalDarkMode, getGlobalThemeMode, initKbach, isKbachReady, isRuntimeCSSDisabled, kb, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode, useColors, useTheme };
148
+ /**
149
+ * A tiny class-name composer — combine strings, arrays, and objects (with
150
+ * boolean values) into one space-separated className, skipping anything
151
+ * falsy. Matches the widely-used `clsx` npm package's public API and
152
+ * behavior exactly (a from-scratch reimplementation, not a fork or a thin
153
+ * wrapper around it) — this package ships its own copy so using it adds
154
+ * zero runtime dependencies, and so it works identically whether or not
155
+ * `clsx`/`classnames` happens to already be installed.
156
+ *
157
+ * Kbach class strings never NEED this — a Kbach `className` is just a
158
+ * plain string, composable with plain JS (`"bg-" + color + "-6"`,
159
+ * `[base, active && "opacity-100"].join(" ")`, see this package's own
160
+ * README) with no special API required. This exists purely as an
161
+ * ergonomic convenience for the common "several conditional classes on one
162
+ * element" shape, for anyone who'd rather write it declaratively:
163
+ *
164
+ * ```ts
165
+ * clsx("px-4 py-2", isActive && "bg-blue-6", { "opacity-50": disabled });
166
+ * // "px-4 py-2 bg-blue-6" (when isActive is true and disabled is false)
167
+ * ```
168
+ *
169
+ * The static build-time scanner (vite-plugin/postcss-plugin) already
170
+ * recognizes `clsx(...)` call sites (alongside `cn`/`classnames`/`cx`/
171
+ * `kb`) and extracts every string literal inside them on its own — using
172
+ * this function needs no extra plugin configuration to keep working with
173
+ * the static CSS build.
174
+ *
175
+ * Identical to `@kbach/react-native`'s own `clsx.ts` — deliberately
176
+ * duplicated rather than factored into a shared package, since this is
177
+ * genuinely all there is to it and a whole extra published package for one
178
+ * ~20-line function would be more ceremony than the thing itself.
179
+ */
180
+ /** Anything `clsx` accepts as one argument — recursively, for arrays. */
181
+ type ClassValue = ClassArray | ClassDictionary | string | number | bigint | boolean | null | undefined;
182
+ /** A key is kept (as a literal class name) only when its value is truthy — `{ "opacity-50": disabled }`. */
183
+ interface ClassDictionary {
184
+ [className: string]: unknown;
185
+ }
186
+ type ClassArray = ClassValue[];
187
+ /**
188
+ * Joins every truthy piece across all arguments with a single space,
189
+ * skipping falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`)
190
+ * entirely — they contribute nothing, not even an extra space. Arrays are
191
+ * flattened (recursively, to any depth); a plain object keeps only the
192
+ * keys whose value is truthy.
193
+ */
194
+ declare function clsx(...inputs: ClassValue[]): string;
195
+
196
+ export { type ClassArray, type ClassDictionary, type ClassValue, type ColorScale, type ColorsAPI, KbachReset, type ThemeMode, ThemeProvider, type ThemeProviderProps, type ThemeState, clsx, disableRuntimeCSS, getGlobalDarkMode, getGlobalThemeMode, initKbach, isKbachReady, isRuntimeCSSDisabled, kb, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode, useColors, useTheme };
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  KbachReset: () => KbachReset,
34
34
  ThemeProvider: () => ThemeProvider,
35
35
  applyKbachConfig: () => applyKbachConfig,
36
+ clsx: () => clsx,
36
37
  defaultTheme: () => defaultTheme,
37
38
  disableRuntimeCSS: () => disableRuntimeCSS,
38
39
  getGlobalDarkMode: () => getGlobalDarkMode,
@@ -70,7 +71,7 @@ function isKbachReady() {
70
71
  function generateCss(classString, themeJson) {
71
72
  if (!ready) {
72
73
  throw new Error(
73
- "[kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
74
+ "[Kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
74
75
  );
75
76
  }
76
77
  return (0, import_core_engine.generate_css)(classString, themeJson);
@@ -846,7 +847,9 @@ function ThemeProvider({ children, defaultMode = "system" }) {
846
847
  if (mountedProviderCount > 1 && !warnedMultipleProviders && process.env.NODE_ENV !== "production") {
847
848
  warnedMultipleProviders = true;
848
849
  console.warn(
849
- "[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store useTheme() reads everywhere, not scoped per-provider, so their defaultMode seeds will fight over the same state."
850
+ "%c[Kbach]%c Multiple <ThemeProvider> instances are mounted at once.\nDark mode is one global store \u2014 useTheme() reads it everywhere, not scoped per provider \u2014 so their defaultMode seeds will fight over the same state.\nMount exactly one <ThemeProvider>, as high in the tree as convenient.",
851
+ "background:#7c3aed;color:#fff;font-weight:600;padding:1px 6px;border-radius:3px",
852
+ "font-weight:400"
850
853
  );
851
854
  }
852
855
  return () => {
@@ -926,11 +929,38 @@ function useColors() {
926
929
  ensureColorVariablesInjected(theme);
927
930
  return (0, import_react3.useMemo)(() => makeColorsProxy(theme, isDark2), [theme, isDark2]);
928
931
  }
932
+
933
+ // src/clsx.ts
934
+ function clsx(...inputs) {
935
+ let out = "";
936
+ for (const input of inputs) {
937
+ const piece = resolveClassValue(input);
938
+ if (piece) out = out ? `${out} ${piece}` : piece;
939
+ }
940
+ return out;
941
+ }
942
+ function resolveClassValue(value) {
943
+ if (!value) return "";
944
+ const type = typeof value;
945
+ if (type === "string" || type === "number" || type === "bigint") return String(value);
946
+ if (Array.isArray(value)) return clsx(...value);
947
+ if (type === "object") {
948
+ let out = "";
949
+ for (const key in value) {
950
+ if (Object.prototype.hasOwnProperty.call(value, key) && value[key]) {
951
+ out = out ? `${out} ${key}` : key;
952
+ }
953
+ }
954
+ return out;
955
+ }
956
+ return "";
957
+ }
929
958
  // Annotate the CommonJS export names for ESM import in node:
930
959
  0 && (module.exports = {
931
960
  KbachReset,
932
961
  ThemeProvider,
933
962
  applyKbachConfig,
963
+ clsx,
934
964
  defaultTheme,
935
965
  disableRuntimeCSS,
936
966
  getGlobalDarkMode,
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  isKbachReady,
5
5
  isRuntimeCSSDisabled,
6
6
  kb
7
- } from "./chunk-KJY3W4GF.mjs";
7
+ } from "./chunk-BJEOK2VK.mjs";
8
8
  import {
9
9
  applyKbachConfig,
10
10
  ensureColorVariablesInjected,
@@ -54,7 +54,9 @@ function ThemeProvider({ children, defaultMode = "system" }) {
54
54
  if (mountedProviderCount > 1 && !warnedMultipleProviders && process.env.NODE_ENV !== "production") {
55
55
  warnedMultipleProviders = true;
56
56
  console.warn(
57
- "[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store useTheme() reads everywhere, not scoped per-provider, so their defaultMode seeds will fight over the same state."
57
+ "%c[Kbach]%c Multiple <ThemeProvider> instances are mounted at once.\nDark mode is one global store \u2014 useTheme() reads it everywhere, not scoped per provider \u2014 so their defaultMode seeds will fight over the same state.\nMount exactly one <ThemeProvider>, as high in the tree as convenient.",
58
+ "background:#7c3aed;color:#fff;font-weight:600;padding:1px 6px;border-radius:3px",
59
+ "font-weight:400"
58
60
  );
59
61
  }
60
62
  return () => {
@@ -134,10 +136,37 @@ function useColors() {
134
136
  ensureColorVariablesInjected(theme);
135
137
  return useMemo2(() => makeColorsProxy(theme, isDark), [theme, isDark]);
136
138
  }
139
+
140
+ // src/clsx.ts
141
+ function clsx(...inputs) {
142
+ let out = "";
143
+ for (const input of inputs) {
144
+ const piece = resolveClassValue(input);
145
+ if (piece) out = out ? `${out} ${piece}` : piece;
146
+ }
147
+ return out;
148
+ }
149
+ function resolveClassValue(value) {
150
+ if (!value) return "";
151
+ const type = typeof value;
152
+ if (type === "string" || type === "number" || type === "bigint") return String(value);
153
+ if (Array.isArray(value)) return clsx(...value);
154
+ if (type === "object") {
155
+ let out = "";
156
+ for (const key in value) {
157
+ if (Object.prototype.hasOwnProperty.call(value, key) && value[key]) {
158
+ out = out ? `${out} ${key}` : key;
159
+ }
160
+ }
161
+ return out;
162
+ }
163
+ return "";
164
+ }
137
165
  export {
138
166
  KbachReset,
139
167
  ThemeProvider,
140
168
  applyKbachConfig,
169
+ clsx,
141
170
  defaultTheme,
142
171
  disableRuntimeCSS,
143
172
  getGlobalDarkMode,
@@ -45,7 +45,7 @@ var ready = false;
45
45
  function generateCss(classString, themeJson) {
46
46
  if (!ready) {
47
47
  throw new Error(
48
- "[kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
48
+ "[Kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
49
49
  );
50
50
  }
51
51
  return (0, import_core_engine.generate_css)(classString, themeJson);
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-D7OC2AE7.mjs";
6
- import "./chunk-KJY3W4GF.mjs";
5
+ } from "./chunk-5EXGGZMA.mjs";
6
+ import "./chunk-BJEOK2VK.mjs";
7
7
  import "./chunk-OVKVZA7B.mjs";
8
8
 
9
9
  // src/jsx-dev-runtime.tsx
@@ -44,7 +44,7 @@ var ready = false;
44
44
  function generateCss(classString, themeJson) {
45
45
  if (!ready) {
46
46
  throw new Error(
47
- "[kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
47
+ "[Kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb()."
48
48
  );
49
49
  }
50
50
  return (0, import_core_engine.generate_css)(classString, themeJson);
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-D7OC2AE7.mjs";
6
- import "./chunk-KJY3W4GF.mjs";
5
+ } from "./chunk-5EXGGZMA.mjs";
6
+ import "./chunk-BJEOK2VK.mjs";
7
7
  import "./chunk-OVKVZA7B.mjs";
8
8
  export {
9
9
  Fragment,
@@ -485,8 +485,8 @@ function formatKbachCSS(tokenRules, theme, usedTags) {
485
485
 
486
486
  // src/vite-plugin/unknownClassWarnings.ts
487
487
  var import_path2 = require("path");
488
- var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
489
- var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
488
+
489
+ // src/terminalWarn.ts
490
490
  var useColor = !!process.stdout?.isTTY && !process.env.NO_COLOR;
491
491
  var paint = (code, s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
492
492
  var green = (s) => paint("32", s);
@@ -494,9 +494,14 @@ var blue = (s) => paint("34", s);
494
494
  var white = (s) => paint("97", s);
495
495
  var gray = (s) => paint("90", s);
496
496
  var bold = (s) => paint("1", s);
497
- var ARROW = () => green("\u2192");
498
- var TAG = () => bold(green("[kbach]"));
499
497
  var highlight = (s) => bold(white(s));
498
+ function kbachTag() {
499
+ return `${green("\u2192")} ${bold(green("[Kbach]"))}`;
500
+ }
501
+
502
+ // src/vite-plugin/unknownClassWarnings.ts
503
+ var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
504
+ var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
500
505
  function findLineCol(code, needle) {
501
506
  const index = code.indexOf(needle);
502
507
  if (index === -1) return null;
@@ -526,15 +531,21 @@ function stripModifierPrefix(token) {
526
531
  return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
527
532
  }
528
533
  var MARKER_CLASSES = /* @__PURE__ */ new Set(["group", "peer"]);
534
+ function isMarkerClass(base) {
535
+ if (MARKER_CLASSES.has(base)) return true;
536
+ const slashIdx = base.lastIndexOf("/");
537
+ if (slashIdx === -1) return false;
538
+ return MARKER_CLASSES.has(base.slice(0, slashIdx)) && slashIdx < base.length - 1;
539
+ }
529
540
  function warnIfUnknownClass(token, filePath, code, isResolvable, projectCssClasses, warnedTokens) {
530
541
  if (isResolvable || warnedTokens.has(token)) return;
531
542
  const base = stripModifierPrefix(token);
532
- if (!base || projectCssClasses.has(base) || MARKER_CLASSES.has(base)) return;
543
+ if (!base || projectCssClasses.has(base) || isMarkerClass(base)) return;
533
544
  warnedTokens.add(token);
534
545
  const pos = findLineCol(code, token);
535
546
  const location = pos ? `${blue(filePath)}${green(`:${pos.line}:${pos.column}`)}` : blue(filePath);
536
547
  console.warn(
537
- `${ARROW()} ${TAG()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
548
+ `${kbachTag()} ${gray("Unknown class")} ${highlight(token)} ${gray("\u2014 no Kbach utility or project CSS rule matches it. Typo?")}
538
549
  ${bold("at")} ${location}`
539
550
  );
540
551
  }
@@ -1174,7 +1185,8 @@ function spliceGeneratedCss(root, generatedCss) {
1174
1185
  }
1175
1186
  if (startIdx === -1 || endIdx === -1) {
1176
1187
  console.warn(
1177
- `[kbach] postcss plugin: no "/* kbach:start */" / "/* kbach:end */" marker pair found \u2014 add both to the file this plugin processes (see @kbach/react/vite's own convention). Nothing was generated.`
1188
+ `${kbachTag()} ${gray("No")} ${highlight("/* kbach:start */")}${gray(" / ")}${highlight("/* kbach:end */")} ${gray("marker pair found in this file \u2014 nothing was generated.")}
1189
+ ${bold("fix:")} ${gray("add both markers to the file this plugin processes (same convention the Vite plugin's generated kbach.css uses).")}`
1178
1190
  );
1179
1191
  return;
1180
1192
  }
@@ -1,8 +1,12 @@
1
1
  import {
2
2
  DEFAULT_SCAN_DIRS,
3
+ bold,
3
4
  createKbachStaticCssEngine,
5
+ gray,
6
+ highlight,
7
+ kbachTag,
4
8
  resolveEffectiveTheme
5
- } from "./chunk-XV2ZLURB.mjs";
9
+ } from "./chunk-MGPV3PS6.mjs";
6
10
  import "./chunk-OWWJ2PME.mjs";
7
11
  import "./chunk-OVKVZA7B.mjs";
8
12
 
@@ -30,7 +34,8 @@ function spliceGeneratedCss(root, generatedCss) {
30
34
  }
31
35
  if (startIdx === -1 || endIdx === -1) {
32
36
  console.warn(
33
- `[kbach] postcss plugin: no "/* kbach:start */" / "/* kbach:end */" marker pair found \u2014 add both to the file this plugin processes (see @kbach/react/vite's own convention). Nothing was generated.`
37
+ `${kbachTag()} ${gray("No")} ${highlight("/* kbach:start */")}${gray(" / ")}${highlight("/* kbach:end */")} ${gray("marker pair found in this file \u2014 nothing was generated.")}
38
+ ${bold("fix:")} ${gray("add both markers to the file this plugin processes (same convention the Vite plugin's generated kbach.css uses).")}`
34
39
  );
35
40
  return;
36
41
  }