@kbach/ui 0.1.0-beta.6 → 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/kbach-ui.md DELETED
@@ -1,900 +0,0 @@
1
- # Kbach UI — Complete AI Reference
2
-
3
- Kbach is a Tailwind-like utility CSS framework for React (web). Classes are written as `className` strings and resolved at render time through a custom JSX runtime. On web, stateful and structural CSS rules are injected into the page so they work with the browser cascade. A Vite plugin generates a physical `app.css` file for static CSS delivery.
4
-
5
- Package: `@kbach/ui`
6
-
7
- ---
8
-
9
- ## Setup
10
-
11
- ### tsconfig.json
12
- ```json
13
- { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@kbach/ui" } }
14
- ```
15
-
16
- ### vite.config.ts — Static CSS setup (recommended, plain Vite only — skip @vitejs/plugin-react if a meta-framework already provides its own Vite/React plugin, e.g. React Router's reactRouter(); see below)
17
- Requires `vite` and `@vitejs/plugin-react` as dev dependencies — not installed by `@kbach/ui` itself: `npm install -D vite @vitejs/plugin-react`.
18
- ```ts
19
- import { defineConfig } from 'vite';
20
- import react from '@vitejs/plugin-react';
21
- import { kbach } from '@kbach/ui/vite';
22
-
23
- export default defineConfig({
24
- plugins: [
25
- react({ jsxImportSource: '@kbach/ui' }),
26
- kbach(), // generates / updates app.css on every HMR event
27
- ],
28
- });
29
- ```
30
- Then create the stylesheet the plugin writes into and import it once — this import is the step that actually disables runtime CSS injection; `kbach()` in the plugins array alone only generates the file:
31
- ```css
32
- /* src/kbach.css */
33
- /* kbach:start */
34
- /* kbach:end */
35
- ```
36
- ```ts
37
- // main.tsx
38
- import './kbach.css';
39
- ```
40
-
41
- ### Runtime setup (any bundler, no Vite plugin)
42
- Skip `kbach()` and the `kbach.css` import above. Works with any bundler (Vite, webpack, Turbopack, Metro-for-web) with no build plugin — the only option for Next.js (see below), or for skipping the plugin for now on Vite.
43
-
44
- ### Babel (non-Vite)
45
- ```js
46
- module.exports = {
47
- presets: [['@babel/preset-react', { runtime: 'automatic', importSource: '@kbach/ui' }]],
48
- };
49
- ```
50
-
51
- ### Per-file (no config needed)
52
- ```jsx
53
- /** @jsxImportSource @kbach/ui */
54
- ```
55
-
56
- ### Wrap app
57
- ```jsx
58
- import { ThemeProvider } from '@kbach/ui';
59
- <ThemeProvider defaultMode="system"><App /></ThemeProvider>
60
- ```
61
-
62
- ### Next.js
63
- tsconfig `jsxImportSource` setup above applies as-is. No Vite plugin for Next.js (webpack/Turbopack) — falls back to runtime CSS injection, which only runs client-side, so expect a brief flash of unstyled content on first paint before hydration. `@kbach/ui`'s compiled output ships its own `"use client"` directive, so App Router Server Components can use `className`, `styled()`, hooks, and `<ThemeProvider>` directly, with no manual client wrapper needed.
64
-
65
- ### React Router
66
- Framework mode (v7+, SSR): do NOT add `@vitejs/plugin-react` — `@react-router/dev`'s `reactRouter()` Vite plugin already includes its own JSX transform + Fast Refresh integration. Adding both makes each inject its own Fast Refresh preamble into the same module, crashing the page (`Identifier 'RefreshRuntime' has already been declared`) before React hydrates — every class on the page silently fails to style because the app never mounts. tsconfig `jsxImportSource` alone is enough:
67
- ```ts
68
- import { reactRouter } from '@react-router/dev/vite';
69
- import { kbach } from '@kbach/ui/vite'; // omit if not using static CSS
70
- export default { plugins: [kbach(), reactRouter()] };
71
- ```
72
- Default scan dirs include `app/`. Library mode (client-only, no meta-framework Vite plugin involved) needs no special handling beyond the standard Vite setup above.
73
-
74
- ### React Native / Expo
75
- Same `npm install @kbach/ui` — no separate package. `@kbach/native` is deprecated and no longer maintained; its last published version is frozen as a compatibility shim re-exporting this package.
76
-
77
- ```js
78
- // babel.config.js
79
- module.exports = function (api) {
80
- api.cache(true);
81
- return { presets: ['babel-preset-expo', '@kbach/ui/babel'] };
82
- };
83
- ```
84
- One-liner: `const { createKbachConfig } = require('@kbach/ui/native'); module.exports = createKbachConfig();`. Merge into an existing config with `withKbachBabel({ presets: [...] })` (same module). After editing this file: `npx expo start --clear`.
85
-
86
- ```jsx
87
- import { ThemeProvider } from '@kbach/ui';
88
- <ThemeProvider defaultMode="system"><App /></ThemeProvider>
89
- ```
90
- `ThemeProvider` auto-detects React Native at render time and reads `useColorScheme()`/`useWindowDimensions()` automatically — same import as web, no `/native` subpath needed.
91
-
92
- `disablePersistence` on `<ThemeProvider>` saves to `AsyncStorage` on native (vs. `localStorage` on web) — same prop, platform-appropriate storage.
93
-
94
- Utility Reference below is tagged inline: `(web only)` entries no-op silently on native. Native-only additions not in the main tables: `tint-{color}` (Image/icon tinting), `perspective-{n}`, `backface-hidden`, `text-shadow`/`text-shadow-lg`. `ring-*` is a partial exception — falls back to `borderWidth`/`borderColor` on native (no box-shadow in RN), which *does* affect layout and shares properties with `border-*`.
95
-
96
- In a browser (Expo Web, Metro web), `@kbach/ui` switches to the same CSS-class strategy as plain web automatically — RN components substitute to HTML elements (`View`→`div`, `Text`→`span`, etc.), RN-only props map to HTML equivalents, and either the Vite plugin (recommended, same as Static CSS setup above) or a `<KbachReset />` near the root covers the base reset.
97
-
98
- CSS inheritance doesn't exist in React Native — apply font utilities to each `Text`, or define a styled component once: `const Body = styled(Text, 'font-sans text-gray-10 dark:text-white');`.
99
-
100
- ---
101
-
102
- ## Vite Plugin
103
-
104
- The `kbach()` Vite plugin scans your source files and writes generated CSS between `/* kbach:start */` / `/* kbach:end */` markers in your main CSS file (`app/app.css`, `src/index.css`, etc.).
105
-
106
- - Runs on `buildStart` (initial load) and on every HMR file change
107
- - Stateless: rescans all files fresh every time — removed classes are immediately evicted
108
- - Output is grouped by category with CSS custom properties for theme colors
109
- - Also indexes every `.css`/`.scss`/`.sass`/`.less` file the same `include` dirs cover, and warns (`console.warn`, dev-server terminal, not the browser) for any class that's neither a real Kbach utility NOR defined anywhere in those stylesheets — a likely typo. Classes intentionally handled elsewhere (CSS Modules, styled-components, a third-party component's own class) are recognized once anything in the project literally defines `.that-class-name` and stay silent. Each warning prints a `file:line:column` location that terminals with clickable-link support (VS Code's included) turn into a jump-to-that-class link.
110
-
111
- ```ts
112
- // vite.config.ts
113
- import { kbach } from '@kbach/ui/vite';
114
-
115
- kbach({
116
- darkMode: 'attribute',
117
- theme: {
118
- colors: { brand: { 6: '#6366f1' } },
119
- },
120
- })
121
- ```
122
-
123
- Generated output format:
124
- ```css
125
- /* kbach:start */
126
- /* Generated by Kbach — do not edit */
127
-
128
- :root {
129
- --color-blue-6: #3b82f6;
130
- --color-gray-10: #111827;
131
- }
132
-
133
- /* Layout */
134
- .flex { display: flex }
135
- .items-center { align-items: center }
136
-
137
- /* Sizing */
138
- .w-full { width: 100% }
139
- .w-\[200px\] { width: 200px }
140
-
141
- /* Dark Mode */
142
- [data-theme="dark"] .dark\:bg-gray-10 { background-color: var(--color-gray-10) }
143
- /* kbach:end */
144
- ```
145
-
146
- ---
147
-
148
- ## Core API
149
-
150
- ### className prop
151
- Works on any element once the JSX runtime is active.
152
- ```jsx
153
- <div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
154
- <p className="text-gray-10 text-lg font-bold" />
155
- <button className="bg-blue-7 hover:bg-blue-8 rounded-lg px-4 py-2" />
156
- ```
157
-
158
- ### styled(Component, baseClasses)
159
- Pre-style a component. Returns a new component that accepts a `kb` prop for extra classes. Forwards the full class string as `className` so CSS rules (group-hover:, before:, print:) match the element.
160
- ```jsx
161
- import { styled } from '@kbach/ui';
162
-
163
- const Card = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
164
- const Button = styled('button', 'bg-blue-7 hover:bg-blue-8 rounded-xl px-6 py-3');
165
-
166
- <Card kb="mt-4"> // merges mt-4 with base classes
167
- <Button kb="w-full" /> // merges w-full with base classes
168
- ```
169
-
170
- ### useStyles(classes)
171
- Resolve classes to a style object inside a component.
172
- ```jsx
173
- import { useStyles } from '@kbach/ui';
174
- const style = useStyles('bg-blue-6 px-3 py-1 rounded-full');
175
- return <span style={style}>Badge</span>;
176
- ```
177
-
178
- ### kb(classes)
179
- Resolve outside a component (static contexts).
180
- ```js
181
- import { kb } from '@kbach/ui';
182
- const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
183
- ```
184
-
185
- ### cx(...classes)
186
- Conditionally join class strings. Falsy values ignored.
187
- ```jsx
188
- import { cx } from '@kbach/ui';
189
- <div className={cx('p-4', isActive && 'border-2 border-blue-6', isDisabled && 'opacity-50')} />
190
- ```
191
-
192
- ### useTheme()
193
- ```ts
194
- const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
195
- // mode: 'light' | 'dark' | 'system'
196
- // resolvedMode: 'light' | 'dark'
197
- // isDark: boolean
198
- // setMode(mode): void
199
- // toggle(): void
200
- // config: ResolvedConfig
201
- ```
202
-
203
- ### useIsDark()
204
- ```ts
205
- const isDark = useIsDark(); // boolean
206
- ```
207
-
208
- ### useColors()
209
- Returns a proxy over the active theme's color palette.
210
- ```ts
211
- const colors = useColors();
212
- colors.blue[6] // '#3b82f6'
213
- colors.blue['6/50'] // 'rgba(59,130,246,0.5)'
214
- colors.white // '#ffffff'
215
- colors['white/20'] // 'rgba(255,255,255,0.2)'
216
- colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
217
- ```
218
- Typed against the built-in theme by default — `colors.blu` (typo) is a compile error. Custom `kbach.config.js` colors work too, with **zero setup**: the Vite plugin (and the Babel plugin, on React Native) automatically generate a `kbach-types.d.ts` next to your config, kept in sync every time the dev server / Metro picks up an edit to it — safe to add to `.gitignore`. `useColors()`/`useSpacing()` see your custom names immediately, no type parameter needed, same typo-catching as the built-in ones.
219
-
220
- If you'd rather commit the types instead of generating them (a library package with no dev server/bundler step of its own, for instance), hand-author the same thing in any `.d.ts` your tsconfig includes:
221
-
222
- ```ts
223
- import '@kbach/ui'; // or '@kbach/native' — either works, native re-exports react's types
224
- declare module '@kbach/ui' {
225
- interface KbachCustomColors {
226
- brand: ColorScale; // a 1–12 shade scale, like the built-in blue/red
227
- accent: string; // a flat color, like the built-in white/black — also what a
228
- // mode-aware { light, dark } config color resolves to at read time
229
- }
230
- interface KbachCustomSpacing {
231
- 18: true; // only the key is read — value is just a placeholder
232
- }
233
- }
234
- ```
235
- A hand-authored file and the generated one both merge into the exact same interfaces, so either — or both at once — works.
236
-
237
- ---
238
-
239
- ## Modifier System
240
-
241
- Up to 3 modifiers can be chained in any order before the utility name.
242
-
243
- ```
244
- dark:hover:bg-blue-8
245
- sm:dark:text-lg
246
- motion-reduce:transition-none
247
- ```
248
-
249
- ### Theme modifiers
250
- | Modifier | Condition |
251
- |---|---|
252
- | `dark:` | Dark mode active |
253
- | `light:` | Light mode active |
254
- | `not-dark:` | Light mode active (alias) |
255
- | `not-light:` | Dark mode active (alias) |
256
-
257
- Dark mode strategy set in `ThemeProvider` or config:
258
- - `'attribute'` (default) — `[data-theme="dark"]` on a wrapper element
259
- - `'class'` — `.dark` class on a wrapper element
260
- - `'media'` — `@media (prefers-color-scheme: dark)`
261
-
262
- ### Interaction modifiers
263
- | Modifier | Triggers on |
264
- |---|---|
265
- | `hover:` | Mouse hover |
266
- | `focus:` | Element focused |
267
- | `focus-within:` | Focus anywhere inside element |
268
- | `focus-visible:` | Keyboard focus ring |
269
- | `active:` | Active state |
270
- | `pressed:` | Click / touch pressed |
271
- | `visited:` | Visited link |
272
- | `disabled:` | Disabled element |
273
- | `checked:` | Checkbox / radio checked |
274
- | `placeholder:` | Input placeholder text |
275
-
276
- Negated: `not-hover:`, `not-focus:`, `not-active:`, `not-pressed:`, `not-visited:`, `not-disabled:`, `not-checked:`
277
-
278
- ### Structural modifiers
279
- | Modifier | Pseudo-class |
280
- |---|---|
281
- | `first:` | `:first-child` |
282
- | `last:` | `:last-child` |
283
- | `odd:` | `:nth-child(odd)` |
284
- | `even:` | `:nth-child(even)` |
285
- | `only:` | `:only-child` |
286
-
287
- ### Responsive modifiers
288
- | Modifier | Min-width |
289
- |---|---|
290
- | `sm:` | 576 px |
291
- | `md:` | 768 px |
292
- | `lg:` | 1024 px |
293
- | `xl:` | 1280 px |
294
- | `2xl:` | 1536 px |
295
-
296
- Responsive styles are handled via `@media (min-width)` CSS rules — no JS breakpoint tracking.
297
-
298
- ### Group / peer modifiers
299
- Mark a parent with `group`, then use `group-hover:` etc. on children.
300
-
301
- ```jsx
302
- <div className="group">
303
- <span className="opacity-0 group-hover:opacity-100 transition" />
304
- </div>
305
- ```
306
-
307
- | Modifier | Fires when |
308
- |---|---|
309
- | `group-hover:` | Ancestor `.group` is hovered |
310
- | `group-focus:` | Ancestor `.group` is focused |
311
- | `peer-hover:` | Previous sibling `.peer` is hovered |
312
- | `peer-focus:` | Previous sibling `.peer` is focused |
313
-
314
- **Named groups/peers** — nested groups need names to avoid an inner element
315
- reacting to the wrong (nearest) ancestor: `group/{name}` + `group-hover/{name}:`,
316
- same for `peer/{name}` + `peer-hover/{name}:`/`peer-focus/{name}:`.
317
- ```jsx
318
- <div className="group/card">
319
- <div className="group/icon">
320
- <span className="group-hover/icon:opacity-100" />
321
- </div>
322
- <span className="group-hover/card:underline" />
323
- </div>
324
- ```
325
-
326
- ### Pseudo-element modifiers
327
- ```jsx
328
- <div className="before:content-['*'] before:text-red-6 relative" />
329
- <p className="first-letter:text-4xl first-letter:font-bold" />
330
- <p className="selection:bg-blue-3" />
331
- <input className="placeholder:text-gray-5" />
332
- ```
333
-
334
- | Modifier | CSS selector |
335
- |---|---|
336
- | `before:` | `::before` |
337
- | `after:` | `::after` |
338
- | `selection:` | `::selection` |
339
- | `first-letter:` | `::first-letter` |
340
- | `first-line:` | `::first-line` |
341
- | `marker:` | `::marker` |
342
- | `placeholder:` | `::placeholder` |
343
-
344
- ### Print modifier
345
- ```jsx
346
- <div className="print:hidden" />
347
- <div className="print:text-black print:bg-white" />
348
- ```
349
-
350
- ### Orientation modifiers
351
- | Modifier | Media query |
352
- |---|---|
353
- | `landscape:` | `@media (orientation: landscape)` |
354
- | `portrait:` | `@media (orientation: portrait)` |
355
-
356
- ### Accessibility modifiers
357
- | Modifier | Media query |
358
- |---|---|
359
- | `motion-reduce:` | `@media (prefers-reduced-motion: reduce)` |
360
- | `motion-safe:` | `@media (prefers-reduced-motion: no-preference)` |
361
- | `contrast-more:` | `@media (prefers-contrast: more)` |
362
- | `contrast-less:` | `@media (prefers-contrast: less)` |
363
-
364
- ### Directionality modifiers
365
- | Modifier | CSS selector scope |
366
- |---|---|
367
- | `rtl:` | `[dir="rtl"] .cls` |
368
- | `ltr:` | `[dir="ltr"] .cls` |
369
-
370
- ### Important modifier
371
- Prefix any class with `!` to add `!important` to every CSS declaration.
372
- ```jsx
373
- <div className="!p-0 !m-0 !bg-transparent" />
374
- ```
375
-
376
- ---
377
-
378
- ## Arbitrary Values
379
-
380
- Wrap any value in `[]` to use it directly.
381
- ```jsx
382
- <div className="bg-[#6366f1]" />
383
- <div className="p-[14px]" />
384
- <div className="w-[calc(100%-2rem)]" />
385
- <div className="text-[18px]" />
386
- <div className="rounded-[20px]" />
387
- <div className="bg-[rgba(99,102,241,0.15)]" />
388
- <div className="grid-cols-[1fr_2fr_1fr]" />
389
- ```
390
-
391
- ---
392
-
393
- ## Negative Values
394
- ```jsx
395
- <div className="-mt-4" /> // marginTop: -16
396
- <div className="-mx-2" /> // marginHorizontal: -8
397
- <div className="-translate-x-2" />
398
- <div className="-mt-[10px]" /> // marginTop: -10px
399
- ```
400
-
401
- ---
402
-
403
- ## Color with Opacity
404
- ```jsx
405
- <div className="bg-blue-6/50" /> // 50% opacity
406
- <div className="text-gray-10/75" /> // 75% opacity
407
- <div className="bg-black/[0.15]" /> // arbitrary opacity
408
- ```
409
-
410
- ---
411
-
412
- ## Color System
413
-
414
- ### 12-shade scale
415
- 1 = lightest, 12 = darkest.
416
-
417
- ```
418
- shade 1 2 3 4 5 6 7 8 9 10 11 12
419
- ─────────────────────────────────────────────
420
- light dark
421
- ```
422
-
423
- Usage: `bg-blue-6`, `text-gray-10`, `border-red-4/50`
424
-
425
- ### Color families (22 total)
426
- Grays: `slate`, `gray`, `zinc`, `neutral`, `stone`
427
- Colors: `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`
428
- Special: `transparent`, `current` (currentColor), `black`, `white`
429
-
430
- ---
431
-
432
- ## Utility Reference
433
-
434
- ### Background
435
- ```
436
- bg-{color} backgroundColor
437
- bg-{color}/{opacity} backgroundColor with alpha
438
- bg-transparent
439
- bg-clip-border/padding/content/text
440
- bg-gradient-to-{dir} linear gradient (t, tr, r, br, b, bl, l, tl)
441
- use with: from-{color}, via-{color}, to-{color}
442
- bg-none/auto/cover/contain
443
- bg-center/top/bottom/left/right/left-top/…
444
- bg-repeat/no-repeat/repeat-x/repeat-y
445
- bg-fixed/local/scroll
446
- bg-blend-{mode} normal, multiply, screen, overlay, darken, lighten, …
447
- ```
448
-
449
- ### Text
450
- ```
451
- text-{size} xs(12) sm(14) base(16) lg(18) xl(20) 2xl(24) 3xl(30) 4xl(36) 5xl(48) 6xl(60) 7xl(72) 8xl(96) 9xl(128)
452
- text-{color}
453
- text-left/right/center/justify/start/end
454
- text-wrap/nowrap/balance/pretty
455
- ```
456
-
457
- ### Font
458
- ```
459
- font-thin/extralight/light/normal/medium/semibold/bold/extrabold/black
460
- font-{family} sans, mono, serif, or custom
461
- ```
462
-
463
- ### Text decoration
464
- ```
465
- underline / overline / line-through / no-underline
466
- decoration-{color}
467
- decoration-solid/dashed/dotted/double/wavy
468
- decoration-0/1/2/4/8/auto/from-font
469
- underline-offset-0/1/2/4/8/auto
470
- ```
471
-
472
- ### Text transform
473
- ```
474
- uppercase / lowercase / capitalize / normal-case
475
- italic / not-italic
476
- ```
477
-
478
- ### Text overflow
479
- ```
480
- truncate
481
- overflow-ellipsis
482
- line-clamp-{n} n = 1–20
483
- line-clamp-none
484
- whitespace-normal/nowrap/pre/pre-wrap/pre-line
485
- break-normal/words/all
486
- ```
487
-
488
- ### Typography misc
489
- ```
490
- leading-none/tight/snug/normal/relaxed/loose (+ numeric 3–10)
491
- tracking-tighter/tight/normal/wide/wider/widest
492
- antialiased / subpixel-antialiased
493
- ```
494
-
495
- ### Spacing — Padding
496
- ```
497
- p-{n} px-{n} py-{n} pt-{n} pr-{n} pb-{n} pl-{n}
498
- ```
499
-
500
- ### Spacing — Margin
501
- ```
502
- m-{n} mx-{n} my-{n} mt-{n} mr-{n} mb-{n} ml-{n}
503
- mx-auto (centers element)
504
- ```
505
-
506
- Spacing scale (1 unit = 4px):
507
- `px(1) 0 0.5(2) 1(4) 1.5(6) 2(8) 2.5(10) 3(12) 3.5(14) 4(16) 5(20) 6(24) 7(28) 8(32) 9(36) 10(40) 11(44) 12(48) 14(56) 16(64) 20(80) 24(96) 28(112) 32(128) 36(144) 40(160) 44(176) 48(192) 52(208) 56(224) 60(240) 64(256) 72(288) 80(320) 96(384) auto full(100%) 1/2 1/3 2/3 1/4 3/4 screen(100dvh) min max fit`
508
-
509
- ### Sizing
510
- ```
511
- w-{n} h-{n} size-{n} min-w-{n} min-h-{n} max-w-{n} max-h-{n}
512
-
513
- Named max-w: none xs(320) sm(384) md(448) lg(512) xl(576) 2xl(672) 3xl(768)
514
- 4xl(896) 5xl(1024) 6xl(1152) 7xl(1280) prose(65ch)
515
-
516
- Screen (dvw/dvh — correct on mobile where browser chrome resizes the
517
- visible viewport; vw/vh are pinned to the largest viewport and overflow
518
- behind a shown address bar): w-screen(100dvw) h-screen(100dvh)
519
- min-w-screen max-w-screen min-h-screen max-h-screen
520
- ```
521
-
522
- ### Display
523
- ```
524
- block / inline / inline-block / flex / inline-flex
525
- grid / inline-grid / hidden / contents / flow-root / table
526
- ```
527
-
528
- ### Flex
529
- ```
530
- flex-row/col/row-reverse/col-reverse
531
- flex-wrap/nowrap/wrap-reverse
532
- flex-1 / flex-auto / flex-initial / flex-none
533
- flex-grow/grow-0 flex-shrink/shrink-0
534
- basis-{n}
535
- items-start/end/center/baseline/stretch
536
- justify-start/end/center/between/around/evenly
537
- justify-items-start/end/center/stretch
538
- justify-self-start/end/center/auto
539
- self-start/end/center/auto/stretch/baseline
540
- content-start/end/center/between/around/evenly/stretch
541
- order-{n}
542
- gap-{n} gap-x-{n} gap-y-{n}
543
- ```
544
-
545
- ### Grid
546
- ```
547
- grid-cols-{n} repeat(n, minmax(0, 1fr)) n = 1–12
548
- grid-rows-{n}
549
- grid-flow-row/col/dense/row-dense/col-dense
550
- auto-cols-auto/min/max/fr
551
- auto-rows-auto/min/max/fr
552
- col-span-{n} / col-span-full
553
- col-start-{n}/auto col-end-{n}/auto
554
- row-span-{n} / row-span-full
555
- row-start-{n}/auto row-end-{n}/auto
556
- place-items-start/end/center/stretch
557
- place-content-start/end/center/between/around/evenly/stretch
558
- place-self-start/end/center/auto/stretch
559
- ```
560
-
561
- ### Position
562
- ```
563
- static / relative / absolute / fixed / sticky
564
- inset-{n} inset-x-{n} inset-y-{n}
565
- top-{n} right-{n} bottom-{n} left-{n}
566
- z-0/10/20/30/40/50/auto
567
- ```
568
-
569
- ### Overflow
570
- ```
571
- overflow-hidden/visible/scroll/auto/clip
572
- overflow-x-hidden/visible/scroll/auto/clip
573
- overflow-y-hidden/visible/scroll/auto/clip
574
- ```
575
-
576
- ### Border
577
- ```
578
- border / border-{n} borderWidth: 0 1 2 4 8
579
- border-t/r/b/l
580
- border-{color}
581
- border-solid/dashed/dotted/none
582
- border-collapse / border-separate
583
- rounded / rounded-none/sm/md/lg/xl/2xl/3xl/full
584
- rounded-t/r/b/l rounded-tl/tr/bl/br
585
- ```
586
-
587
- ### Shadow
588
- ```
589
- shadow-sm / shadow / shadow-md / shadow-lg / shadow-xl / shadow-2xl / shadow-inner / shadow-none
590
- ```
591
- Web: real `box-shadow` (Tailwind's own default values). Native: RN's shadowColor/shadowOffset/shadowOpacity/shadowRadius/elevation, tuned independently for RN's elevation model — not derived from the web value, so they don't need to match pixel-for-pixel. `shadow-inner` is web-only (RN has no inset-shadow equivalent). Customize or add sizes via `theme.extend.shadow` — see [Theme Configuration](#theme-configuration) below; each preset key merges independently, so overriding one size's `boxShadow` doesn't touch its native properties or any other size.
592
-
593
- ### Opacity
594
- ```
595
- opacity-0/5/10/15/20/25/30/40/50/60/70/75/80/90/95/100
596
- ```
597
-
598
- ### Ring
599
- Web: box-shadow ring. Native: approximated via borderWidth/borderColor
600
- (no box-shadow on RN) — affects layout there and shares properties with
601
- `border-*` (last class wins if both are used). `ring-offset-*`/`ring-inset`
602
- stay web-only, with no native equivalent.
603
- ```
604
- ring / ring-{n}(0 1 2 4 8)
605
- ring-{color}
606
- ring-inset (web only)
607
- ring-offset-{n}(0 1 2 4 8) (web only)
608
- ```
609
-
610
- ### Outline
611
- ```
612
- outline-none / outline / outline-{n}(0 1 2 4 8)
613
- outline-{color}
614
- outline-offset-{n}(0 1 2 4 8)
615
- ```
616
-
617
- ### Transforms
618
- ```
619
- scale-{n} scale-x-{n} scale-y-{n}
620
- rotate-{n}
621
- translate-x-{n} translate-y-{n}
622
- skew-x-{n} skew-y-{n}
623
- origin-center/top/top-right/right/bottom-right/bottom/bottom-left/left/top-left
624
- perspective-{n}
625
- ```
626
-
627
- ### Filters
628
- ```
629
- blur-{sm/md/lg/xl/2xl/3xl}
630
- brightness-{n} contrast-{n}
631
- grayscale / grayscale-0
632
- hue-rotate-{n}
633
- invert / invert-0
634
- saturate-{n}
635
- sepia / sepia-0
636
- drop-shadow-{sm/md/lg/xl/2xl/none}
637
-
638
- backdrop-blur-{n} backdrop-brightness-{n} backdrop-contrast-{n}
639
- backdrop-grayscale backdrop-hue-rotate-{n} backdrop-invert
640
- backdrop-opacity-{n} backdrop-saturate-{n} backdrop-sepia
641
- ```
642
-
643
- ### Animation & Transition
644
- ```
645
- animate-spin / animate-ping / animate-pulse / animate-bounce / animate-none
646
- transition / transition-all/none/colors/opacity/shadow/transform
647
- duration-75/100/150/200/300/500/700/1000
648
- delay-75/100/150/200/300/500/700/1000
649
- ease-linear/in/out/in-out ease-[cubic-bezier(...)]
650
- ```
651
-
652
- ### Cursor
653
- ```
654
- cursor-auto/default/pointer/wait/text/move/not-allowed
655
- cursor-grab/grabbing/zoom-in/zoom-out/crosshair/help/none
656
- ```
657
-
658
- ### Pointer events / User select
659
- ```
660
- pointer-events-none / pointer-events-auto
661
- select-none / select-text / select-all / select-auto
662
- ```
663
-
664
- ### Touch action
665
- ```
666
- touch-auto / touch-none / touch-pan-x / touch-pan-y
667
- touch-pan-left / touch-pan-right / touch-pan-up / touch-pan-down
668
- touch-pinch-zoom / touch-manipulation
669
- ```
670
-
671
- ### Scroll
672
- ```
673
- scroll-smooth / scroll-auto
674
- ```
675
-
676
- ### Float & Clear
677
- ```
678
- float-left / float-right / float-start / float-end / float-none
679
- clear-left / clear-right / clear-both / clear-start / clear-end / clear-none
680
- ```
681
-
682
- ### Vertical align
683
- ```
684
- align-baseline / align-top / align-middle / align-bottom
685
- align-text-top / align-text-bottom / align-sub / align-super
686
- ```
687
-
688
- ### Visibility
689
- ```
690
- visible / invisible
691
- sr-only / not-sr-only
692
- ```
693
-
694
- ### Lists
695
- ```
696
- list-none / list-disc / list-decimal
697
- list-inside / list-outside
698
- ```
699
-
700
- ### Misc
701
- ```
702
- appearance-none
703
- resize / resize-none / resize-x / resize-y
704
- box-border / box-content
705
- object-contain/cover/fill/none/scale-down
706
- object-center/top/bottom/left/right/…
707
- aspect-auto / aspect-square / aspect-video / aspect-[4/3]
708
- columns-{n} / columns-auto / columns-{size}
709
- caret-{color} / caret-auto / caret-transparent
710
- accent-{color} / accent-auto
711
- stroke-{color} / stroke-{n} / stroke-none (web only, SVG)
712
- fill-{color} / fill-none (web only, SVG)
713
- mix-blend-{mode}
714
- bg-blend-{mode}
715
- will-change-auto/scroll/contents/transform
716
- divide-x-{n} / divide-y-{n} / divide-{color} / divide-solid/dashed/dotted
717
- space-x-{n} / space-y-{n}
718
- group / peer (standalone marker classes)
719
- ```
720
-
721
- ---
722
-
723
- ## Theme Configuration
724
-
725
- ### kbach.config.js (project root)
726
- ```js
727
- module.exports = {
728
- darkMode: 'attribute', // 'attribute' | 'class' | 'media'
729
-
730
- theme: {
731
- colors: {
732
- brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a8a' },
733
- },
734
- },
735
-
736
- extend: {
737
- theme: {
738
- colors: { brand: { 6: '#6366f1' } },
739
- spacing: { 18: 72, 22: 88 },
740
- fontSize: { '10xl': 160 },
741
- fontFamily: {
742
- sans: 'Inter, sans-serif',
743
- },
744
- shadow: {
745
- // Deep-merged into the existing preset, not replaced — lg keeps its
746
- // native shadowColor/shadowOffset/shadowOpacity/shadowRadius/
747
- // elevation, only boxShadow (the web value) changes here.
748
- lg: { boxShadow: '0 10px 40px -10px rgba(99, 102, 241, 0.4)' },
749
- // A brand-new key adds a new shadow-3xl utility alongside the
750
- // defaults (sm/DEFAULT/md/lg/xl/2xl/inner/none) — web-only here,
751
- // since it has no native shadow*/elevation properties.
752
- '3xl': { boxShadow: '0 35px 60px -15px rgba(0, 0, 0, 0.3)' },
753
- },
754
- },
755
- },
756
-
757
- plugins: [
758
- ({ addUtility, theme }) => {
759
- addUtility('border-brand', {
760
- borderColor: theme('colors.brand.6'),
761
- borderWidth: 2,
762
- });
763
- },
764
- ],
765
- };
766
- ```
767
-
768
- **Global default font (web):** Setting `fontFamily.sans` to anything other than `'System'` auto-injects `body { font-family: <font> }`.
769
-
770
- ### Mode-aware colors (dark mode without `dark:`)
771
-
772
- A color value can be `{ light, dark }` instead of a plain string — define it once, and every class using that color automatically picks the right side, with no `dark:` variant needed at the call site:
773
-
774
- ```js
775
- extend: {
776
- theme: {
777
- colors: {
778
- surface: { light: '#ffffff', dark: '#111827' },
779
- // each side can itself be an alias — resolved independently
780
- accent: { light: 'blue-6', dark: 'blue-4' },
781
- // or derived from another color at an opacity — 'name/opacity', resolved
782
- // once here instead of only being computable at runtime via colors.alpha()
783
- accentSoft: { light: 'accent/30', dark: 'accent/40' },
784
- },
785
- },
786
- },
787
- ```
788
-
789
- ```jsx
790
- <div className="bg-surface text-accent hover:bg-accentSoft" />
791
- // equivalent to writing bg-white dark:bg-gray-9 text-blue-6 dark:text-blue-4
792
- // hover:bg-[rgba(...)] dark:hover:bg-[rgba(...)] by hand
793
- ```
794
-
795
- Works everywhere a color does — `useColors()` (returns the active side directly), opacity composition (`bg-surface/50`), per-shade within a scale (`brand: { 6: { light: '#3b82f6', dark: '#60a5fa' } }`), and stacked under an explicit modifier (`dark:hover:bg-accent` correctly uses the dark side, still scoped to dark mode + hover — the modifier doesn't need to be there in the first place, but it's respected if it is).
796
-
797
- ### Runtime update
798
- ```js
799
- import { updateConfig, clearCache } from '@kbach/ui';
800
- updateConfig({ extend: { theme: { colors: { brand: { 6: '#6366f1' } } } } });
801
- clearCache(); // always call after updateConfig()
802
- ```
803
-
804
- ### Default theme values
805
- ```
806
- spacing: 1 unit = 4px
807
- fontSize: xs(12)–9xl(128)
808
- borderRadius: none(0) sm(2) DEFAULT(4) md(6) lg(8) xl(12) 2xl(16) 3xl(24) full(9999)
809
- borderWidth: DEFAULT(1) 0 2 4 8
810
- opacity: 0 5 10 15 20 25 30 40 50 60 70 75 80 90 95 100
811
- lineHeight: none(1) tight(1.25) snug(1.375) normal(1.5) relaxed(1.625) loose(2)
812
- letterSpacing:tighter(-0.8) tight(-0.4) normal(0) wide(0.4) wider(0.8) widest(1.6)
813
- zIndex: auto 0 10 20 30 40 50
814
- screens: sm(576) md(768) lg(1024) xl(1280) 2xl(1536)
815
- ```
816
-
817
- ---
818
-
819
- ## Common Patterns
820
-
821
- ### Dark mode card
822
- ```jsx
823
- <div className="bg-white dark:bg-gray-9 rounded-2xl p-6 shadow-md">
824
- <h2 className="text-2xl font-bold text-gray-10 dark:text-white">Title</h2>
825
- <p className="text-gray-6 dark:text-gray-4 mt-2">Body text</p>
826
- </div>
827
- ```
828
-
829
- ### Interactive button
830
- ```jsx
831
- <button className="bg-blue-7 hover:bg-blue-8 active:bg-blue-9 disabled:opacity-50 disabled:cursor-not-allowed text-white font-semibold px-6 py-3 rounded-xl transition" />
832
- ```
833
-
834
- ### Responsive layout
835
- ```jsx
836
- <div className="flex flex-col md:flex-row gap-4">
837
- <aside className="w-full md:w-64 lg:w-80">…</aside>
838
- <main className="flex-1">…</main>
839
- </div>
840
- ```
841
-
842
- ### Responsive grid
843
- ```jsx
844
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
845
- {items.map(item => <Card key={item.id} />)}
846
- </div>
847
- ```
848
-
849
- ### Group hover reveal
850
- ```jsx
851
- <div className="group relative overflow-hidden rounded-xl">
852
- <img src="…" className="transition group-hover:scale-105" />
853
- <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center">
854
- <span className="text-white font-bold">View</span>
855
- </div>
856
- </div>
857
- ```
858
-
859
- ### Before/after pseudo-elements
860
- ```jsx
861
- <div className="relative before:absolute before:inset-0 before:bg-blue-6/10 before:rounded-xl" />
862
- ```
863
-
864
- ### Input with caret and focus ring
865
- ```jsx
866
- <input className="caret-blue-6 focus:ring-2 focus:ring-blue-5 focus:outline-none border border-gray-4 rounded-lg px-4 py-2" />
867
- ```
868
-
869
- ### Print-specific styles
870
- ```jsx
871
- <nav className="print:hidden" />
872
- <article className="print:text-black print:bg-white print:shadow-none" />
873
- ```
874
-
875
- ### Reduced-motion safe animation
876
- ```jsx
877
- <div className="motion-safe:animate-spin motion-reduce:opacity-75" />
878
- ```
879
-
880
- ### RTL-aware spacing
881
- ```jsx
882
- <div className="ltr:pl-4 rtl:pr-4 ltr:text-left rtl:text-right" />
883
- ```
884
-
885
- ### Contrast accessibility
886
- ```jsx
887
- <button className="bg-blue-6 contrast-more:bg-blue-9 contrast-more:border-2 text-white">
888
- Submit
889
- </button>
890
- ```
891
-
892
- ---
893
-
894
- ## Caching
895
-
896
- The resolver uses an LRU cache (10,000 entries). Cleared automatically on `updateConfig()`. Manually:
897
- ```js
898
- import { clearCache } from '@kbach/ui';
899
- clearCache();
900
- ```