@kbach/react 0.4.3 → 0.5.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.
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.
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.
4
4
 
5
5
  ```jsx
6
6
  /** @jsxImportSource @kbach/react */
@@ -8,6 +8,9 @@ Tailwind-like utility classes for **React (web)**. Classes are converted to inli
8
8
  <div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
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
+ <div className="group">
12
+ <span className="group-hover:text-blue-6 transition" />
13
+ </div>
11
14
  ```
12
15
 
13
16
  ## Install
@@ -83,12 +86,13 @@ Works on any HTML element or React component once the JSX runtime is configured.
83
86
  ```jsx
84
87
  <div className="bg-white dark:bg-gray-10 p-4 rounded-xl" />
85
88
  <p className="text-gray-10 dark:text-white text-lg font-bold" />
86
- <button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-8 rounded-lg px-4 py-2" />
89
+ <button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-9 rounded-lg px-4 py-2" />
90
+ <input className="caret-blue-6 focus:ring-2" />
87
91
  ```
88
92
 
89
93
  ### styled(Component, classes)
90
94
 
91
- Pre-style any component. Returns a new component that merges the base classes with any `className` or `kb` prop passed at use time.
95
+ 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.
92
96
 
93
97
  ```jsx
94
98
  import { styled } from '@kbach/react';
@@ -121,7 +125,7 @@ function Badge() {
121
125
 
122
126
  ### kb(classes)
123
127
 
124
- Resolve classes outside a component (static contexts, StyleSheet.create, etc.).
128
+ Resolve classes outside a component (static contexts, etc.).
125
129
 
126
130
  ```js
127
131
  import { kb } from '@kbach/react';
@@ -179,29 +183,13 @@ import { useColors } from '@kbach/react';
179
183
 
180
184
  const colors = useColors();
181
185
 
182
- // Shade access
183
186
  colors.blue[6] // '#6c87b6'
184
- colors.red[11] // '#3e0606'
185
-
186
- // Shade + opacity (0–100)
187
187
  colors.blue['6/50'] // 'rgba(108,135,182,0.5)'
188
- colors.slate['3/10'] // 'rgba(221,227,242,0.1)'
189
-
190
- // Flat colors
191
188
  colors.white // '#ffffff'
192
- colors.transparent // 'transparent'
193
-
194
- // Flat color + opacity
195
189
  colors['white/20'] // 'rgba(255,255,255,0.2)'
196
- colors['black/80'] // 'rgba(0,0,0,0.8)'
197
-
198
- // Arbitrary CSS color with opacity
199
- colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
200
- colors.alpha('rgb(100,200,100)', 30) // 'rgba(100,200,100,0.3)'
190
+ colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
201
191
  ```
202
192
 
203
- Common uses: chart libraries that need raw hex values, dynamic inline styles, color pickers that mirror the theme palette.
204
-
205
193
  ### ThemeToggle
206
194
 
207
195
  Drop-in toggle component.
@@ -217,19 +205,95 @@ Drop-in toggle component.
217
205
 
218
206
  Up to 3 modifiers can be chained in any order.
219
207
 
208
+ ### Theme & interaction
209
+
220
210
  | Modifier | Trigger |
221
211
  |---|---|
222
212
  | `dark:` | Dark mode active |
223
213
  | `light:` | Light mode active |
224
214
  | `hover:` | Mouse hover |
225
- | `pressed:` | Click or touch |
226
- | `focus:` | Element focused |
227
- | `active:` | Element active |
228
- | `disabled:` | Element disabled |
215
+ | `pressed:` | Click / touch |
216
+ | `focus:` | Focused |
217
+ | `active:` | Active state |
218
+ | `disabled:` | Disabled |
219
+ | `checked:` | Checked (checkbox/radio) |
220
+ | `visited:` | Visited link |
221
+ | `focus-within:` | Focus inside element |
222
+ | `focus-visible:` | Keyboard focus ring |
223
+
224
+ Negated forms: `not-hover:`, `not-focus:`, `not-active:`, `not-pressed:`, `not-disabled:`, `not-checked:`, `not-visited:`
225
+
226
+ ### Structural
227
+
228
+ | Modifier | Matches |
229
+ |---|---|
230
+ | `first:` | `:first-child` |
231
+ | `last:` | `:last-child` |
232
+ | `odd:` | `:nth-child(odd)` |
233
+ | `even:` | `:nth-child(even)` |
234
+ | `only:` | `:only-child` |
235
+
236
+ ### Responsive
237
+
238
+ | Modifier | Min-width |
239
+ |---|---|
240
+ | `sm:` | 640 px |
241
+ | `md:` | 768 px |
242
+ | `lg:` | 1024 px |
243
+ | `xl:` | 1280 px |
244
+ | `2xl:` | 1536 px |
245
+
246
+ ### Group / peer
247
+
248
+ Mark an ancestor with `group` or `peer`, then style descendants with the modifier:
249
+
250
+ ```jsx
251
+ <div className="group">
252
+ <span className="opacity-0 group-hover:opacity-100 transition" />
253
+ </div>
254
+ ```
255
+
256
+ | Modifier | Fires when |
257
+ |---|---|
258
+ | `group-hover:` | Ancestor `.group` is hovered |
259
+ | `group-focus:` | Ancestor `.group` is focused |
260
+ | `peer-hover:` | Previous sibling `.peer` is hovered |
261
+ | `peer-focus:` | Previous sibling `.peer` is focused |
262
+
263
+ ### Pseudo-elements
264
+
265
+ Inject content or style structural parts of an element with real CSS:
266
+
267
+ ```jsx
268
+ <div className="before:content-['*'] before:text-red-6 relative" />
269
+ <p className="first-letter:text-4xl first-letter:font-bold" />
270
+ <input className="placeholder:text-gray-5" />
271
+ <p className="selection:bg-blue-3" />
272
+ ```
273
+
274
+ | Modifier | CSS |
275
+ |---|---|
276
+ | `before:` | `::before` |
277
+ | `after:` | `::after` |
278
+ | `selection:` | `::selection` |
279
+ | `first-letter:` | `::first-letter` |
280
+ | `first-line:` | `::first-line` |
281
+ | `marker:` | `::marker` |
282
+ | `placeholder:` | `::placeholder` |
283
+
284
+ ### Print
285
+
286
+ ```jsx
287
+ <div className="print:hidden" />
288
+ <div className="print:text-black print:bg-white" />
289
+ ```
290
+
291
+ ### Important modifier
292
+
293
+ Prefix any class with `!` to add `!important` to every CSS declaration it produces:
229
294
 
230
295
  ```jsx
231
- <div className="dark:hover:bg-gray-9" />
232
- <button className="dark:pressed:bg-indigo-8" />
296
+ <div className="!p-0 !m-0" />
233
297
  ```
234
298
 
235
299
  ## Arbitrary values
@@ -238,6 +302,7 @@ Up to 3 modifiers can be chained in any order.
238
302
  <div className="bg-[#6366f1]" />
239
303
  <div className="p-[14px]" />
240
304
  <div className="text-[18px]" />
305
+ <div className="w-[calc(100%-2rem)]" />
241
306
  <div className="bg-[rgba(99,102,241,0.15)]" />
242
307
  ```
243
308
 
@@ -265,13 +330,26 @@ Available families: slate, gray, zinc, neutral, stone, red, orange, amber, yello
265
330
  |---|---|
266
331
  | Background | `bg-white`, `bg-gray-10`, `bg-blue-6`, `bg-[#fff]` |
267
332
  | Text | `text-sm`, `text-2xl`, `text-gray-10`, `font-bold` |
333
+ | Text wrap | `text-balance`, `text-pretty`, `text-nowrap`, `text-wrap` |
334
+ | Line clamp | `line-clamp-3`, `line-clamp-none` |
268
335
  | Padding | `p-4`, `px-6`, `py-3` |
269
336
  | Margin | `m-4`, `mx-auto`, `mt-2`, `-mt-4` |
270
- | Size | `w-full`, `h-12`, `w-[200px]` |
337
+ | Size | `size-12`, `w-full`, `h-screen`, `w-screen`, `w-[200px]` |
271
338
  | Flex | `flex-1`, `flex-row`, `items-center`, `justify-between`, `gap-4` |
339
+ | Grid | `grid-3`, `col-span-2`, `grid-flow-col` |
272
340
  | Border | `border`, `border-2`, `border-gray-4`, `rounded-xl` |
273
341
  | Shadow | `shadow`, `shadow-md`, `shadow-lg` |
274
342
  | Opacity | `opacity-50`, `opacity-75` |
343
+ | Overflow | `overflow-hidden`, `overflow-clip`, `overflow-x-auto` |
344
+ | Cursor | `cursor-pointer`, `cursor-grab`, `cursor-zoom-in` |
345
+ | Scroll | `scroll-smooth`, `scroll-auto` |
346
+ | Float / clear | `float-left`, `float-right`, `clear-both` |
347
+ | Vertical align | `align-middle`, `align-top`, `align-baseline` |
348
+ | Touch action | `touch-none`, `touch-pan-x`, `touch-manipulation` |
349
+ | Caret | `caret-blue-6`, `caret-transparent` |
350
+ | Accent | `accent-blue-6`, `accent-auto` |
351
+ | Animation | `animate-spin`, `animate-pulse`, `animate-bounce`, `animate-ping` |
352
+ | Transition | `transition`, `duration-300`, `delay-150` |
275
353
 
276
354
  ## Configuration
277
355
 
@@ -8,7 +8,7 @@ import {
8
8
  resolve,
9
9
  useConditionalWidth,
10
10
  useGlobalDarkMode
11
- } from "./chunk-HY6PU2XS.mjs";
11
+ } from "./chunk-LTYLJ4B3.mjs";
12
12
 
13
13
  // src/jsx-runtime.tsx
14
14
  import { jsx as _jsx, jsxs as _jsxs, Fragment } from "react/jsx-runtime";
@@ -32,6 +32,7 @@ var DarkWrapper = React.forwardRef(
32
32
  const width = useConditionalWidth(needsWidth);
33
33
  const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
34
34
  const computedStyle = flatten(resolvedStyle, isDark, {}, breakpoints);
35
+ stripInternalMarkers(computedStyle);
35
36
  if (typeof Component !== "string") stripWebOnlyProps(computedStyle);
36
37
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
37
38
  const props = { ref, ...rest, style: finalStyle };
@@ -39,6 +40,13 @@ var DarkWrapper = React.forwardRef(
39
40
  }
40
41
  );
41
42
  DarkWrapper.displayName = "Kbach.DarkWrapper";
43
+ function stripInternalMarkers(s) {
44
+ delete s.__divideX;
45
+ delete s.__divideY;
46
+ delete s.__divideColor;
47
+ delete s.__divideStyle;
48
+ delete s.__keyframe;
49
+ }
42
50
  function stripWebOnlyProps(s) {
43
51
  if (s.display === "grid" || s.display === "inline-grid") delete s.display;
44
52
  delete s.gridTemplateColumns;
@@ -46,9 +54,18 @@ function stripWebOnlyProps(s) {
46
54
  delete s.gridColumn;
47
55
  delete s.gridRow;
48
56
  delete s.gridArea;
57
+ delete s.gridColumnStart;
58
+ delete s.gridColumnEnd;
59
+ delete s.gridRowStart;
60
+ delete s.gridRowEnd;
49
61
  delete s.gridAutoFlow;
50
62
  delete s.gridAutoColumns;
51
63
  delete s.gridAutoRows;
64
+ delete s.placeItems;
65
+ delete s.placeContent;
66
+ delete s.justifyItems;
67
+ delete s.placeSelf;
68
+ delete s.justifySelf;
52
69
  if (s.position === "sticky" || s.position === "fixed" || s.position === "static") {
53
70
  delete s.position;
54
71
  }
@@ -122,6 +139,57 @@ function mergeStyle(computed, userStyle) {
122
139
  function makeElement(isStaticChildren, type, props, key) {
123
140
  return (isStaticChildren ? _jsxs : _jsx)(type, props, key);
124
141
  }
142
+ function hasGridStyles(resolved) {
143
+ for (const styles of Object.values(resolved)) {
144
+ if (!styles) continue;
145
+ if (styles.display === "grid" || styles.display === "inline-grid") return true;
146
+ if ("gridTemplateColumns" in styles || "gridTemplateRows" in styles) return true;
147
+ if ("gridColumn" in styles || "gridRow" in styles) return true;
148
+ if ("gridColumnStart" in styles || "gridColumnEnd" in styles) return true;
149
+ if ("gridRowStart" in styles || "gridRowEnd" in styles) return true;
150
+ if ("gridAutoFlow" in styles || "gridAutoColumns" in styles || "gridAutoRows" in styles) return true;
151
+ if ("placeItems" in styles || "placeContent" in styles || "justifyItems" in styles) return true;
152
+ if ("placeSelf" in styles || "justifySelf" in styles) return true;
153
+ }
154
+ return false;
155
+ }
156
+ function hasWebPositionStyles(resolved) {
157
+ for (const styles of Object.values(resolved)) {
158
+ if (!styles) continue;
159
+ if (styles.position === "sticky" || styles.position === "fixed" || styles.position === "static") return true;
160
+ }
161
+ return false;
162
+ }
163
+ function stripInternalMarkers2(s) {
164
+ delete s.__divideX;
165
+ delete s.__divideY;
166
+ delete s.__divideColor;
167
+ delete s.__divideStyle;
168
+ delete s.__keyframe;
169
+ }
170
+ function stripWebOnlyInlineProps(s) {
171
+ if (s.display === "grid" || s.display === "inline-grid") delete s.display;
172
+ delete s.gridTemplateColumns;
173
+ delete s.gridTemplateRows;
174
+ delete s.gridColumn;
175
+ delete s.gridRow;
176
+ delete s.gridArea;
177
+ delete s.gridColumnStart;
178
+ delete s.gridColumnEnd;
179
+ delete s.gridRowStart;
180
+ delete s.gridRowEnd;
181
+ delete s.gridAutoFlow;
182
+ delete s.gridAutoColumns;
183
+ delete s.gridAutoRows;
184
+ delete s.placeItems;
185
+ delete s.placeContent;
186
+ delete s.justifyItems;
187
+ delete s.placeSelf;
188
+ delete s.justifySelf;
189
+ if (s.position === "sticky" || s.position === "fixed" || s.position === "static") {
190
+ delete s.position;
191
+ }
192
+ }
125
193
  function processElement(type, rawProps, key, isStaticChildren) {
126
194
  if (!rawProps) return _jsx(type, null, key);
127
195
  const { className, kb: kbProp, __kbachStyles, __kbachClasses } = rawProps;
@@ -130,13 +198,12 @@ function processElement(type, rawProps, key, isStaticChildren) {
130
198
  return makeElement(isStaticChildren, type, rawProps, key);
131
199
  }
132
200
  const config = getConfig();
133
- const isOnWeb = typeof document !== "undefined" && classStr != null;
134
- const isGridOnWeb = isOnWeb && /(?:^|\s)(?:[\w-]+:)*(?:grd|grid-(?:\d+|cols-\d+|rows-\d+))(?:\s|$)/.test(classStr);
135
- const hasWebPosition = isOnWeb && /(?:^|\s)(?:[\w-]+:)*(?:fixed|sticky)(?:\s|$)/.test(classStr);
136
- const actualType = isGridOnWeb || hasWebPosition ? "div" : type;
137
201
  const resolved = __kbachStyles ?? (classStr ? resolve(classStr, config.theme, config.darkMode) : {});
138
202
  const { style: userStyle, ...passProps } = omitConsumed(rawProps);
139
203
  const { interactive, modeOrResponsive } = bucketMods(resolved);
204
+ const onWeb = typeof document !== "undefined";
205
+ const needsDiv = onWeb && (hasGridStyles(resolved) || hasWebPositionStyles(resolved));
206
+ const actualType = needsDiv ? "div" : type;
140
207
  if (interactive) {
141
208
  return _jsx(InteractiveWrapper, {
142
209
  Component: actualType,
@@ -156,20 +223,8 @@ function processElement(type, rawProps, key, isStaticChildren) {
156
223
  }, key);
157
224
  }
158
225
  const computedStyle = flatten(resolved, false);
159
- if (typeof actualType !== "string") {
160
- if (computedStyle.display === "grid" || computedStyle.display === "inline-grid") delete computedStyle.display;
161
- delete computedStyle.gridTemplateColumns;
162
- delete computedStyle.gridTemplateRows;
163
- delete computedStyle.gridColumn;
164
- delete computedStyle.gridRow;
165
- delete computedStyle.gridArea;
166
- delete computedStyle.gridAutoFlow;
167
- delete computedStyle.gridAutoColumns;
168
- delete computedStyle.gridAutoRows;
169
- if (computedStyle.position === "sticky" || computedStyle.position === "fixed" || computedStyle.position === "static") {
170
- delete computedStyle.position;
171
- }
172
- }
226
+ stripInternalMarkers2(computedStyle);
227
+ if (typeof actualType !== "string") stripWebOnlyInlineProps(computedStyle);
173
228
  const finalStyle = mergeStyle(computedStyle, userStyle);
174
229
  return makeElement(isStaticChildren, actualType, {
175
230
  ...passProps,