@kbach/ui 0.1.0-beta.7 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,205 +1,63 @@
1
1
  # @kbach/ui
2
2
 
3
- Tailwind-like utility classes for Reactweb, React Native, and Expo. Write `className` strings once; a custom JSX runtime resolves them at render time on every platform. An optional Vite plugin outputs a static `kbach.css` for zero runtime cost on web.
3
+ A tiny, dependency-free class-name composerthe same public API and
4
+ behavior as the widely-used [`clsx`](https://www.npmjs.com/package/clsx)
5
+ package, reimplemented from scratch so using it adds zero runtime
6
+ dependencies to your project.
4
7
 
5
- ```jsx
6
- <div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
7
- <div className="bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-lg px-6 py-3" />
8
- ```
9
-
10
- [npm package](https://www.npmjs.com/package/@kbach/ui)
8
+ > Beta — API may still change before 1.0.0.
11
9
 
12
- ## Setup
10
+ ## Install
13
11
 
14
- ```
12
+ ```sh
15
13
  npm install @kbach/ui
16
14
  ```
17
15
 
18
- ### JSX runtime (always required)
19
-
20
- **tsconfig.json:**
21
-
22
- ```json
23
- { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@kbach/ui" } }
24
- ```
25
-
26
- That's the only setting needed — Vite, Next.js, and React Router all read it. Don't *also* set `jsxImportSource` on a bundler plugin (e.g. `@vitejs/plugin-react`); one source of truth avoids conflicts.
27
-
28
- ### Which setup do I need?
29
-
30
- | Framework | Use |
31
- |---|---|
32
- | Vite, React Router library mode, CRA, other Vite-based | **[Static CSS](#static-css)** (recommended) — zero runtime cost, catches typos at build time |
33
- | React Router, framework mode | **[Static CSS](#static-css)** — and skip `@vitejs/plugin-react`, see note below |
34
- | Next.js | **[Next.js](#nextjs)** — Runtime setup, plus one App Router-specific detail |
35
- | React Native, Expo | **[React Native / Expo](#react-native--expo)** — different setup entirely (Babel preset, not the JSX runtime step above) |
36
-
37
- ### Static CSS
38
-
39
- Vite only, and the recommended setup for any Vite-based app — a build-time plugin writes real CSS into a file you import at build time, so nothing is generated client-side and there's zero runtime cost. Three pieces, all required:
40
-
41
- **1. Add the plugin:**
16
+ ## Usage
42
17
 
43
18
  ```ts
44
- // vite.config.ts
45
- import { defineConfig } from 'vite';
46
- import { kbach } from '@kbach/ui/vite';
19
+ import { clsx } from '@kbach/ui';
47
20
 
48
- export default defineConfig({ plugins: [kbach()] });
21
+ clsx('px-4 py-2', isActive && 'bg-blue-6', { 'opacity-50': disabled });
22
+ // "px-4 py-2 bg-blue-6" — when isActive is true and disabled is false
49
23
  ```
50
24
 
51
- **2. Create an empty stylesheet with the markers, and import it once:**
52
-
53
- ```css
54
- /* src/kbach.css */
55
- /* kbach:start */
56
- /* kbach:end */
57
- ```
25
+ Also available as the default export, matching how `clsx` itself is
26
+ conventionally imported:
58
27
 
59
28
  ```ts
60
- // main.tsx
61
- import './kbach.css';
29
+ import clsx from '@kbach/ui';
62
30
  ```
63
31
 
64
- This import is what actually switches the app over to Static CSS — the plugin alone only generates the file; without importing it, runtime injection stays active and you get both at once.
32
+ Accepts, in any combination and to any nesting depth:
65
33
 
66
- **3. Wrap your app no `<KbachReset />` here, `kbach.css` already includes the base reset:**
67
-
68
- ```jsx
69
- import { ThemeProvider } from '@kbach/ui';
70
-
71
- export default function Root() {
72
- return <ThemeProvider defaultMode="system"><App /></ThemeProvider>;
73
- }
74
- ```
75
-
76
- Done. The plugin scans your source at build time and writes CSS between the markers, and warns in the terminal (with a clickable `file:line`) for any class it doesn't recognize — usually a typo.
77
-
78
- Using a custom `kbach.config.js`? See [Wiring a custom config in](#wiring-a-custom-config-in) below — it needs to be passed in twice for Static CSS specifically.
79
-
80
- **React Router framework mode:** don't add `@vitejs/plugin-react` — `reactRouter()` already provides JSX handling, and both together crash the page (`Identifier 'RefreshRuntime' has already been declared`).
34
+ - **Strings** (`'px-4'`)kept as-is.
35
+ - **Falsy values** (`false`, `null`, `undefined`, `0`, `''`, `NaN`) — dropped
36
+ entirely, so `isActive && 'bg-blue-6'` reads naturally.
37
+ - **Objects** (`{ 'opacity-50': disabled }`) a key is kept only when its
38
+ value is truthy.
39
+ - **Arrays** (`['px-4', condition && 'py-2']`) — flattened recursively.
81
40
 
82
41
  ```ts
83
- // vite.config.ts
84
- import { reactRouter } from '@react-router/dev/vite';
85
- import { defineConfig } from 'vite';
86
- import { kbach } from '@kbach/ui/vite'; // omit if using Runtime setup instead
87
-
88
- export default defineConfig({ plugins: [kbach(), reactRouter()] });
89
- ```
90
-
91
- (React Router library mode — `createBrowserRouter`, no SSR — has no such conflict; set it up like any Vite + React app.)
92
-
93
- ### Runtime
94
-
95
- Client-side CSS injection — works with any bundler (Vite, webpack, Turbopack, Metro-for-web, …), no build plugin. Next.js always uses this, or use it on Vite if you'd rather not wire up the plugin yet:
96
-
97
- ```jsx
98
- import { ThemeProvider, KbachReset } from '@kbach/ui';
99
-
100
- export default function Root() {
101
- return (
102
- <ThemeProvider defaultMode="system">
103
- <KbachReset />
104
- <App />
105
- </ThemeProvider>
106
- );
107
- }
42
+ clsx('base', ['px-4', { 'bg-blue-6': true }], { 'opacity-50': false });
43
+ // "base px-4 bg-blue-6"
108
44
  ```
109
45
 
110
- That's it done. `<KbachReset />` renders the base reset as real markup instead of waiting on client JS — matters most for SSR, where it avoids a flash of unstyled browser defaults before hydration.
111
-
112
- Using a custom `kbach.config.js`? Pass it to `ThemeProvider` — see [Wiring a custom config in](#wiring-a-custom-config-in).
113
-
114
- Don't also set up Static CSS above in the same app — pick one.
115
-
116
- ### Next.js
117
-
118
- Always [Runtime setup](#runtime) above — Static CSS doesn't apply (webpack/Turbopack, not Vite). The `tsconfig.json` step from [Setup](#setup) applies as-is; SWC reads `jsxImportSource` the same way Vite does.
119
-
120
- The one Next.js-specific detail: render `<KbachReset />` once in the root App Router `layout.tsx`:
121
-
122
- ```jsx
123
- // app/layout.tsx
124
- import { ThemeProvider, KbachReset } from '@kbach/ui';
125
-
126
- export default function RootLayout({ children }) {
127
- return (
128
- <html lang="en">
129
- <body>
130
- <ThemeProvider defaultMode="system">
131
- <KbachReset />
132
- {children}
133
- </ThemeProvider>
134
- </body>
135
- </html>
136
- );
137
- }
138
- ```
139
-
140
- Without it, expect a flash of raw browser defaults on first paint until hydration completes. `@kbach/ui`'s compiled output ships its own `"use client"` directive, so App Router Server Components can use `className`, `styled()`, hooks, `<ThemeProvider>`, and `<KbachReset>` directly — no manual `'use client'` wrapper needed.
141
-
142
- ### React Native / Expo
143
-
144
- Same `npm install @kbach/ui` — no separate package. Everything else (API, modifiers, color system) is the same import as web; only setup differs.
145
-
146
- **1. babel.config.js:**
147
-
148
- ```js
149
- module.exports = function (api) {
150
- api.cache(true);
151
- return {
152
- presets: [
153
- 'babel-preset-expo',
154
- '@kbach/ui/babel',
155
- ],
156
- };
157
- };
158
- ```
159
-
160
- Or the one-liner helper: `const { createKbachConfig } = require('@kbach/ui/native'); module.exports = createKbachConfig();`. After changing this file, clear the Metro cache: `npx expo start --clear`.
161
-
162
- **2. Wrap your app:**
163
-
164
- ```jsx
165
- import { ThemeProvider } from '@kbach/ui';
166
-
167
- export default function App() {
168
- return (
169
- <ThemeProvider defaultMode="system">
170
- <AppContent />
171
- </ThemeProvider>
172
- );
173
- }
174
- ```
175
-
176
- `ThemeProvider` auto-detects React Native at render time and reads `useColorScheme()`/`useWindowDimensions()` automatically — same import as web, no `/native` subpath needed.
177
-
178
- A handful of utilities are native-only or web-only, and Expo Web/React Native Web has its own notes — see [KBACH.md](./KBACH.md#native-only-utilities) for the full platform-differences reference.
179
-
180
- ### Wiring a custom config in
181
-
182
- `kbach.config.js` isn't picked up automatically — it has to be passed in explicitly, and **where** depends on what it affects:
183
-
184
- - **Runtime** (dark mode, `useColors()`, animations) — needed by every setup above, pass it to `ThemeProvider`:
185
- ```jsx
186
- import { ThemeProvider } from '@kbach/ui';
187
- import kbachConfig from '../kbach.config';
188
-
189
- <ThemeProvider defaultMode="system" config={kbachConfig}><App /></ThemeProvider>
190
- ```
191
- - **Build-time** (what the Vite plugin scans against) — only for [Static CSS](#static-css), pass it to the plugin:
192
- ```ts
193
- import { kbach } from '@kbach/ui/vite';
194
- import kbachConfig from './kbach.config';
195
-
196
- export default defineConfig({ plugins: [kbach(kbachConfig)] });
197
- ```
46
+ ## Do you need this with Kbach?
198
47
 
199
- Skipping the runtime one under Static CSS is an easy mistake the generated `kbach.css` looks correct, but dark mode/`useColors()`/animations silently fall back to defaults since nothing told the running app what you customized.
48
+ Not reallya Kbach `className` is just a plain string, so plain
49
+ JavaScript already composes it fine (`"bg-" + color + '-6'`,
50
+ `[base, active && 'opacity-100'].join(' ')`; see
51
+ [`@kbach/react`](https://www.npmjs.com/package/@kbach/react)'s own README).
52
+ This package exists purely as an ergonomic convenience for anyone who'd
53
+ rather write conditional classes declaratively, and for porting code that
54
+ already uses `clsx`/`classnames` without adding a second dependency.
200
55
 
201
- ## More information
56
+ `@kbach/react`'s static build-time scanner already recognizes `clsx(...)`
57
+ call sites (alongside `cn`/`classnames`/`cx`/`kb`) and extracts every string
58
+ literal inside them on its own — using this function needs no extra plugin
59
+ configuration to keep working with the static CSS build.
202
60
 
203
- [kbach-ui.md](./kbach-ui.md) — the complete reference: `ThemeProvider`/`useTheme`/dark mode, the full API (`styled`, `cx`, `useStyles`, `kb`, `useColors`, typed theme tokens), every modifier, the color system, CSS resets, and all `kbach.config.js` options — covers web and React Native/Expo.
61
+ ## License
204
62
 
205
- `@kbach/native` is deprecated and no longer maintained — its last published npm version is frozen as a compatibility shim re-exporting this package. Install `@kbach/ui` directly for new projects.
63
+ MIT
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A tiny, dependency-free class-name composer — combine strings, arrays,
3
+ * and objects (with boolean values) into one space-separated className,
4
+ * skipping anything falsy. Matches the widely-used `clsx` npm package's
5
+ * public API and behavior exactly (a from-scratch reimplementation, not a
6
+ * fork or a thin wrapper around it) — Kbach ships its own copy so using it
7
+ * adds zero runtime dependencies, and so it works identically whether or
8
+ * not `clsx`/`classnames` happens to already be installed.
9
+ *
10
+ * Kbach class strings never NEED this — a Kbach `className` is just a
11
+ * plain string, composable with plain JS (`"bg-" + color + "-6"`,
12
+ * `[base, active && "opacity-100"].join(" ")`, see `@kbach/react`'s own
13
+ * README) with no special API required. This exists purely as an
14
+ * ergonomic convenience for the common "several conditional classes on one
15
+ * element" shape, for anyone who'd rather write it declaratively:
16
+ *
17
+ * ```ts
18
+ * clsx("px-4 py-2", isActive && "bg-blue-6", { "opacity-50": disabled });
19
+ * // "px-4 py-2 bg-blue-6" (when isActive is true and disabled is false)
20
+ * ```
21
+ *
22
+ * `@kbach/react`'s static build-time scanner already recognizes `clsx(...)`
23
+ * call sites (alongside `cn`/`classnames`/`cx`/`kb`) and extracts every
24
+ * string literal inside them on its own — using this function needs no
25
+ * extra plugin configuration to keep working with the static CSS build.
26
+ */
27
+ /** Anything `clsx` accepts as one argument — recursively, for arrays. */
28
+ type ClassValue = ClassArray | ClassDictionary | string | number | bigint | boolean | null | undefined;
29
+ /** A key is kept (as a literal class name) only when its value is truthy — `{ "opacity-50": disabled }`. */
30
+ interface ClassDictionary {
31
+ [className: string]: unknown;
32
+ }
33
+ type ClassArray = ClassValue[];
34
+ /**
35
+ * Joins every truthy piece across all arguments with a single space,
36
+ * skipping falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`)
37
+ * entirely — they contribute nothing, not even an extra space. Arrays are
38
+ * flattened (recursively, to any depth); a plain object keeps only the
39
+ * keys whose value is truthy.
40
+ */
41
+ declare function clsx(...inputs: ClassValue[]): string;
42
+
43
+ export { type ClassArray, type ClassDictionary, type ClassValue, clsx, clsx as default };
package/dist/index.d.ts CHANGED
@@ -1,329 +1,43 @@
1
- import * as React from 'react';
2
- import React__default, { Context, ReactNode, ComponentType, ForwardRefExoticComponent } from 'react';
3
- import * as __core from './core';
4
- import { ThemeMode, ResolvedConfig, DefaultColorName, defaultColors, ThemeColors, DefaultSpacingKey, ThemeSpacing, FrameworkConfig, StyleValue, ResolvedStyle } from './core';
5
- export { DefaultColorName, DefaultSpacingKey, FrameworkConfig, ParsedClass, PluginAPI, ResolvedConfig, ResolvedStyle, StyleValue, ThemeConfig, ThemeMode, buildConfig, clearCache, defaultColors, defaultTheme, disableRuntimeCSS, flatten, generateKbachTypesDts, getConfig, initConfig, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, updateConfig } from './core';
6
- export { r as registerWebElement } from './web-substitute-6xH1WxpZ.js';
7
-
8
- interface ThemeContextValue {
9
- /** The user-selected mode ('light' | 'dark' | 'system') */
10
- mode: ThemeMode;
11
- /** The effective resolved mode (never 'system') */
12
- resolvedMode: 'light' | 'dark';
13
- /** Convenience boolean */
14
- isDark: boolean;
15
- /** Change the theme mode programmatically */
16
- setMode: (mode: ThemeMode) => void;
17
- /** Toggle between light and dark (ignores system) */
18
- toggle: () => void;
19
- /** The fully resolved framework config (theme values, darkMode strategy, etc.) */
20
- config: ResolvedConfig;
21
- }
22
- declare const ThemeContext: Context<ThemeContextValue | null>;
23
- declare function useTheme(): ThemeContextValue;
24
- declare function useIsDark(): boolean;
25
-
26
- interface ColorScale {
27
- /** `colors.blue[6]` → raw hex string */
28
- readonly [shade: number]: string;
29
- /** `colors.blue['6/50']` → shade 6 at 50% opacity */
30
- readonly [key: string]: string;
31
- }
32
1
  /**
33
- * Empty on purpose augment it via declaration merging so `useColors()` (and
34
- * `useSpacing()`'s equivalent, `KbachCustomSpacing`) know about a project's
35
- * `kbach.config.js` colors without repeating a type parameter at every call
36
- * site. `kbach.config.js` is a plain runtime-loaded .js file, so TypeScript
37
- * can't see into it on its ownthis is the same declaration-merging pattern
38
- * styled-components' `DefaultTheme` and i18next's resource typing use for the
39
- * identical problem. Put this in any .d.ts your tsconfig includes:
2
+ * A tiny, dependency-free class-name composer combine strings, arrays,
3
+ * and objects (with boolean values) into one space-separated className,
4
+ * skipping anything falsy. Matches the widely-used `clsx` npm package's
5
+ * public API and behavior exactly (a from-scratch reimplementation, not a
6
+ * fork or a thin wrapper around it)Kbach ships its own copy so using it
7
+ * adds zero runtime dependencies, and so it works identically whether or
8
+ * not `clsx`/`classnames` happens to already be installed.
9
+ *
10
+ * Kbach class strings never NEED this — a Kbach `className` is just a
11
+ * plain string, composable with plain JS (`"bg-" + color + "-6"`,
12
+ * `[base, active && "opacity-100"].join(" ")`, see `@kbach/react`'s own
13
+ * README) with no special API required. This exists purely as an
14
+ * ergonomic convenience for the common "several conditional classes on one
15
+ * element" shape, for anyone who'd rather write it declaratively:
40
16
  *
41
17
  * ```ts
42
- * import '@kbach/ui'; // or '@kbach/native' — either works, native re-exports react's types
43
- * declare module '@kbach/ui' {
44
- * interface KbachCustomColors {
45
- * primary: string; // a flat color, like the built-in `white`/`black`
46
- * brand: ColorScale; // a 1–12 shade scale, like the built-in `blue`/`red`
47
- * }
48
- * }
18
+ * clsx("px-4 py-2", isActive && "bg-blue-6", { "opacity-50": disabled });
19
+ * // "px-4 py-2 bg-blue-6" (when isActive is true and disabled is false)
49
20
  * ```
50
21
  *
51
- * A mode-aware `{ light, dark }` config color (see ColorValue) still resolves
52
- * to a flat `string` at read time — declare those as `string` here too, not
53
- * as the config shape.
54
- */
55
- interface KbachCustomColors {
56
- }
57
- type ColorValueFor<K extends string> = K extends keyof typeof defaultColors ? (typeof defaultColors)[K] extends string ? string : ColorScale : K extends keyof KbachCustomColors ? KbachCustomColors[K] : ColorScale | string;
58
- /** Every color name TypeScript knows about without an explicit type parameter: the built-in theme plus whatever's been added via the KbachCustomColors augmentation above. */
59
- type KnownColorName = DefaultColorName | Extract<keyof KbachCustomColors, string>;
60
- /**
61
- * `ColorName` defaults to `KnownColorName` (the built-in theme's color names
62
- * plus anything augmented onto `KbachCustomColors` above), so `useColors()`
63
- * gets full autocomplete and typo-catching out of the box — including custom
64
- * `kbach.config.js` colors, once augmented once project-wide. Without that
65
- * augmentation, a project with extra colors can still widen per call instead:
66
- * `useColors<DefaultColorName | 'brand'>()`.
67
- */
68
- type ColorsAPI<ColorName extends string = KnownColorName> = {
69
- readonly [K in ColorName]: ColorValueFor<K>;
70
- } & {
71
- /**
72
- * Pass any CSS color through, optionally applying an opacity (0–100).
73
- * - `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
74
- * - `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'`
75
- * - `colors.alpha('rgba(0,0,0,0.5)')` → `'rgba(0,0,0,0.5)'` (passthrough)
76
- */
77
- readonly alpha: (color: string, opacity?: number) => string;
78
- };
79
- declare function wrapColors<ColorName extends string = KnownColorName>(rawColors: ThemeColors, isDark?: boolean): ColorsAPI<ColorName>;
80
- declare function useColors<ColorName extends string = KnownColorName>(): ColorsAPI<ColorName>;
81
-
82
- /**
83
- * Empty on purpose — augment it via declaration merging so `useSpacing()`
84
- * (like `useColors()`'s `KbachCustomColors`) knows about a project's
85
- * `kbach.config.js` spacing keys without repeating a type parameter at every
86
- * call site:
87
- *
88
- * ```ts
89
- * import '@kbach/ui'; // or '@kbach/native'
90
- * declare module '@kbach/ui' {
91
- * interface KbachCustomSpacing {
92
- * 18: true; // value doesn't matter — only the key is read (see SpacingAPI)
93
- * }
94
- * }
95
- * ```
22
+ * `@kbach/react`'s static build-time scanner already recognizes `clsx(...)`
23
+ * call sites (alongside `cn`/`classnames`/`cx`/`kb`) and extracts every
24
+ * string literal inside them on its own — using this function needs no
25
+ * extra plugin configuration to keep working with the static CSS build.
96
26
  */
97
- interface KbachCustomSpacing {
27
+ /** Anything `clsx` accepts as one argument — recursively, for arrays. */
28
+ type ClassValue = ClassArray | ClassDictionary | string | number | bigint | boolean | null | undefined;
29
+ /** A key is kept (as a literal class name) only when its value is truthy — `{ "opacity-50": disabled }`. */
30
+ interface ClassDictionary {
31
+ [className: string]: unknown;
98
32
  }
99
- /** Every spacing key TypeScript knows about without an explicit type parameter. */
100
- type KnownSpacingKey = DefaultSpacingKey | Extract<keyof KbachCustomSpacing, string>;
101
- /**
102
- * `SpacingKey` defaults to `KnownSpacingKey` (the built-in theme's spacing keys
103
- * plus anything augmented onto `KbachCustomSpacing` above), so `useSpacing()`
104
- * gets full autocomplete and typo-catching out of the box — including custom
105
- * `kbach.config.js` keys, once augmented once project-wide. Without that
106
- * augmentation, a project with extra keys can still widen per call instead:
107
- * `useSpacing<DefaultSpacingKey | '18'>()`.
108
- */
109
- type SpacingAPI<SpacingKey extends string = KnownSpacingKey> = {
110
- readonly [K in SpacingKey]: number | string;
111
- };
112
- declare function wrapSpacing<SpacingKey extends string = KnownSpacingKey>(rawSpacing: ThemeSpacing): SpacingAPI<SpacingKey>;
33
+ type ClassArray = ClassValue[];
113
34
  /**
114
- * Returns the active theme's spacing scale as a typed, autocomplete-friendly
115
- * object useful anywhere a raw JS number/string is needed instead of a
116
- * className (Animated API distances, chart dimensions, FlatList separator
117
- * heights, etc.). Values match exactly what `p-`/`m-`/`w-`/`h-`/`gap-` and
118
- * other spacing-scale utilities resolve to.
119
- *
120
- * ```ts
121
- * const spacing = useSpacing();
122
- * spacing[4] // 16
123
- * spacing.full // '100%'
124
- * spacing['1/2'] // '50%'
125
- * ```
126
- */
127
- declare function useSpacing<SpacingKey extends string = KnownSpacingKey>(): SpacingAPI<SpacingKey>;
128
-
129
- interface ThemeProviderProps {
130
- children: ReactNode;
131
- /** Initial mode. Falls back to persisted value, then 'system'. */
132
- defaultMode?: ThemeMode;
133
- /**
134
- * System color scheme for native `defaultMode="system"`.
135
- *
136
- * On React Native this is detected automatically via `useColorScheme()` —
137
- * pass this prop only to override that (e.g. in tests, or Storybook).
138
- *
139
- * @example
140
- * ```tsx
141
- * <ThemeProvider defaultMode="system" colorScheme="dark">…</ThemeProvider>
142
- * ```
143
- */
144
- colorScheme?: 'light' | 'dark' | null;
145
- /**
146
- * Current window/screen width in pixels for responsive breakpoints.
147
- * Detected automatically on both web (`window.innerWidth`) and React
148
- * Native (`useWindowDimensions()`) — pass this prop only to override that.
149
- */
150
- windowWidth?: number;
151
- /** Override the config (useful for per-tree config). Defaults to global getConfig(). */
152
- config?: FrameworkConfig;
153
- /** Disable persistence to localStorage */
154
- disablePersistence?: boolean;
155
- }
156
- declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth: windowWidthProp, config: configOverride, disablePersistence, }: ThemeProviderProps): React__default.JSX.Element;
157
-
158
- /**
159
- * Renders Kbach's base browser-default reset (borderless button/input,
160
- * visible checkbox/radio, no arrow-less <select>, etc.) as a plain <style>
161
- * tag so it's part of the page's initial HTML.
162
- *
163
- * Runtime-only setups (no Vite plugin / static kbach.css) otherwise only get
164
- * this reset once ThemeProvider's client-side effect runs — fine for a plain
165
- * CSR app, but under SSR the server has no JS to run it, so the first paint
166
- * ships with raw browser defaults (e.g. the native button border) until
167
- * hydration catches up. Render this once, as high in <head> as your
168
- * framework allows, to close that gap.
169
- *
170
- * Skips rendering entirely once disableRuntimeCSS() has fired — the Vite
171
- * plugin calls it automatically wherever kbach.css gets imported (see
172
- * vite-plugin.ts's transform hook), and that static file already inlines
173
- * this exact reset. Previously this rendered unconditionally regardless of
174
- * that flag, so an app using the static-CSS setup that ALSO keeps
175
- * <KbachReset/> mounted (e.g. copied from a starter template's root layout
176
- * and never revisited) got the same reset rules twice — once from the
177
- * static file, once from this tag — on every single page load, real static
178
- * CSS or not. Checked here (render time), not just documented, so the
179
- * static setup is actually leak-free even when this stays in the tree.
180
- */
181
- declare function KbachReset(): React.JSX.Element | null;
182
-
183
- interface StyledProps {
184
- /** Additional utility classes applied on top of the base classes */
185
- kb?: string;
186
- /** Merged with resolved kb styles; applied last */
187
- style?: StyleValue | StyleValue[];
188
- }
189
- type OmittedKeys = 'style';
190
- /**
191
- * Create a styled component from any React / React Native component.
192
- *
193
- * ```tsx
194
- * const Card = styled(View, 'bg-white dark:bg-gray-9 rounded-xl p-4 shadow-md');
195
- * const Button = styled(TouchableOpacity, 'bg-blue-6 pressed:bg-blue-8 dark:bg-blue-7 rounded-lg p-3');
196
- *
197
- * // Use it:
198
- * <Card kb="mt-4">...</Card>
199
- * <Button onPress={handlePress} kb="w-full" />
200
- * ```
201
- */
202
- declare function styled<T extends ComponentType<any>>(Component: T, baseClasses?: string): ForwardRefExoticComponent<Omit<React__default.ComponentPropsWithRef<T>, OmittedKeys> & StyledProps>;
203
-
204
- interface InteractionState {
205
- hover?: boolean;
206
- focus?: boolean;
207
- /** Maps to 'pressed' and 'active' modifiers */
208
- pressed?: boolean;
209
- active?: boolean;
210
- disabled?: boolean;
211
- checked?: boolean;
212
- visited?: boolean;
213
- placeholder?: boolean;
214
- }
215
- /**
216
- * Resolve a utility class string to a style object for the current theme + state.
217
- *
218
- * ```tsx
219
- * // Basic usage
220
- * const styles = useStyles('bg-white dark:bg-gray-9 p-4');
221
- *
222
- * // With interaction state
223
- * const [pressed, setPressed] = useState(false);
224
- * const styles = useStyles('bg-blue-6 pressed:bg-blue-8', { pressed });
225
- *
226
- * // Multiple class strings (merged left-to-right)
227
- * const styles = useStyles(['bg-white p-4', 'dark:bg-gray-9 rounded-xl']);
228
- * ```
229
- */
230
- declare function useStyles(classString: string | string[], state?: InteractionState): StyleValue;
231
- /**
232
- * Returns the full ResolvedStyle bucket map (base, dark, hover, …).
233
- * Useful when you need to apply styles selectively or pass them to Animated.
234
- */
235
- declare function useResolvedStyle(classString: string | string[]): __core.ResolvedStyle;
236
-
237
- /**
238
- * Subscribe to the global dark-mode store with React's concurrent-safe
239
- * useSyncExternalStore so that any component using className/kb re-renders
240
- * immediately when the theme changes — without needing ThemeContext.
241
- */
242
- declare function useGlobalDarkMode(): boolean;
243
-
244
- /**
245
- * Returns the name of the currently active breakpoint — the largest breakpoint
246
- * whose min-width the window satisfies, or `'xs'` when below all breakpoints.
247
- *
248
- * ```ts
249
- * const bp = useBreakpoint(); // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
250
- * ```
251
- */
252
- declare function useBreakpoint(): string;
253
- /**
254
- * Returns a record of boolean flags for each breakpoint — `true` when the
255
- * window width satisfies that breakpoint's min-width threshold.
256
- *
257
- * ```ts
258
- * const { sm, md, lg } = useResponsive();
259
- * const padding = lg ? 32 : sm ? 16 : 8;
260
- * ```
261
- */
262
- declare function useResponsive(): Record<string, boolean>;
263
-
264
- interface InteractiveWrapperProps {
265
- /** The real component to render (View, TouchableOpacity, div, a third-party button…) */
266
- Component: React__default.ComponentType<any> | string;
267
- /** Pre-resolved style buckets from resolve() */
268
- resolvedStyle: ResolvedStyle;
269
- /** On web: original class string so CSS pseudo-rules still fire */
270
- className?: string;
271
- /** Extra style prop passed by the user */
272
- style?: StyleValue | StyleValue[];
273
- children?: React__default.ReactNode;
274
- onPressIn?: (...args: any[]) => void;
275
- onPressOut?: (...args: any[]) => void;
276
- onPointerDown?: (...args: any[]) => void;
277
- onPointerUp?: (...args: any[]) => void;
278
- onPointerLeave?: (...args: any[]) => void;
279
- onPointerCancel?: (...args: any[]) => void;
280
- onMouseEnter?: (...args: any[]) => void;
281
- onMouseLeave?: (...args: any[]) => void;
282
- onFocus?: (...args: any[]) => void;
283
- onBlur?: (...args: any[]) => void;
284
- }
285
- /**
286
- * Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
287
- * string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
288
- *
289
- * Manages interaction state locally and flattens the correct style bucket on
290
- * every render. The wrapped component sees a plain `style` prop — it never
291
- * knows it was wrapped.
292
- *
293
- * Refs are forwarded so the host component's imperative API still works.
294
- */
295
- declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<InteractiveWrapperProps & React__default.RefAttributes<unknown>>;
296
-
297
- /**
298
- * Resolve a utility class string outside of a React component.
299
- *
300
- * On **native** — returns a StyleValue (style object) for the given mode.
301
- * On **web** — injects CSS and returns the original class string (use as className).
302
- *
303
- * ```ts
304
- * // Inside a component use useStyles() instead.
305
- * // kb() is useful for StyleSheet.create() calls and static values.
306
- *
307
- * const styles = StyleSheet.create({
308
- * container: kb('flex-1 bg-white p-4') as any,
309
- * });
310
- *
311
- * // Web: use as className
312
- * <div className={kb('bg-white dark:bg-gray-9 p-4') as string} />
313
- * ```
314
- *
315
- * @param classString Space-separated utility classes
316
- * @param isDark Whether dark mode is active (default: false)
317
- */
318
- declare function kb(classString: string, isDark?: boolean): StyleValue | string;
319
- /**
320
- * Conditionally join class names. Falsy values are ignored.
321
- *
322
- * ```ts
323
- * cx('bg-white p-4', isActive && 'border-2 border-blue-6', undefined)
324
- * // → 'bg-white p-4 border-2 border-blue-6'
325
- * ```
35
+ * Joins every truthy piece across all arguments with a single space,
36
+ * skipping falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`)
37
+ * entirely they contribute nothing, not even an extra space. Arrays are
38
+ * flattened (recursively, to any depth); a plain object keeps only the
39
+ * keys whose value is truthy.
326
40
  */
327
- declare function cx(...classes: Array<string | false | null | undefined>): string;
41
+ declare function clsx(...inputs: ClassValue[]): string;
328
42
 
329
- export { type ColorScale, type ColorsAPI, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type KbachCustomColors, type KbachCustomSpacing, KbachReset, type SpacingAPI, type StyledProps, ThemeContext, type ThemeContextValue, ThemeProvider, type ThemeProviderProps, cx, kb, styled, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useSpacing, useStyles, useTheme, wrapColors, wrapSpacing };
43
+ export { type ClassArray, type ClassDictionary, type ClassValue, clsx, clsx as default };