@kbach/react 0.5.38 → 0.5.40

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,6 +1,6 @@
1
1
  # @kbach/react
2
2
 
3
- Tailwind-like utility classes for **React (web)**. Classes are converted to inline styles at render time through a custom JSX runtime — no stylesheet required. Responsive, dark mode, hover, and pseudo-element rules are injected as real CSS so they work with pure CSS cascading.
3
+ Tailwind-like utility classes for **React (web)**. Classes are resolved to inline styles at render time through a custom JSX runtime — no separate stylesheet step required. Responsive, dark mode, hover, and pseudo-element rules are injected as real CSS so they work with the full CSS cascade.
4
4
 
5
5
  ```jsx
6
6
  /** @jsxImportSource @kbach/react */
@@ -9,7 +9,7 @@ Tailwind-like utility classes for **React (web)**. Classes are converted to inli
9
9
  <div className="bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-lg px-6 py-3" />
10
10
  <div className="bg-[#6366f1] p-[14px] rounded-[20px]" />
11
11
  <div className="group">
12
- <span className="group-hover:text-blue-6 transition" />
12
+ <span className="opacity-0 group-hover:opacity-100 transition" />
13
13
  </div>
14
14
  ```
15
15
 
@@ -36,7 +36,7 @@ npm install @kbach/react
36
36
 
37
37
  **Vite**
38
38
 
39
- ```js
39
+ ```ts
40
40
  // vite.config.ts
41
41
  import { defineConfig } from 'vite';
42
42
  import react from '@vitejs/plugin-react';
@@ -45,12 +45,12 @@ import { kbach } from '@kbach/react/vite';
45
45
  export default defineConfig({
46
46
  plugins: [
47
47
  react({ jsxImportSource: '@kbach/react' }),
48
- kbach(), // generates app.css on every HMR event
48
+ kbach(), // generates CSS and writes it to your main CSS file
49
49
  ],
50
50
  });
51
51
  ```
52
52
 
53
- The `kbach()` Vite plugin scans your source files and writes generated CSS between `/* kbach:start */` / `/* kbach:end */` markers in your main CSS file (`src/index.css`, `app/app.css`, etc.). It rescans fresh on every HMR event so removed classes are immediately evicted.
53
+ The `kbach()` Vite plugin scans your source files for class strings (including ternary expressions, template literals, and `clsx`/`cn` call patterns) and writes the generated CSS between `/* kbach:start */` / `/* kbach:end */` markers in your main CSS file (`src/index.css`, `app/app.css`, etc.). On HMR only the changed file is rescanned — unchanged class tokens reuse their cached CSS.
54
54
 
55
55
  **Babel**
56
56
 
@@ -85,20 +85,20 @@ export default function Root() {
85
85
 
86
86
  ## API
87
87
 
88
- ### className prop
88
+ ### className / kb prop
89
89
 
90
- Works on any HTML element or React component once the JSX runtime is configured.
90
+ Works on any HTML element or React component once the JSX runtime is configured. Both `className` and `kb` are equivalent — `kb` is shorter and avoids conflicts with library components that forward `className` to their own DOM elements.
91
91
 
92
92
  ```jsx
93
93
  <div className="bg-white dark:bg-gray-10 p-4 rounded-xl" />
94
- <p className="text-gray-10 dark:text-white text-lg font-bold" />
94
+ <p kb="text-gray-10 dark:text-white text-lg font-bold" />
95
95
  <button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-9 rounded-lg px-4 py-2" />
96
96
  <input className="caret-blue-6 focus:ring-2" />
97
97
  ```
98
98
 
99
99
  ### styled(Component, classes)
100
100
 
101
- Pre-style any component. Returns a new component that merges the base classes with any `kb` prop passed at use time. On web, the class string is forwarded as `className` so CSS rules (group-hover:, before:, print:, etc.) match the element.
101
+ Pre-style any component. Returns a new component that merges the base classes with any `kb` prop passed at use time. On web, the class string is forwarded as `className` so CSS rules (`group-hover:`, `before:`, `print:`, etc.) match the element.
102
102
 
103
103
  ```jsx
104
104
  import { styled } from '@kbach/react';
@@ -116,22 +116,36 @@ const Button = styled(
116
116
  </Card>
117
117
  ```
118
118
 
119
- ### useStyles(classes)
119
+ ### useStyles(classes, state?)
120
120
 
121
- Resolve classes to a style object inside a component.
121
+ Resolve classes to a style object inside a component. Accepts a string, an array of strings (merged left-to-right), and an optional interaction state object.
122
122
 
123
123
  ```jsx
124
124
  import { useStyles } from '@kbach/react';
125
125
 
126
- function Badge() {
127
- const style = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');
128
- return <span style={style}>New</span>;
129
- }
126
+ // Basic
127
+ const style = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');
128
+
129
+ // Multiple class strings merged
130
+ const style = useStyles(['bg-white p-4', 'dark:bg-gray-9 rounded-xl']);
131
+
132
+ // With interaction state
133
+ const [pressed, setPressed] = useState(false);
134
+ const style = useStyles('bg-blue-5 pressed:bg-blue-7 rounded-lg', { pressed });
135
+ ```
136
+
137
+ ### useResolvedStyle(classes)
138
+
139
+ Returns the raw bucket map (`{ base, dark, hover, … }`) without flattening. Useful when you need to pass different buckets to different elements, or animate between states with `Animated`.
140
+
141
+ ```js
142
+ const resolved = useResolvedStyle('bg-white dark:bg-gray-9 p-4');
143
+ // { base: { backgroundColor: '#fff', padding: 16 }, dark: { backgroundColor: '#1f2937' } }
130
144
  ```
131
145
 
132
146
  ### kb(classes)
133
147
 
134
- Resolve classes outside a component (static contexts, etc.).
148
+ Resolve classes outside a component (static contexts, `StyleSheet.create()`, etc.).
135
149
 
136
150
  ```js
137
151
  import { kb } from '@kbach/react';
@@ -172,25 +186,33 @@ const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
172
186
 
173
187
  ### useIsDark()
174
188
 
175
- Shorthand hook when you only need the dark-mode boolean.
176
-
177
189
  ```js
178
190
  import { useIsDark } from '@kbach/react';
179
-
180
191
  const isDark = useIsDark();
181
192
  ```
182
193
 
194
+ ### useBreakpoint() / useResponsive()
195
+
196
+ ```js
197
+ import { useBreakpoint, useResponsive } from '@kbach/react';
198
+
199
+ const bp = useBreakpoint(); // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
200
+
201
+ const { sm, md, lg } = useResponsive();
202
+ const padding = lg ? 32 : sm ? 16 : 8;
203
+ ```
204
+
183
205
  ### useColors()
184
206
 
185
- Returns the active theme's color palette as a smart proxy. Supports shade access, the `/opacity` modifier, flat colors with opacity, and arbitrary CSS color manipulation.
207
+ Returns the active theme's color palette as a smart proxy. Supports shade access, `/opacity` modifier, and arbitrary CSS color manipulation. Handles 3, 4, 6, and 8-digit hex values.
186
208
 
187
209
  ```js
188
210
  import { useColors } from '@kbach/react';
189
211
 
190
212
  const colors = useColors();
191
213
 
192
- colors.blue[6] // '#6c87b6'
193
- colors.blue['6/50'] // 'rgba(108,135,182,0.5)'
214
+ colors.blue[6] // '#3b82f6'
215
+ colors.blue['6/50'] // 'rgba(59,130,246,0.5)'
194
216
  colors.white // '#ffffff'
195
217
  colors['white/20'] // 'rgba(255,255,255,0.2)'
196
218
  colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
@@ -209,54 +231,73 @@ Drop-in toggle component.
209
231
 
210
232
  ## Modifiers
211
233
 
212
- Up to 3 modifiers can be chained in any order.
234
+ Modifiers are chainable with no hard cap — as many as needed in any order:
235
+
236
+ ```jsx
237
+ <div className="dark:sm:hover:focus:p-4" />
238
+ <div className="dark:group-hover:not-disabled:text-blue-6" />
239
+ ```
213
240
 
214
- ### Theme & interaction
241
+ ### Theme & mode
215
242
 
216
243
  | Modifier | Trigger |
217
244
  |---|---|
218
245
  | `dark:` | Dark mode active |
219
- | `light:` | Light mode active |
220
- | `hover:` | Mouse hover |
221
- | `pressed:` | Click / touch |
222
- | `focus:` | Focused |
223
- | `active:` | Active state |
224
- | `disabled:` | Disabled |
225
- | `checked:` | Checked (checkbox/radio) |
226
- | `visited:` | Visited link |
227
- | `focus-within:` | Focus inside element |
228
- | `focus-visible:` | Keyboard focus ring |
229
-
230
- Negated forms: `not-hover:`, `not-focus:`, `not-active:`, `not-pressed:`, `not-disabled:`, `not-checked:`, `not-visited:`
231
-
232
- ### Structural
233
-
234
- | Modifier | Matches |
246
+ | `not-dark:` / `light:` | Light mode active |
247
+ | `not-light:` | Dark mode active (alias) |
248
+
249
+ ### Interactive — JS state + CSS pseudo
250
+
251
+ | Modifier | Trigger |
252
+ |---|---|
253
+ | `hover:` / `not-hover:` | Mouse hover |
254
+ | `focus:` / `not-focus:` | Focused |
255
+ | `pressed:` / `not-pressed:` | Click / touch down |
256
+ | `active:` / `not-active:` | Active state |
257
+ | `disabled:` / `not-disabled:` | Disabled |
258
+ | `checked:` / `not-checked:` | Checkbox / radio checked |
259
+ | `visited:` / `not-visited:` | Visited link |
260
+ | `placeholder:` | Placeholder text |
261
+
262
+ ### CSS-only pseudo-classes (structural)
263
+
264
+ | Modifier | CSS |
235
265
  |---|---|
236
266
  | `first:` | `:first-child` |
237
267
  | `last:` | `:last-child` |
238
268
  | `odd:` | `:nth-child(odd)` |
239
269
  | `even:` | `:nth-child(even)` |
240
270
  | `only:` | `:only-child` |
271
+ | `focus-within:` | `:focus-within` |
272
+ | `focus-visible:` | `:focus-visible` |
241
273
 
242
- ### Responsive
274
+ ### Pseudo-elements
243
275
 
244
- | Modifier | Min-width |
245
- |---|---|
246
- | `sm:` | 576 px |
247
- | `md:` | 768 px |
248
- | `lg:` | 1024 px |
249
- | `xl:` | 1280 px |
250
- | `2xl:` | 1536 px |
276
+ ```jsx
277
+ <div className="before:content-['*'] before:text-red-6 relative" />
278
+ <p className="first-letter:text-4xl first-letter:font-bold" />
279
+ <input className="placeholder:text-gray-5" />
280
+ <p className="selection:bg-blue-3" />
281
+ ```
251
282
 
252
- ### Group / peer
283
+ | Modifier | CSS |
284
+ |---|---|
285
+ | `before:` | `::before` |
286
+ | `after:` | `::after` |
287
+ | `selection:` | `::selection` |
288
+ | `first-letter:` | `::first-letter` |
289
+ | `first-line:` | `::first-line` |
290
+ | `marker:` | `::marker` |
291
+ | `placeholder:` | `::placeholder` |
253
292
 
254
- Mark an ancestor with `group` or `peer`, then style descendants with the modifier:
293
+ ### Group / peer ancestor selectors
255
294
 
256
295
  ```jsx
257
296
  <div className="group">
258
297
  <span className="opacity-0 group-hover:opacity-100 transition" />
259
298
  </div>
299
+ <input className="peer" />
300
+ <p className="peer-focus:text-blue-6" />
260
301
  ```
261
302
 
262
303
  | Modifier | Fires when |
@@ -266,37 +307,43 @@ Mark an ancestor with `group` or `peer`, then style descendants with the modifie
266
307
  | `peer-hover:` | Previous sibling `.peer` is hovered |
267
308
  | `peer-focus:` | Previous sibling `.peer` is focused |
268
309
 
269
- ### Pseudo-elements
310
+ ### Responsive
311
+
312
+ | Modifier | Min-width |
313
+ |---|---|
314
+ | `sm:` | 576 px |
315
+ | `md:` | 768 px |
316
+ | `lg:` | 1024 px |
317
+ | `xl:` | 1280 px |
318
+ | `2xl:` | 1536 px |
270
319
 
271
- Inject content or style structural parts of an element with real CSS:
320
+ ### Print
272
321
 
273
322
  ```jsx
274
- <div className="before:content-['*'] before:text-red-6 relative" />
275
- <p className="first-letter:text-4xl first-letter:font-bold" />
276
- <input className="placeholder:text-gray-5" />
277
- <p className="selection:bg-blue-3" />
323
+ <div className="print:hidden" />
324
+ <div className="print:text-black print:bg-white" />
278
325
  ```
279
326
 
280
- | Modifier | CSS |
327
+ ### Orientation & accessibility media
328
+
329
+ | Modifier | Media query |
281
330
  |---|---|
282
- | `before:` | `::before` |
283
- | `after:` | `::after` |
284
- | `selection:` | `::selection` |
285
- | `first-letter:` | `::first-letter` |
286
- | `first-line:` | `::first-line` |
287
- | `marker:` | `::marker` |
288
- | `placeholder:` | `::placeholder` |
331
+ | `landscape:` | `(orientation: landscape)` |
332
+ | `portrait:` | `(orientation: portrait)` |
333
+ | `motion-reduce:` | `(prefers-reduced-motion: reduce)` |
334
+ | `motion-safe:` | `(prefers-reduced-motion: no-preference)` |
335
+ | `contrast-more:` | `(prefers-contrast: more)` |
336
+ | `contrast-less:` | `(prefers-contrast: less)` |
289
337
 
290
- ### Print
338
+ ### Directionality
291
339
 
292
340
  ```jsx
293
- <div className="print:hidden" />
294
- <div className="print:text-black print:bg-white" />
341
+ <div className="rtl:text-right ltr:text-left" />
295
342
  ```
296
343
 
297
- ### Important modifier
344
+ ### Important
298
345
 
299
- Prefix any class with `!` to add `!important` to every CSS declaration it produces:
346
+ Prefix any class with `!` to add `!important` to every declaration it produces:
300
347
 
301
348
  ```jsx
302
349
  <div className="!p-0 !m-0" />
@@ -365,76 +412,104 @@ module.exports = {
365
412
  darkMode: 'attribute', // 'attribute' | 'class' | 'media'
366
413
 
367
414
  theme: {
415
+ // Replace a theme section entirely
368
416
  colors: {
369
417
  brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' },
370
418
  },
371
419
  },
372
420
 
373
421
  extend: {
374
- theme: {
375
- colors: { brand: { 6: '#6366f1' } },
376
- spacing: { 18: '72px' },
377
- },
422
+ // Add to defaults without replacing them
423
+ colors: { brand: { 6: '#6366f1' } },
424
+ spacing: { 18: '72px' },
425
+ screens: { '3xl': '1920px' },
378
426
  },
379
427
 
380
428
  plugins: [
381
- ({ addUtility, theme }) => {
429
+ ({ addUtility, addVariant, theme }) => {
430
+ // Custom utility
382
431
  addUtility('border-brand', {
383
432
  borderColor: theme('colors.brand.6'),
384
433
  borderWidth: 2,
385
434
  });
435
+
436
+ // Custom variant — selector string (auto-converted to CSS rules)
437
+ addVariant('hocus', ':hover, :focus');
438
+ addVariant('supports-grid', '@media (display: grid)');
439
+ addVariant('dark-theme', '.dark-theme');
440
+
441
+ // Custom variant — full ModifierDef for JS-tracked interactive states
442
+ addVariant('long-press', {
443
+ pseudo: ':active',
444
+ jsBehavior: 'interactive',
445
+ jsMatch: (_, state) => !!state.longPressed,
446
+ });
386
447
  },
387
448
  ],
388
449
  };
389
450
  ```
390
451
 
391
- Apply at runtime:
452
+ ### Color aliases
392
453
 
393
- ```js
394
- import { updateConfig } from '@kbach/react';
454
+ Color values can reference other palette entries — chains up to 5 levels deep are resolved:
395
455
 
396
- updateConfig({ extend: { theme: { colors: { brand: { 6: '#6366f1' } } } } });
456
+ ```js
457
+ extend: {
458
+ colors: {
459
+ primary: 'blue-6', // → resolved hex of blue shade 6
460
+ brand: { 6: 'primary' }, // → follows through to blue-6's hex
461
+ },
462
+ },
397
463
  ```
398
464
 
399
465
  ### Font Family
400
466
 
401
- Define custom font families and use them with `font-<name>`:
402
-
403
467
  ```js
404
468
  // kbach.config.js
405
469
  module.exports = {
406
470
  extend: {
407
471
  fontFamily: {
408
- // shorthand: extend.fontFamily is equivalent to extend.theme.fontFamily
409
- sans: 'CormorantGaramond_400Regular',
410
- 'sans-md': 'CormorantGaramond_500Medium',
411
- 'sans-semi': 'CormorantGaramond_600SemiBold',
412
- 'sans-bold': 'CormorantGaramond_700Bold',
472
+ sans: 'Inter_400Regular',
473
+ 'sans-md': 'Inter_500Medium',
474
+ 'sans-bold': 'Inter_700Bold',
413
475
  },
414
476
  },
415
477
  };
416
478
  ```
417
479
 
418
- Use them as utilities:
480
+ Use as utilities:
419
481
 
420
482
  ```jsx
421
- <Text className="font-sans">Regular</Text>
422
- <Text className="font-sans-md">Medium</Text>
423
- <Text className="font-sans-bold">Bold</Text>
483
+ <p className="font-sans">Regular</p>
484
+ <p className="font-sans-md">Medium</p>
485
+ <p className="font-sans-bold">Bold</p>
424
486
  ```
425
487
 
426
- **Global default font (web only):** When you set `fontFamily.sans` to anything other than `'System'`, Kbach automatically injects `body { font-family: <your-font> }` into the page — all text inherits it without needing `font-sans` on every element.
488
+ **Global default font (web):** When `fontFamily.sans` is set to anything other than `'System'`, Kbach automatically injects `body { font-family: <your-font> }` — all text inherits it without `font-sans` on every element.
427
489
 
428
- **React Native:** CSS inheritance does not exist in RN. Apply `font-sans` (or your base font utility) to each `Text` component, or use `styled()` to create a pre-styled text component:
490
+ Apply at runtime (e.g. from a server-side user preference):
429
491
 
430
- ```jsx
431
- // Define once
432
- const Body = styled(Text, 'font-sans');
433
- const Heading = styled(Text, 'font-sans-bold text-2xl');
492
+ ```js
493
+ import { updateConfig } from '@kbach/react';
434
494
 
435
- // Use everywhere
436
- <Body>Regular text</Body>
437
- <Heading>Title</Heading>
495
+ updateConfig({ extend: { colors: { brand: { 6: '#6366f1' } } } });
496
+ ```
497
+
498
+ ## Custom responsive breakpoints
499
+
500
+ Add breakpoints in `extend.screens` — they automatically register as new modifier names:
501
+
502
+ ```js
503
+ // kbach.config.js
504
+ module.exports = {
505
+ extend: {
506
+ screens: { '3xl': '1920px' },
507
+ },
508
+ };
509
+ ```
510
+
511
+ ```jsx
512
+ <div className="3xl:max-w-screen-3xl 3xl:mx-auto" />
438
513
  ```
439
514
 
440
515
  ## Full reference
@@ -1,18 +1,22 @@
1
1
  import {
2
- EMPTY_BREAKPOINTS,
2
+ EMPTY_BREAKPOINTS2 as EMPTY_BREAKPOINTS,
3
3
  InteractiveWrapper,
4
+ hasResponsiveBuckets,
4
5
  useConditionalWidth,
5
6
  useGlobalDarkMode
6
- } from "./chunk-W7UBSRE5.mjs";
7
+ } from "./chunk-EYPBYQSB.mjs";
7
8
  import {
8
9
  flatten,
9
10
  getActiveBreakpoints,
10
11
  getConfig,
11
12
  getDefaultFontFamily,
13
+ getInteractiveModifiers,
14
+ getModeModifiers,
15
+ getResponsiveModifiers,
12
16
  isWeb,
13
17
  normalizeClassString,
14
18
  resolve
15
- } from "./chunk-NWVL4BES.mjs";
19
+ } from "./chunk-IZ3W6G3V.mjs";
16
20
 
17
21
  // src/jsx-runtime.tsx
18
22
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -20,16 +24,6 @@ import { Fragment } from "react";
20
24
 
21
25
  // src/DarkWrapper.tsx
22
26
  import React from "react";
23
- var RESPONSIVE_MODS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
24
- function hasResponsiveBuckets(resolved) {
25
- for (const key of Object.keys(resolved)) {
26
- if (key === "base") continue;
27
- for (const mod of key.split(":")) {
28
- if (RESPONSIVE_MODS.has(mod)) return true;
29
- }
30
- }
31
- return false;
32
- }
33
27
  var DarkWrapper = React.forwardRef(
34
28
  function DarkWrapper2({ Component, resolvedStyle, style: styleProp, children, ...rest }, ref) {
35
29
  const isDark = useGlobalDarkMode();
@@ -77,51 +71,39 @@ function stripWebOnlyProps(s) {
77
71
  }
78
72
 
79
73
  // src/jsx-runtime.tsx
80
- var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
81
- "hover",
82
- "not-hover",
83
- "focus",
84
- "not-focus",
85
- "active",
86
- "not-active",
87
- "pressed",
88
- "not-pressed",
89
- "disabled",
90
- "not-disabled",
91
- "checked",
92
- "not-checked",
93
- "visited",
94
- "not-visited",
95
- "placeholder",
96
- "group-hover",
97
- "group-focus",
98
- "peer-hover",
99
- "peer-focus"
100
- // focus-within / focus-visible / first / last / odd / even / only are CSS-only —
101
- // they have no JS state to track, so they don't need InteractiveWrapper.
102
- ]);
103
- var RESPONSIVE_MODS2 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
104
- var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
74
+ var _cachedDefaultFont = void 0;
75
+ var _defaultFontVersion = 0;
76
+ var _lastFontVersion = -1;
77
+ function _invalidateDefaultFontCache() {
78
+ _defaultFontVersion++;
79
+ }
80
+ function getCachedDefaultFont() {
81
+ if (_lastFontVersion !== _defaultFontVersion) {
82
+ _cachedDefaultFont = getDefaultFontFamily();
83
+ _lastFontVersion = _defaultFontVersion;
84
+ }
85
+ return _cachedDefaultFont;
86
+ }
87
+ var _bucketModsCache = /* @__PURE__ */ new WeakMap();
105
88
  function bucketMods(resolved) {
89
+ const cached = _bucketModsCache.get(resolved);
90
+ if (cached) return cached;
91
+ const interactiveMods = getInteractiveModifiers();
92
+ const modeMods = getModeModifiers();
93
+ const responsiveMods = getResponsiveModifiers();
106
94
  let interactive = false;
107
95
  let modeOrResponsive = false;
108
96
  for (const key of Object.keys(resolved)) {
109
97
  if (key === "base") continue;
110
- const parts = key.split(":");
111
- for (const mod of parts) {
112
- if (INTERACTIVE_MODS.has(mod)) {
113
- interactive = true;
114
- break;
115
- }
116
- }
117
- for (const mod of parts) {
118
- if (MODE_MODS_JSX.has(mod) || RESPONSIVE_MODS2.has(mod)) {
119
- modeOrResponsive = true;
120
- break;
121
- }
98
+ for (const mod of key.split(":")) {
99
+ if (!interactive && interactiveMods.has(mod)) interactive = true;
100
+ if (!modeOrResponsive && (modeMods.has(mod) || responsiveMods.has(mod))) modeOrResponsive = true;
122
101
  }
102
+ if (interactive && modeOrResponsive) break;
123
103
  }
124
- return { interactive, modeOrResponsive };
104
+ const result = { interactive, modeOrResponsive };
105
+ _bucketModsCache.set(resolved, result);
106
+ return result;
125
107
  }
126
108
  var CONSUMED_PROPS = /* @__PURE__ */ new Set(["className", "kb", "__kbachStyles", "__kbachClasses"]);
127
109
  function omitConsumed(props) {
@@ -196,10 +178,8 @@ function processElement(type, rawProps, key, isStaticChildren) {
196
178
  const { className, kb: kbProp, __kbachStyles, __kbachClasses } = rawProps;
197
179
  const classStr = className ?? kbProp ?? __kbachClasses;
198
180
  if (!classStr && !__kbachStyles) {
199
- const defaultFont = getDefaultFontFamily();
200
- if (!defaultFont) {
201
- return makeElement(isStaticChildren, type, rawProps, key);
202
- }
181
+ const defaultFont = getCachedDefaultFont();
182
+ if (!defaultFont) return makeElement(isStaticChildren, type, rawProps, key);
203
183
  const { style: userStyle2, ...passProps2 } = omitConsumed(rawProps);
204
184
  const finalStyle2 = userStyle2 ? Array.isArray(userStyle2) ? { fontFamily: defaultFont, ...Object.assign({}, ...userStyle2) } : { fontFamily: defaultFont, ...userStyle2 } : { fontFamily: defaultFont };
205
185
  return makeElement(isStaticChildren, type, { ...passProps2, style: finalStyle2 }, key);
@@ -248,6 +228,7 @@ function jsxs(type, props, key) {
248
228
 
249
229
  export {
250
230
  Fragment,
231
+ _invalidateDefaultFontCache,
251
232
  jsx,
252
233
  jsxs
253
234
  };
@@ -2,9 +2,10 @@ import {
2
2
  flatten,
3
3
  getActiveBreakpoints,
4
4
  getGlobalWidth,
5
+ getResponsiveModifiers,
5
6
  isWeb,
6
7
  subscribeGlobalWidth
7
- } from "./chunk-NWVL4BES.mjs";
8
+ } from "./chunk-IZ3W6G3V.mjs";
8
9
 
9
10
  // src/core/darkModeStore.ts
10
11
  var KEY = "__kbach_dark_store__";
@@ -107,23 +108,26 @@ function useConditionalWidth(active) {
107
108
  );
108
109
  }
109
110
 
110
- // src/InteractiveWrapper.tsx
111
- function chain(original, extra) {
112
- return (...args) => {
113
- original?.(...args);
114
- extra();
115
- };
116
- }
117
- var RESPONSIVE_MODS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
111
+ // src/shared-utils.ts
118
112
  function hasResponsiveBuckets(resolved) {
113
+ const responsiveMods = getResponsiveModifiers();
119
114
  for (const key of Object.keys(resolved)) {
120
115
  if (key === "base") continue;
121
116
  for (const mod of key.split(":")) {
122
- if (RESPONSIVE_MODS.has(mod)) return true;
117
+ if (responsiveMods.has(mod)) return true;
123
118
  }
124
119
  }
125
120
  return false;
126
121
  }
122
+ var EMPTY_BREAKPOINTS2 = /* @__PURE__ */ new Set();
123
+
124
+ // src/InteractiveWrapper.tsx
125
+ function chain(original, extra) {
126
+ return (...args) => {
127
+ original?.(...args);
128
+ extra();
129
+ };
130
+ }
127
131
  var InteractiveWrapper = forwardRef(
128
132
  function InteractiveWrapper2({
129
133
  Component,
@@ -219,6 +223,8 @@ export {
219
223
  EMPTY_BREAKPOINTS,
220
224
  useGlobalWidth,
221
225
  useConditionalWidth,
226
+ hasResponsiveBuckets,
227
+ EMPTY_BREAKPOINTS2,
222
228
  useGlobalDarkMode,
223
229
  InteractiveWrapper
224
230
  };