/** * Unified modifier registry — single source of truth for ALL modifier behavior. * * Adding a new modifier requires editing ONLY this file: * 1. Add an entry to BUILTIN_MODIFIERS with its CSS and JS behavior. * 2. Done — parser, resolver, CSS generator, and JSX runtime all derive * their behavior from this data automatically. * * Plugin authors can register custom modifiers via registerModifier(). */ interface ModifierDef { /** * Cascade priority for CSS rule ORDER — NOT specificity. Two rules that * differ only by modifier (e.g. `.hover\:bg-blue-6:hover` and * `.focus\:bg-red-6:focus`) have equal CSS specificity, so when both * conditions are true at once (hovering AND focused), the winner is * whichever rule appears LATER in the stylesheet — CSS's normal same- * specificity tiebreak. Without a fixed priority, "later" would depend on * encounter order (whichever class the app happens to render/scan first), * making the winner effectively random and inconsistent across reloads/ * builds. `order` fixes that: rules are emitted/injected sorted by this * value (ascending — higher wins ties), regardless of source order, so * e.g. `disabled:` always beats `hover:` on the same element no matter * which one was written first in the className or rendered first in the * app. Omit for the default (0). See getModifierOrder() below. */ order?: number; /** CSS pseudo-class or pseudo-element appended to the selector (e.g. ':hover', '::before') */ pseudo?: string; /** Ancestor selector prefix INCLUDING trailing space (e.g. '.group:hover ', '.peer:focus ~ ') */ ancestorSelector?: string; /** Directionality attribute selector prefix INCLUDING trailing space (e.g. '[dir="rtl"] ') */ dirSelector?: string; /** @media query body WITHOUT the '@media ' prefix (e.g. 'print', '(orientation: landscape)') */ mediaQuery?: string; /** Dark/light mode scheme — triggers the configured darkMode strategy in CSS output */ darkScheme?: 'dark' | 'light'; /** True for responsive modifiers — wraps in @media (min-width: theme.screens[name]) */ isResponsive?: boolean; /** * Forces !important on all declarations in the generated CSS rule. * Applied automatically for structural / ancestor / media modifiers that must * win over base inline styles. */ forcesImportant?: boolean; /** * How the JSX runtime routes this modifier: * 'interactive' — managed by InteractiveWrapper (hover, focus, pressed, …) * 'mode' — managed by DarkWrapper (dark, light, not-dark, …) * 'responsive' — managed by DarkWrapper (sm, md, lg, xl, 2xl) * 'css-only' — CSS injection only; matchModifier always returns false */ jsBehavior: 'interactive' | 'mode' | 'responsive' | 'css-only'; /** * Evaluates whether this modifier's condition is met at runtime. * Omit for 'css-only' modifiers — they never apply as inline styles. */ jsMatch?: (isDark: boolean, state: Record, breakpoints: Set) => boolean; } declare function registerModifier(name: string, def: ModifierDef): void; declare function clearPluginModifiers(): void; declare function getModifier(name: string): ModifierDef | undefined; declare function isKnownModifier(name: string): boolean; /** All known modifier names (built-in + plugin). */ declare function getAllModifierNames(): Set; /** Modifier names routed to InteractiveWrapper. */ declare function getInteractiveModifiers(): Set; /** Modifier names routed to DarkWrapper for mode switching. */ declare function getModeModifiers(): Set; /** Modifier names routed to DarkWrapper for responsive handling. */ declare function getResponsiveModifiers(): Set; /** * Evaluates whether a modifier matches the current runtime state. * CSS-only modifiers always return false — they have no JS representation. */ declare function matchModifier(name: string, isDark: boolean, state: Record, breakpoints: Set): boolean; type Platform = 'web' | 'native'; type ThemeMode = 'light' | 'dark' | 'system'; interface StyleValue { [key: string]: string | number | undefined | null | StyleValue | StyleValue[]; } interface ParsedClass { /** The original class string, e.g. "dark:hover:bg-[#fff]" */ original: string; /** Up to 3 modifier prefixes, e.g. ['dark', 'hover'] */ modifiers: string[]; /** Whether a leading `-` was present for negative values */ negative: boolean; /** Whether a leading `!` was present — applies !important to all CSS declarations */ important: boolean; /** The utility name, e.g. 'bg', 'p', 'text' */ utility: string; /** The resolved value, e.g. 'white', '#fff', '4' */ value: string; /** Whether the value was specified with bracket notation [value] */ isArbitrary: boolean; } interface ResolvedStyle { /** Base styles applied unconditionally */ base?: StyleValue; /** Styles keyed by modifier or modifier combo, e.g. 'dark', 'hover', 'dark:hover' */ [modifierKey: string]: StyleValue | undefined; } /** * A color value is either a plain string (hex/rgb/alias-to-another-color-name) * or a mode-aware pair — resolved to `light` or `dark` per the active theme * mode wherever it's actually used (className resolution, useColors()). */ type ColorValue = string | { light: string; dark: string; }; type ColorShades = Record; type ThemeColors = Record; type ThemeSpacing = Record; interface ThemeConfig { colors: ThemeColors; spacing: ThemeSpacing; fontSize: Record; fontFamily: Record; fontWeight: Record; borderRadius: Record; borderWidth: Record; opacity: Record; lineHeight: Record; letterSpacing: Record; zIndex: Record; flex: Record; shadow: Record; screens: Record; /** * Custom @keyframes, web only. Each key is a keyframe name, its value maps * percentage/from/to selectors to a plain CSS declaration object (camelCase * properties, same shape as an inline style object): * keyframes: { wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } } } * Referenced from `animation` below, or directly via animate-[wiggle_1s_ease-in-out]. */ keyframes: Record>; /** * Named animation shorthands built on `keyframes` above, referenced via * animate-{name} (e.g. animate-wiggle): * animation: { wiggle: 'wiggle 1s ease-in-out infinite' } * The first word must match a `keyframes` key so its @keyframes rule can be * injected alongside the animation — a name with no matching keyframes entry * still sets the `animation` CSS property, it just won't animate anything. */ animation: Record; [key: string]: unknown; } /** * 'class' — toggles .dark class on * 'media' — uses prefers-color-scheme media query * 'attribute' — uses data-theme="dark" attribute on */ type DarkMode = 'attribute' | 'class' | 'media'; interface PluginAPI { addUtility(name: string, styles: StyleValue): void; /** * Register a custom variant. * * Pass a CSS selector string for simple cases — it is automatically * converted into a ModifierDef that generates correct CSS rules: * addVariant('hocus', ':hover, :focus') // pseudo * addVariant('supports-grid', '@media (display: grid)') // media * addVariant('dark-green', '.dark-green') // ancestor selector * * Pass a full ModifierDef object for advanced control (e.g. JS-trackable * interactive variants with custom jsMatch logic). */ addVariant(name: string, selectorOrDef: string | ModifierDef): void; theme(path: string, defaultValue?: unknown): unknown; e(className: string): string; } interface FrameworkConfig { darkMode?: DarkMode; theme?: Partial; /** Additive theme extension — accepts either `extend.theme.X` or `extend.X` directly. */ extend?: { theme?: Partial; } & Partial; plugins?: Array<(api: PluginAPI) => void>; content?: string[]; } interface ResolvedConfig { darkMode: DarkMode; theme: ThemeConfig; plugins: Array<(api: PluginAPI) => void>; } /** * True for a mode-aware color pair (`{ light, dark }`), as opposed to a plain * hex/rgb/alias string. Shared by config.ts (alias-chain resolution), * resolvers/color.ts (direct lookups), and modeAwareColors.ts (className * expansion) so all three agree on exactly one definition. Real shade keys * are always numeric strings ('1'–'12'), so this can never collide with one. */ declare function isModeAwareColor(v: unknown): v is { light: string; dark: string; }; /** * Expand every class referencing a mode-aware color (a kbach.config.js color * value shaped `{ light, dark }`) into an explicit base + dark: pair BEFORE * normal parsing — e.g. `bg-surface` becomes `bg-[#ffffff] dark:bg-[#111827]`. * `bg-surface/50` becomes `bg-[#ffffff]/50 dark:bg-[#111827]/50` (the arbitrary- * color-plus-opacity composition already resolveColor() already supports). * `hover:bg-surface` becomes `hover:bg-[#ffffff] dark:hover:bg-[#111827]` — * every other modifier already present on the token carries through onto * both halves of the pair unchanged. * * A token that ALREADY carries an explicit dark:/light:/not-dark:/not-light: * modifier (someone writes `dark:hover:bg-primary` on a `primary` that's * already mode-aware, usually out of habit from before it was) isn't split * into a pair — that would be redundant on top of an already-explicit * choice. Instead the matching side is substituted in place and every * modifier, including the dark:/light: itself, is left exactly as written, * so `dark:hover:bg-primary` still only applies in dark mode, using the * dark side (not the resolveColor()-level fallback's light side, which is * only ever reached by a mode-aware pair that skipped this expansion * entirely — not a path normal className resolution takes). * * Runs once, upfront, purely as a string rewrite — everything downstream * (CSS generation, native bucketing/flatten(), the existing dark: reactivity * machinery: DarkWrapper, bucketMods()) then handles the result exactly like * a hand-written dark: pair, with zero further changes needed anywhere else. * That also means resolve()'s cache (keyed on the ORIGINAL, unexpanded string * — see resolver.ts) stays correct without any changes to its cache key. */ declare function expandModeAwareColorClasses(classString: string, colors: ThemeColors): string; /** * Generates the KbachCustomColors/KbachCustomSpacing module-augmentation * source for every color/spacing key `extend.theme` added beyond the built-in * defaults. Called by both the Vite plugin and the Babel plugin (@kbach/react/babel-plugin) — * kept here, shared, so "which colors count as custom" and "flat string vs. * ColorScale" can't drift between the two. * * Returns '' (nothing to write) when the theme adds no custom colors or * spacing keys at all, so callers can skip writing/deleting a file for the * common case of a project still on the stock theme. */ declare function generateKbachTypesDts(theme: ThemeConfig): string; declare const defaultColors: { transparent: string; current: string; black: string; white: string; slate: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; gray: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; zinc: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; neutral: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; stone: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; red: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; orange: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; amber: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; yellow: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; lime: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; green: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; emerald: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; teal: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; cyan: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; sky: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; blue: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; indigo: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; violet: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; purple: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; fuchsia: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; pink: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; rose: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; }; declare const defaultTheme: { colors: { transparent: string; current: string; black: string; white: string; slate: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; gray: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; zinc: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; neutral: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; stone: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; red: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; orange: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; amber: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; yellow: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; lime: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; green: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; emerald: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; teal: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; cyan: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; sky: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; blue: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; indigo: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; violet: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; purple: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; fuchsia: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; pink: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; rose: { 1: string; 2: string; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; 11: string; 12: string; }; }; spacing: { px: number; 0: number; '0.5': number; 1: number; '1.5': number; 2: number; '2.5': number; 3: number; '3.5': number; 4: number; 5: number; 6: number; 7: number; 8: number; 9: number; 10: number; 11: number; 12: number; 14: number; 16: number; 20: number; 24: number; 28: number; 32: number; 36: number; 40: number; 44: number; 48: number; 52: number; 56: number; 60: number; 64: number; 72: number; 80: number; 96: number; auto: string; full: string; '1/2': string; '1/3': string; '2/3': string; '1/4': string; '3/4': string; screen: string; min: string; max: string; fit: string; }; fontSize: { xs: number; sm: number; base: number; lg: number; xl: number; '2xl': number; '3xl': number; '4xl': number; '5xl': number; '6xl': number; '7xl': number; '8xl': number; '9xl': number; }; fontFamily: { sans: string; mono: string; serif: string; }; fontWeight: { thin: string; extralight: string; light: string; normal: string; medium: string; semibold: string; bold: string; extrabold: string; black: string; }; borderRadius: { none: number; sm: number; DEFAULT: number; md: number; lg: number; xl: number; '2xl': number; '3xl': number; full: number; }; borderWidth: { DEFAULT: number; 0: number; 2: number; 4: number; 8: number; }; opacity: { 0: number; 5: number; 10: number; 15: number; 20: number; 25: number; 30: number; 40: number; 50: number; 60: number; 70: number; 75: number; 80: number; 90: number; 95: number; 100: number; }; lineHeight: { none: number; tight: number; snug: number; normal: number; relaxed: number; loose: number; 3: string; 4: string; 5: string; 6: string; 7: string; 8: string; 9: string; 10: string; }; letterSpacing: { tighter: number; tight: number; normal: number; wide: number; wider: number; widest: number; }; zIndex: { auto: string; 0: number; 10: number; 20: number; 30: number; 40: number; 50: number; }; flex: { 1: number; auto: string; initial: string; none: string; }; shadow: { sm: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; DEFAULT: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; md: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; lg: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; xl: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; '2xl': { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; none: { shadowColor: string; shadowOffset: { width: number; height: number; }; shadowOpacity: number; shadowRadius: number; elevation: number; }; }; screens: { sm: number; md: number; lg: number; xl: number; '2xl': number; }; keyframes: {}; animation: {}; }; type DefaultColorName = keyof typeof defaultColors; type DefaultSpacingKey = `${keyof typeof defaultTheme.spacing}`; declare const RESET_STYLE_ID = "kbach-reset"; declare const BASE_RESET: string; /** * Bounded LRU cache using Map's insertion-order iteration. * Map.keys().next() gives the oldest entry for O(1) eviction. * Safe upper bound prevents unbounded growth (no memory leak). */ declare class LRUCache { private readonly capacity; private readonly cache; private readonly onEvict?; constructor(capacity?: number, onEvict?: (key: K, value: V) => void); get(key: K): V | undefined; set(key: K, value: V): this; has(key: K): boolean; delete(key: K): boolean; clear(): void; get size(): number; } /** * Detected at module load time — safe to call repeatedly without perf cost. */ declare const isWeb: boolean; declare const isNative: boolean; declare function setResolveTarget(target: 'web' | 'native' | null): void; declare function getEffectiveIsWeb(): boolean; /** * Convert an arbitrary-bracket value to a native-friendly number when possible. * e.g. '10px' → 10, '1rem' → 16, '50%' → '50%' (keep as string), '#fff' → '#fff' */ declare function toNativeValue(raw: string): string | number; /** * Escape a class name for use inside a CSS selector. * e.g. 'bg-[#fff]' → 'bg-\\[\\#fff\\]' */ declare function escapeCSSSelector(cls: string): string; /** * Styled console.warn for browser/runtime code. Uses the `%c` CSS-styling * console format (supported by Chrome, Firefox, Safari, and Edge DevTools) * so Kbach's own warnings are visually distinct from the surrounding noise — * a colored "[kbach]" tag followed by a short, plain message. * * Kept deliberately terse at call sites: one sentence, no walls of text. */ declare function kbachWarn(message: string): void; declare function parseClass(className: string): ParsedClass | null; /** * Split a class string into tokens. Whitespace at bracket depth > 0 is STRIPPED * (not just skipped) so that arbitrary values like rgb(41, 172, 15) become valid * CSS class name tokens: bg-[rgb(41,172,15)]. */ declare function splitClassTokens(classString: string): string[]; /** * Normalize a full class string so it is safe to use as an HTML className value. * Strips spaces inside brackets: "bg-[rgb(41, 172, 15)] p-4" → "bg-[rgb(41,172,15)] p-4" */ declare function normalizeClassString(classString: string): string; /** Parse a space-separated class string into individual ParsedClass objects. */ declare function parseClasses(classString: string): ParsedClass[]; declare function resolveColor(value: string, colors: ThemeColors, isArbitrary: boolean): string | null; /** * Parse a hex color string (#rgb, #rgba, #rrggbb, #rrggbbaa) into an [r, g, b] * tuple. Any alpha nibble/byte is ignored — callers apply their own opacity. * Shared with useColors.ts's applyOpacity() so hex parsing lives in one place. */ declare function parseHexRgb(hex: string): [number, number, number] | null; declare function resolveSpacing(value: string, negative: boolean, spacing: ThemeConfig['spacing'], isArbitrary: boolean): string | number | null; declare function resolveSizing(value: string, spacing: ThemeConfig['spacing'], isArbitrary: boolean): string | number | null; declare function resolveUtility(parsed: ParsedClass, theme: ThemeConfig): StyleValue | null; /** * Returns true if the utility name is known to the framework (built-in or plugin). * Used for dev-mode warnings — resolveUtility returning null could mean either * "intentionally null on this platform" or "completely unknown utility name". * This check covers the second case. */ declare function isKnownUtility(utility: string): boolean; /** * Sorted (longest-first) unique list of all built-in + plugin utility prefixes. * Used by parser.ts for greedy prefix matching — replaces the hard-coded * UTILITY_PREFIXES array that had to be kept in sync manually. */ declare function getBuiltinUtilityPrefixes(): readonly string[]; /** * Set of all built-in + plugin standalone utility names. * Used by parser.ts to recognise no-value tokens — replaces the hard-coded * STANDALONE_UTILITIES set that had to be kept in sync manually. */ declare function getBuiltinStandaloneNames(): ReadonlySet; declare function setDefaultFontFamily(font: string | undefined): void; declare function getDefaultFontFamily(): string | undefined; declare function disableRuntimeCSS(): void; declare function isRuntimeCSSDisabled(): boolean; declare function generateClassCSS(classString: string, theme: ThemeConfig, darkMode?: 'attribute' | 'class' | 'media', screens?: Record): string; /** * Resolve a class string to a ResolvedStyle object. * * Results are cached per (theme, classString, darkMode) — repeated calls with * the same arguments are O(1). Different theme objects each get their own * cache so concurrent ThemeProviders with different configs are always correct. */ declare function resolve(classString: string, theme: ThemeConfig, darkMode?: 'attribute' | 'class' | 'media'): ResolvedStyle; /** * Flatten a ResolvedStyle into a single StyleValue for the current runtime state. * Used by useStyles() and styled(). * * The sort step is memoized per resolved object reference (#6) — when resolve() * returns a cached object, getSortedEntries() is O(1) on all subsequent calls. */ declare function flatten(resolved: ResolvedStyle, isDark: boolean, state?: { hover?: boolean; focus?: boolean; pressed?: boolean; active?: boolean; disabled?: boolean; checked?: boolean; visited?: boolean; placeholder?: boolean; }, breakpoints?: Set): StyleValue; /** * Clear CSS injection state so stale rules are re-injected when the theme changes. * * The per-theme style cache (WeakMap) does not need to be cleared manually: * updateConfig() creates a new theme object, making the old cache entry * automatically unreachable for GC. */ declare function clearCache(): void; /** * Load, merge, and cache the resolved config. * Call resetConfig() to force a reload (e.g. in tests or after live update). */ declare function getConfig(): ResolvedConfig; declare function resetConfig(): void; declare function buildConfig(userConfig: FrameworkConfig): ResolvedConfig; type ConfigListener = (config: ResolvedConfig) => void; declare function onConfigChange(listener: ConfigListener): () => void; declare function updateConfig(userConfig: FrameworkConfig): void; /** * Like updateConfig, but only calls it when the config object reference has * actually changed. Used by the Babel-injected IIFE so that: * - multiple files with kbach classes don't re-run the update on every load * - Fast Refresh DOES re-run when kbach.config.js changes (new module → new object) */ declare function initConfig(userConfig: FrameworkConfig): void; /** * Module-level dark-mode singleton. * * This intentionally lives outside React so the custom JSX runtime can read it * synchronously during render without needing context. ThemeProvider writes to it; * DarkWrapper / InteractiveWrapper subscribe via useSyncExternalStore. * * Used to be backed by globalThis instead of a plain module-level variable: * tsup used to bundle core/ separately into each of dist/index.js and * dist/jsx-runtime.js (esbuild doesn't support code-splitting CJS output), * so Metro loading each by path got independent copies of this module with * independent top-level state. core/ is now built as its own dist/core/ * entry and required externally by both (see packages/react/tsup.config.ts), * so there's only ever one real instance of this module to begin with — a * plain module-level object is enough. */ /** * Silently update isDark without notifying subscribers. * Safe to call during React's render phase — no state side-effects. * ThemeProvider calls this before returning JSX so the JSX runtime and * children that call getGlobalDarkMode() during the same render pass * already see the correct value. */ declare function syncGlobalDarkMode(isDark: boolean): void; /** * Update isDark and notify all subscribers. * Called by ThemeProvider in a layout effect (after commit) so DarkWrapper / * InteractiveWrapper consumers re-render with the updated dark-mode value. * Notifications are skipped when the value hasn't changed since the last * broadcast to avoid spurious re-renders — see the notifiedIsDark comment * above for why that comparison can't use `isDark` itself. */ declare function setGlobalDarkMode(isDark: boolean): void; /** Read current dark-mode state synchronously (safe in render, no hook needed). */ declare function getGlobalDarkMode(): boolean; /** * Subscribe to dark-mode changes. * @returns Cleanup function — call it to unsubscribe (no leak). */ declare function subscribeGlobalDarkMode(callback: () => void): () => void; /** * Global responsive width store. * * Used to be backed by globalThis so all CJS bundle splits (index.js, * jsx-runtime.js, jsx-dev-runtime.js) shared one instance — tsup used to * bundle core/ separately into each of them (esbuild doesn't support * code-splitting CJS output). core/ is now built as its own dist/core/ * entry and required externally by all three (see * packages/react/tsup.config.ts), so there's only ever one real instance of * this module to begin with — a plain module-level object is enough. */ type WidthListener = () => void; /** Synchronous write for use in the render phase. */ declare function syncGlobalWidth(width: number): void; /** Update the breakpoint-name → min-width map from the resolved theme config. */ declare function syncGlobalScreens(screens: Record): void; declare function getGlobalScreens(): Record; /** * Async write — fires listeners so subscribers re-render. Skip check uses * notifiedWidth, not width — see the ResponsiveStore.notifiedWidth comment. */ declare function setGlobalWidth(width: number): void; declare function getGlobalWidth(): number; declare function subscribeGlobalWidth(listener: WidthListener): () => void; /** * Returns the Set of breakpoint names that are currently active * (i.e. width >= their min-width threshold). * * `screens` defaults to the global store's screens map (the common case — * DarkWrapper/InteractiveWrapper have no per-tree config available). Pass an * explicit map to check against a LOCAL config instead — e.g. useBreakpoint()/ * useResponsive() pass the nearest 's own `config.theme.screens` * so they stay correct per-provider rather than silently reading whichever * config the global store happens to hold (see ThemeProvider's per-tree * config-override limitation). */ declare function getActiveBreakpoints(width?: number, screens?: Record): Set; export { BASE_RESET, type ColorShades, type ColorValue, type DarkMode, type DefaultColorName, type DefaultSpacingKey, type FrameworkConfig, LRUCache, type ParsedClass, type Platform, type PluginAPI, RESET_STYLE_ID, type ResolvedConfig, type ResolvedStyle, type StyleValue, type ThemeColors, type ThemeConfig, type ThemeMode, type ThemeSpacing, buildConfig, clearCache, clearPluginModifiers, defaultColors, defaultTheme, disableRuntimeCSS, escapeCSSSelector, expandModeAwareColorClasses, flatten, generateClassCSS, generateKbachTypesDts, getActiveBreakpoints, getAllModifierNames, getBuiltinStandaloneNames, getBuiltinUtilityPrefixes, getConfig, getDefaultFontFamily, getEffectiveIsWeb, getGlobalDarkMode, getGlobalScreens, getGlobalWidth, getInteractiveModifiers, getModeModifiers, getModifier, getResponsiveModifiers, initConfig, isKnownModifier, isKnownUtility, isModeAwareColor, isNative, isRuntimeCSSDisabled, isWeb, kbachWarn, matchModifier, normalizeClassString, onConfigChange, parseClass, parseClasses, parseHexRgb, registerModifier, resetConfig, resolve, resolveColor, resolveSizing, resolveSpacing, resolveUtility, setDefaultFontFamily, setGlobalDarkMode, setGlobalWidth, setResolveTarget, splitClassTokens, subscribeGlobalDarkMode, subscribeGlobalWidth, syncGlobalDarkMode, syncGlobalScreens, syncGlobalWidth, toNativeValue, updateConfig };