@kbach/ui 0.1.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.
@@ -0,0 +1,328 @@
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 global {
23
+ var __kbachThemeContext: Context<ThemeContextValue | null> | undefined;
24
+ }
25
+ declare const ThemeContext: Context<ThemeContextValue | null>;
26
+ declare function useTheme(): ThemeContextValue;
27
+ declare function useIsDark(): boolean;
28
+
29
+ interface ColorScale {
30
+ /** `colors.blue[6]` → raw hex string */
31
+ readonly [shade: number]: string;
32
+ /** `colors.blue['6/50']` → shade 6 at 50% opacity */
33
+ readonly [key: string]: string;
34
+ }
35
+ /**
36
+ * Empty on purpose — augment it via declaration merging so `useColors()` (and
37
+ * `useSpacing()`'s equivalent, `KbachCustomSpacing`) know about a project's
38
+ * `kbach.config.js` colors without repeating a type parameter at every call
39
+ * site. `kbach.config.js` is a plain runtime-loaded .js file, so TypeScript
40
+ * can't see into it on its own — this is the same declaration-merging pattern
41
+ * styled-components' `DefaultTheme` and i18next's resource typing use for the
42
+ * identical problem. Put this in any .d.ts your tsconfig includes:
43
+ *
44
+ * ```ts
45
+ * import '@kbach/ui'; // or '@kbach/native' — either works, native re-exports react's types
46
+ * declare module '@kbach/ui' {
47
+ * interface KbachCustomColors {
48
+ * primary: string; // a flat color, like the built-in `white`/`black`
49
+ * brand: ColorScale; // a 1–12 shade scale, like the built-in `blue`/`red`
50
+ * }
51
+ * }
52
+ * ```
53
+ *
54
+ * A mode-aware `{ light, dark }` config color (see ColorValue) still resolves
55
+ * to a flat `string` at read time — declare those as `string` here too, not
56
+ * as the config shape.
57
+ */
58
+ interface KbachCustomColors {
59
+ }
60
+ 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;
61
+ /** 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. */
62
+ type KnownColorName = DefaultColorName | Extract<keyof KbachCustomColors, string>;
63
+ /**
64
+ * `ColorName` defaults to `KnownColorName` (the built-in theme's color names
65
+ * plus anything augmented onto `KbachCustomColors` above), so `useColors()`
66
+ * gets full autocomplete and typo-catching out of the box — including custom
67
+ * `kbach.config.js` colors, once augmented once project-wide. Without that
68
+ * augmentation, a project with extra colors can still widen per call instead:
69
+ * `useColors<DefaultColorName | 'brand'>()`.
70
+ */
71
+ type ColorsAPI<ColorName extends string = KnownColorName> = {
72
+ readonly [K in ColorName]: ColorValueFor<K>;
73
+ } & {
74
+ /**
75
+ * Pass any CSS color through, optionally applying an opacity (0–100).
76
+ * - `colors.alpha('#3b82f6', 50)` → `'rgba(59,130,246,0.5)'`
77
+ * - `colors.alpha('rgb(0,0,0)', 10)` → `'rgba(0,0,0,0.1)'`
78
+ * - `colors.alpha('rgba(0,0,0,0.5)')` → `'rgba(0,0,0,0.5)'` (passthrough)
79
+ */
80
+ readonly alpha: (color: string, opacity?: number) => string;
81
+ };
82
+ declare function wrapColors<ColorName extends string = KnownColorName>(rawColors: ThemeColors, isDark?: boolean): ColorsAPI<ColorName>;
83
+ declare function useColors<ColorName extends string = KnownColorName>(): ColorsAPI<ColorName>;
84
+
85
+ /**
86
+ * Empty on purpose — augment it via declaration merging so `useSpacing()`
87
+ * (like `useColors()`'s `KbachCustomColors`) knows about a project's
88
+ * `kbach.config.js` spacing keys without repeating a type parameter at every
89
+ * call site:
90
+ *
91
+ * ```ts
92
+ * import '@kbach/ui'; // or '@kbach/native'
93
+ * declare module '@kbach/ui' {
94
+ * interface KbachCustomSpacing {
95
+ * 18: true; // value doesn't matter — only the key is read (see SpacingAPI)
96
+ * }
97
+ * }
98
+ * ```
99
+ */
100
+ interface KbachCustomSpacing {
101
+ }
102
+ /** Every spacing key TypeScript knows about without an explicit type parameter. */
103
+ type KnownSpacingKey = DefaultSpacingKey | Extract<keyof KbachCustomSpacing, string>;
104
+ /**
105
+ * `SpacingKey` defaults to `KnownSpacingKey` (the built-in theme's spacing keys
106
+ * plus anything augmented onto `KbachCustomSpacing` above), so `useSpacing()`
107
+ * gets full autocomplete and typo-catching out of the box — including custom
108
+ * `kbach.config.js` keys, once augmented once project-wide. Without that
109
+ * augmentation, a project with extra keys can still widen per call instead:
110
+ * `useSpacing<DefaultSpacingKey | '18'>()`.
111
+ */
112
+ type SpacingAPI<SpacingKey extends string = KnownSpacingKey> = {
113
+ readonly [K in SpacingKey]: number | string;
114
+ };
115
+ declare function wrapSpacing<SpacingKey extends string = KnownSpacingKey>(rawSpacing: ThemeSpacing): SpacingAPI<SpacingKey>;
116
+ /**
117
+ * Returns the active theme's spacing scale as a typed, autocomplete-friendly
118
+ * object — useful anywhere a raw JS number/string is needed instead of a
119
+ * className (Animated API distances, chart dimensions, FlatList separator
120
+ * heights, etc.). Values match exactly what `p-`/`m-`/`w-`/`h-`/`gap-` and
121
+ * other spacing-scale utilities resolve to.
122
+ *
123
+ * ```ts
124
+ * const spacing = useSpacing();
125
+ * spacing[4] // 16
126
+ * spacing.full // '100%'
127
+ * spacing['1/2'] // '50%'
128
+ * ```
129
+ */
130
+ declare function useSpacing<SpacingKey extends string = KnownSpacingKey>(): SpacingAPI<SpacingKey>;
131
+
132
+ interface ThemeProviderProps {
133
+ children: ReactNode;
134
+ /** Initial mode. Falls back to persisted value, then 'system'. */
135
+ defaultMode?: ThemeMode;
136
+ /**
137
+ * System color scheme for native `defaultMode="system"`.
138
+ *
139
+ * Pass the value of `useColorScheme()` from `react-native`. When importing
140
+ * `ThemeProvider` from `@kbach/ui/native` this is handled automatically.
141
+ *
142
+ * @example
143
+ * ```tsx
144
+ * import { useColorScheme } from 'react-native';
145
+ * const colorScheme = useColorScheme();
146
+ * <ThemeProvider defaultMode="system" colorScheme={colorScheme}>…</ThemeProvider>
147
+ * ```
148
+ */
149
+ colorScheme?: 'light' | 'dark' | null;
150
+ /**
151
+ * Current window/screen width in pixels for responsive breakpoints.
152
+ * On web this is read from `window.innerWidth` automatically.
153
+ * When importing `ThemeProvider` from `@kbach/ui/native` this is provided
154
+ * automatically from `useWindowDimensions()`.
155
+ */
156
+ windowWidth?: number;
157
+ /** Override the config (useful for per-tree config). Defaults to global getConfig(). */
158
+ config?: FrameworkConfig;
159
+ /** Disable persistence to localStorage */
160
+ disablePersistence?: boolean;
161
+ }
162
+ declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth: windowWidthProp, config: configOverride, disablePersistence, }: ThemeProviderProps): React__default.JSX.Element;
163
+
164
+ /**
165
+ * Renders Kbach's base browser-default reset (borderless button/input,
166
+ * visible checkbox/radio, no arrow-less <select>, etc.) as a plain <style>
167
+ * tag so it's part of the page's initial HTML.
168
+ *
169
+ * Runtime-only setups (no Vite plugin / static kbach.css) otherwise only get
170
+ * this reset once ThemeProvider's client-side effect runs — fine for a plain
171
+ * CSR app, but under SSR the server has no JS to run it, so the first paint
172
+ * ships with raw browser defaults (e.g. the native button border) until
173
+ * hydration catches up. Render this once, as high in <head> as your
174
+ * framework allows, to close that gap.
175
+ *
176
+ * Not needed if you're on the static-CSS setup — kbach.css already includes
177
+ * the same reset, and the runtime injector detects this tag and skips adding
178
+ * it a second time either way.
179
+ */
180
+ declare function KbachReset(): React.JSX.Element | null;
181
+
182
+ interface StyledProps {
183
+ /** Additional utility classes applied on top of the base classes */
184
+ kb?: string;
185
+ /** Merged with resolved kb styles; applied last */
186
+ style?: StyleValue | StyleValue[];
187
+ }
188
+ type OmittedKeys = 'style';
189
+ /**
190
+ * Create a styled component from any React / React Native component.
191
+ *
192
+ * ```tsx
193
+ * const Card = styled(View, 'bg-white dark:bg-gray-800 rounded-xl p-4 shadow-md');
194
+ * const Button = styled(TouchableOpacity, 'bg-blue-500 pressed:bg-blue-700 dark:bg-blue-600 rounded-lg p-3');
195
+ *
196
+ * // Use it:
197
+ * <Card kb="mt-4">...</Card>
198
+ * <Button onPress={handlePress} kb="w-full" />
199
+ * ```
200
+ */
201
+ declare function styled<T extends ComponentType<any>>(Component: T, baseClasses?: string): ForwardRefExoticComponent<Omit<React__default.ComponentPropsWithRef<T>, OmittedKeys> & StyledProps>;
202
+
203
+ interface InteractionState {
204
+ hover?: boolean;
205
+ focus?: boolean;
206
+ /** Maps to 'pressed' and 'active' modifiers */
207
+ pressed?: boolean;
208
+ active?: boolean;
209
+ disabled?: boolean;
210
+ checked?: boolean;
211
+ visited?: boolean;
212
+ placeholder?: boolean;
213
+ }
214
+ /**
215
+ * Resolve a utility class string to a style object for the current theme + state.
216
+ *
217
+ * ```tsx
218
+ * // Basic usage
219
+ * const styles = useStyles('bg-white dark:bg-gray-900 p-4');
220
+ *
221
+ * // With interaction state
222
+ * const [pressed, setPressed] = useState(false);
223
+ * const styles = useStyles('bg-blue-500 pressed:bg-blue-700', { pressed });
224
+ *
225
+ * // Multiple class strings (merged left-to-right)
226
+ * const styles = useStyles(['bg-white p-4', 'dark:bg-gray-900 rounded-xl']);
227
+ * ```
228
+ */
229
+ declare function useStyles(classString: string | string[], state?: InteractionState): StyleValue;
230
+ /**
231
+ * Returns the full ResolvedStyle bucket map (base, dark, hover, …).
232
+ * Useful when you need to apply styles selectively or pass them to Animated.
233
+ */
234
+ declare function useResolvedStyle(classString: string | string[]): __core.ResolvedStyle;
235
+
236
+ /**
237
+ * Subscribe to the global dark-mode store with React's concurrent-safe
238
+ * useSyncExternalStore so that any component using className/kb re-renders
239
+ * immediately when the theme changes — without needing ThemeContext.
240
+ */
241
+ declare function useGlobalDarkMode(): boolean;
242
+
243
+ /**
244
+ * Returns the name of the currently active breakpoint — the largest breakpoint
245
+ * whose min-width the window satisfies, or `'xs'` when below all breakpoints.
246
+ *
247
+ * ```ts
248
+ * const bp = useBreakpoint(); // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
249
+ * ```
250
+ */
251
+ declare function useBreakpoint(): string;
252
+ /**
253
+ * Returns a record of boolean flags for each breakpoint — `true` when the
254
+ * window width satisfies that breakpoint's min-width threshold.
255
+ *
256
+ * ```ts
257
+ * const { sm, md, lg } = useResponsive();
258
+ * const padding = lg ? 32 : sm ? 16 : 8;
259
+ * ```
260
+ */
261
+ declare function useResponsive(): Record<string, boolean>;
262
+
263
+ interface InteractiveWrapperProps {
264
+ /** The real component to render (View, TouchableOpacity, div, a third-party button…) */
265
+ Component: React__default.ComponentType<any> | string;
266
+ /** Pre-resolved style buckets from resolve() */
267
+ resolvedStyle: ResolvedStyle;
268
+ /** On web: original class string so CSS pseudo-rules still fire */
269
+ className?: string;
270
+ /** Extra style prop passed by the user */
271
+ style?: StyleValue | StyleValue[];
272
+ children?: React__default.ReactNode;
273
+ onPressIn?: (...args: any[]) => void;
274
+ onPressOut?: (...args: any[]) => void;
275
+ onPointerDown?: (...args: any[]) => void;
276
+ onPointerUp?: (...args: any[]) => void;
277
+ onPointerLeave?: (...args: any[]) => void;
278
+ onPointerCancel?: (...args: any[]) => void;
279
+ onMouseEnter?: (...args: any[]) => void;
280
+ onMouseLeave?: (...args: any[]) => void;
281
+ onFocus?: (...args: any[]) => void;
282
+ onBlur?: (...args: any[]) => void;
283
+ }
284
+ /**
285
+ * Thin wrapper rendered automatically by the JSX runtime whenever a className/kb
286
+ * string contains interactive modifiers (hover:, pressed:, focus:, active:, …).
287
+ *
288
+ * Manages interaction state locally and flattens the correct style bucket on
289
+ * every render. The wrapped component sees a plain `style` prop — it never
290
+ * knows it was wrapped.
291
+ *
292
+ * Refs are forwarded so the host component's imperative API still works.
293
+ */
294
+ declare const InteractiveWrapper: React__default.ForwardRefExoticComponent<InteractiveWrapperProps & React__default.RefAttributes<unknown>>;
295
+
296
+ /**
297
+ * Resolve a utility class string outside of a React component.
298
+ *
299
+ * On **native** — returns a StyleValue (style object) for the given mode.
300
+ * On **web** — injects CSS and returns the original class string (use as className).
301
+ *
302
+ * ```ts
303
+ * // Inside a component use useStyles() instead.
304
+ * // kb() is useful for StyleSheet.create() calls and static values.
305
+ *
306
+ * const styles = StyleSheet.create({
307
+ * container: kb('flex-1 bg-white p-4') as any,
308
+ * });
309
+ *
310
+ * // Web: use as className
311
+ * <div className={kb('bg-white dark:bg-gray-900 p-4') as string} />
312
+ * ```
313
+ *
314
+ * @param classString Space-separated utility classes
315
+ * @param isDark Whether dark mode is active (default: false)
316
+ */
317
+ declare function kb(classString: string, isDark?: boolean): StyleValue | string;
318
+ /**
319
+ * Conditionally join class names. Falsy values are ignored.
320
+ *
321
+ * ```ts
322
+ * cx('bg-white p-4', isActive && 'border-2 border-blue-500', undefined)
323
+ * // → 'bg-white p-4 border-2 border-blue-500'
324
+ * ```
325
+ */
326
+ declare function cx(...classes: Array<string | false | null | undefined>): string;
327
+
328
+ 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 };