@kbach/ui 0.1.0-beta.7 → 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,921 +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
- `kbach()` also accepts an options wrapper — `{ framework, include, safelist }` — instead of a bare framework config directly:
124
-
125
- ```ts
126
- kbach({
127
- framework: { darkMode: 'attribute', theme: { colors: { brand: { 6: '#6366f1' } } } },
128
- include: ['src'], // dirs to scan (relative to Vite root) — default: src, app, pages, components, views, layouts
129
- safelist: [ /* see below */ ],
130
- })
131
- ```
132
-
133
- **`safelist`** — class names to always include, even if no scan finds them. Static extraction only ever sees complete class strings as they appear in your source — it can't evaluate `` `bg-${family}-${shade}` `` or similar runtime-built strings, since the actual value doesn't exist until the component renders (this is exactly why `useStyles()` exists — see its own docs above — it resolves those to a real inline style instead). If you want a genuinely dynamic set of classes in the static file anyway, generate the full list yourself and pass it in — same purpose as Tailwind's own `safelist` config:
134
-
135
- ```ts
136
- const families = ['red', 'blue', 'green' /* … */];
137
- const shades = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
138
-
139
- kbach({
140
- safelist: families.flatMap((f) => shades.map((s) => `bg-${f}-${s}`)),
141
- })
142
- ```
143
-
144
- Generated output format:
145
- ```css
146
- /* kbach:start */
147
- /* Generated by Kbach — do not edit */
148
-
149
- :root {
150
- --color-blue-6: #3b82f6;
151
- --color-gray-10: #111827;
152
- }
153
-
154
- /* Layout */
155
- .flex { display: flex }
156
- .items-center { align-items: center }
157
-
158
- /* Sizing */
159
- .w-full { width: 100% }
160
- .w-\[200px\] { width: 200px }
161
-
162
- /* Dark Mode */
163
- [data-theme="dark"] .dark\:bg-gray-10 { background-color: var(--color-gray-10) }
164
- /* kbach:end */
165
- ```
166
-
167
- ---
168
-
169
- ## Core API
170
-
171
- ### className prop
172
- Works on any element once the JSX runtime is active.
173
- ```jsx
174
- <div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
175
- <p className="text-gray-10 text-lg font-bold" />
176
- <button className="bg-blue-7 hover:bg-blue-8 rounded-lg px-4 py-2" />
177
- ```
178
-
179
- ### styled(Component, baseClasses)
180
- 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.
181
- ```jsx
182
- import { styled } from '@kbach/ui';
183
-
184
- const Card = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
185
- const Button = styled('button', 'bg-blue-7 hover:bg-blue-8 rounded-xl px-6 py-3');
186
-
187
- <Card kb="mt-4"> // merges mt-4 with base classes
188
- <Button kb="w-full" /> // merges w-full with base classes
189
- ```
190
-
191
- ### useStyles(classes)
192
- Resolve classes to a style object inside a component.
193
- ```jsx
194
- import { useStyles } from '@kbach/ui';
195
- const style = useStyles('bg-blue-6 px-3 py-1 rounded-full');
196
- return <span style={style}>Badge</span>;
197
- ```
198
-
199
- ### kb(classes)
200
- Resolve outside a component (static contexts).
201
- ```js
202
- import { kb } from '@kbach/ui';
203
- const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
204
- ```
205
-
206
- ### cx(...classes)
207
- Conditionally join class strings. Falsy values ignored.
208
- ```jsx
209
- import { cx } from '@kbach/ui';
210
- <div className={cx('p-4', isActive && 'border-2 border-blue-6', isDisabled && 'opacity-50')} />
211
- ```
212
-
213
- ### useTheme()
214
- ```ts
215
- const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
216
- // mode: 'light' | 'dark' | 'system'
217
- // resolvedMode: 'light' | 'dark'
218
- // isDark: boolean
219
- // setMode(mode): void
220
- // toggle(): void
221
- // config: ResolvedConfig
222
- ```
223
-
224
- ### useIsDark()
225
- ```ts
226
- const isDark = useIsDark(); // boolean
227
- ```
228
-
229
- ### useColors()
230
- Returns a proxy over the active theme's color palette.
231
- ```ts
232
- const colors = useColors();
233
- colors.blue[6] // '#3b82f6'
234
- colors.blue['6/50'] // 'rgba(59,130,246,0.5)'
235
- colors.white // '#ffffff'
236
- colors['white/20'] // 'rgba(255,255,255,0.2)'
237
- colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
238
- ```
239
- 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.
240
-
241
- 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:
242
-
243
- ```ts
244
- import '@kbach/ui'; // or '@kbach/native' — either works, native re-exports react's types
245
- declare module '@kbach/ui' {
246
- interface KbachCustomColors {
247
- brand: ColorScale; // a 1–12 shade scale, like the built-in blue/red
248
- accent: string; // a flat color, like the built-in white/black — also what a
249
- // mode-aware { light, dark } config color resolves to at read time
250
- }
251
- interface KbachCustomSpacing {
252
- 18: true; // only the key is read — value is just a placeholder
253
- }
254
- }
255
- ```
256
- A hand-authored file and the generated one both merge into the exact same interfaces, so either — or both at once — works.
257
-
258
- ---
259
-
260
- ## Modifier System
261
-
262
- Up to 3 modifiers can be chained in any order before the utility name.
263
-
264
- ```
265
- dark:hover:bg-blue-8
266
- sm:dark:text-lg
267
- motion-reduce:transition-none
268
- ```
269
-
270
- ### Theme modifiers
271
- | Modifier | Condition |
272
- |---|---|
273
- | `dark:` | Dark mode active |
274
- | `light:` | Light mode active |
275
- | `not-dark:` | Light mode active (alias) |
276
- | `not-light:` | Dark mode active (alias) |
277
-
278
- Dark mode strategy set in `ThemeProvider` or config:
279
- - `'attribute'` (default) — `[data-theme="dark"]` on a wrapper element
280
- - `'class'` — `.dark` class on a wrapper element
281
- - `'media'` — `@media (prefers-color-scheme: dark)`
282
-
283
- ### Interaction modifiers
284
- | Modifier | Triggers on |
285
- |---|---|
286
- | `hover:` | Mouse hover |
287
- | `focus:` | Element focused |
288
- | `focus-within:` | Focus anywhere inside element |
289
- | `focus-visible:` | Keyboard focus ring |
290
- | `active:` | Active state |
291
- | `pressed:` | Click / touch pressed |
292
- | `visited:` | Visited link |
293
- | `disabled:` | Disabled element |
294
- | `checked:` | Checkbox / radio checked |
295
- | `placeholder:` | Input placeholder text |
296
-
297
- Negated: `not-hover:`, `not-focus:`, `not-active:`, `not-pressed:`, `not-visited:`, `not-disabled:`, `not-checked:`
298
-
299
- ### Structural modifiers
300
- | Modifier | Pseudo-class |
301
- |---|---|
302
- | `first:` | `:first-child` |
303
- | `last:` | `:last-child` |
304
- | `odd:` | `:nth-child(odd)` |
305
- | `even:` | `:nth-child(even)` |
306
- | `only:` | `:only-child` |
307
-
308
- ### Responsive modifiers
309
- | Modifier | Min-width |
310
- |---|---|
311
- | `sm:` | 576 px |
312
- | `md:` | 768 px |
313
- | `lg:` | 1024 px |
314
- | `xl:` | 1280 px |
315
- | `2xl:` | 1536 px |
316
-
317
- Responsive styles are handled via `@media (min-width)` CSS rules — no JS breakpoint tracking.
318
-
319
- ### Group / peer modifiers
320
- Mark a parent with `group`, then use `group-hover:` etc. on children.
321
-
322
- ```jsx
323
- <div className="group">
324
- <span className="opacity-0 group-hover:opacity-100 transition" />
325
- </div>
326
- ```
327
-
328
- | Modifier | Fires when |
329
- |---|---|
330
- | `group-hover:` | Ancestor `.group` is hovered |
331
- | `group-focus:` | Ancestor `.group` is focused |
332
- | `peer-hover:` | Previous sibling `.peer` is hovered |
333
- | `peer-focus:` | Previous sibling `.peer` is focused |
334
-
335
- **Named groups/peers** — nested groups need names to avoid an inner element
336
- reacting to the wrong (nearest) ancestor: `group/{name}` + `group-hover/{name}:`,
337
- same for `peer/{name}` + `peer-hover/{name}:`/`peer-focus/{name}:`.
338
- ```jsx
339
- <div className="group/card">
340
- <div className="group/icon">
341
- <span className="group-hover/icon:opacity-100" />
342
- </div>
343
- <span className="group-hover/card:underline" />
344
- </div>
345
- ```
346
-
347
- ### Pseudo-element modifiers
348
- ```jsx
349
- <div className="before:content-['*'] before:text-red-6 relative" />
350
- <p className="first-letter:text-4xl first-letter:font-bold" />
351
- <p className="selection:bg-blue-3" />
352
- <input className="placeholder:text-gray-5" />
353
- ```
354
-
355
- | Modifier | CSS selector |
356
- |---|---|
357
- | `before:` | `::before` |
358
- | `after:` | `::after` |
359
- | `selection:` | `::selection` |
360
- | `first-letter:` | `::first-letter` |
361
- | `first-line:` | `::first-line` |
362
- | `marker:` | `::marker` |
363
- | `placeholder:` | `::placeholder` |
364
-
365
- ### Print modifier
366
- ```jsx
367
- <div className="print:hidden" />
368
- <div className="print:text-black print:bg-white" />
369
- ```
370
-
371
- ### Orientation modifiers
372
- | Modifier | Media query |
373
- |---|---|
374
- | `landscape:` | `@media (orientation: landscape)` |
375
- | `portrait:` | `@media (orientation: portrait)` |
376
-
377
- ### Accessibility modifiers
378
- | Modifier | Media query |
379
- |---|---|
380
- | `motion-reduce:` | `@media (prefers-reduced-motion: reduce)` |
381
- | `motion-safe:` | `@media (prefers-reduced-motion: no-preference)` |
382
- | `contrast-more:` | `@media (prefers-contrast: more)` |
383
- | `contrast-less:` | `@media (prefers-contrast: less)` |
384
-
385
- ### Directionality modifiers
386
- | Modifier | CSS selector scope |
387
- |---|---|
388
- | `rtl:` | `[dir="rtl"] .cls` |
389
- | `ltr:` | `[dir="ltr"] .cls` |
390
-
391
- ### Important modifier
392
- Prefix any class with `!` to add `!important` to every CSS declaration.
393
- ```jsx
394
- <div className="!p-0 !m-0 !bg-transparent" />
395
- ```
396
-
397
- ---
398
-
399
- ## Arbitrary Values
400
-
401
- Wrap any value in `[]` to use it directly.
402
- ```jsx
403
- <div className="bg-[#6366f1]" />
404
- <div className="p-[14px]" />
405
- <div className="w-[calc(100%-2rem)]" />
406
- <div className="text-[18px]" />
407
- <div className="rounded-[20px]" />
408
- <div className="bg-[rgba(99,102,241,0.15)]" />
409
- <div className="grid-cols-[1fr_2fr_1fr]" />
410
- ```
411
-
412
- ---
413
-
414
- ## Negative Values
415
- ```jsx
416
- <div className="-mt-4" /> // marginTop: -16
417
- <div className="-mx-2" /> // marginHorizontal: -8
418
- <div className="-translate-x-2" />
419
- <div className="-mt-[10px]" /> // marginTop: -10px
420
- ```
421
-
422
- ---
423
-
424
- ## Color with Opacity
425
- ```jsx
426
- <div className="bg-blue-6/50" /> // 50% opacity
427
- <div className="text-gray-10/75" /> // 75% opacity
428
- <div className="bg-black/[0.15]" /> // arbitrary opacity
429
- ```
430
-
431
- ---
432
-
433
- ## Color System
434
-
435
- ### 12-shade scale
436
- 1 = lightest, 12 = darkest.
437
-
438
- ```
439
- shade 1 2 3 4 5 6 7 8 9 10 11 12
440
- ─────────────────────────────────────────────
441
- light dark
442
- ```
443
-
444
- Usage: `bg-blue-6`, `text-gray-10`, `border-red-4/50`
445
-
446
- ### Color families (22 total)
447
- Grays: `slate`, `gray`, `zinc`, `neutral`, `stone`
448
- Colors: `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`
449
- Special: `transparent`, `current` (currentColor), `black`, `white`
450
-
451
- ---
452
-
453
- ## Utility Reference
454
-
455
- ### Background
456
- ```
457
- bg-{color} backgroundColor
458
- bg-{color}/{opacity} backgroundColor with alpha
459
- bg-transparent
460
- bg-clip-border/padding/content/text
461
- bg-gradient-to-{dir} linear gradient (t, tr, r, br, b, bl, l, tl)
462
- use with: from-{color}, via-{color}, to-{color}
463
- bg-none/auto/cover/contain
464
- bg-center/top/bottom/left/right/left-top/…
465
- bg-repeat/no-repeat/repeat-x/repeat-y
466
- bg-fixed/local/scroll
467
- bg-blend-{mode} normal, multiply, screen, overlay, darken, lighten, …
468
- ```
469
-
470
- ### Text
471
- ```
472
- 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)
473
- text-{color}
474
- text-left/right/center/justify/start/end
475
- text-wrap/nowrap/balance/pretty
476
- ```
477
-
478
- ### Font
479
- ```
480
- font-thin/extralight/light/normal/medium/semibold/bold/extrabold/black
481
- font-{family} sans, mono, serif, or custom
482
- ```
483
-
484
- ### Text decoration
485
- ```
486
- underline / overline / line-through / no-underline
487
- decoration-{color}
488
- decoration-solid/dashed/dotted/double/wavy
489
- decoration-0/1/2/4/8/auto/from-font
490
- underline-offset-0/1/2/4/8/auto
491
- ```
492
-
493
- ### Text transform
494
- ```
495
- uppercase / lowercase / capitalize / normal-case
496
- italic / not-italic
497
- ```
498
-
499
- ### Text overflow
500
- ```
501
- truncate
502
- overflow-ellipsis
503
- line-clamp-{n} n = 1–20
504
- line-clamp-none
505
- whitespace-normal/nowrap/pre/pre-wrap/pre-line
506
- break-normal/words/all
507
- ```
508
-
509
- ### Typography misc
510
- ```
511
- leading-none/tight/snug/normal/relaxed/loose (+ numeric 3–10)
512
- tracking-tighter/tight/normal/wide/wider/widest
513
- antialiased / subpixel-antialiased
514
- ```
515
-
516
- ### Spacing — Padding
517
- ```
518
- p-{n} px-{n} py-{n} pt-{n} pr-{n} pb-{n} pl-{n}
519
- ```
520
-
521
- ### Spacing — Margin
522
- ```
523
- m-{n} mx-{n} my-{n} mt-{n} mr-{n} mb-{n} ml-{n}
524
- mx-auto (centers element)
525
- ```
526
-
527
- Spacing scale (1 unit = 4px):
528
- `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`
529
-
530
- ### Sizing
531
- ```
532
- w-{n} h-{n} size-{n} min-w-{n} min-h-{n} max-w-{n} max-h-{n}
533
-
534
- Named max-w: none xs(320) sm(384) md(448) lg(512) xl(576) 2xl(672) 3xl(768)
535
- 4xl(896) 5xl(1024) 6xl(1152) 7xl(1280) prose(65ch)
536
-
537
- Screen (dvw/dvh — correct on mobile where browser chrome resizes the
538
- visible viewport; vw/vh are pinned to the largest viewport and overflow
539
- behind a shown address bar): w-screen(100dvw) h-screen(100dvh)
540
- min-w-screen max-w-screen min-h-screen max-h-screen
541
- ```
542
-
543
- ### Display
544
- ```
545
- block / inline / inline-block / flex / inline-flex
546
- grid / inline-grid / hidden / contents / flow-root / table
547
- ```
548
-
549
- ### Flex
550
- ```
551
- flex-row/col/row-reverse/col-reverse
552
- flex-wrap/nowrap/wrap-reverse
553
- flex-1 / flex-auto / flex-initial / flex-none
554
- flex-grow/grow-0 flex-shrink/shrink-0
555
- basis-{n}
556
- items-start/end/center/baseline/stretch
557
- justify-start/end/center/between/around/evenly
558
- justify-items-start/end/center/stretch
559
- justify-self-start/end/center/auto
560
- self-start/end/center/auto/stretch/baseline
561
- content-start/end/center/between/around/evenly/stretch
562
- order-{n}
563
- gap-{n} gap-x-{n} gap-y-{n}
564
- ```
565
-
566
- ### Grid
567
- ```
568
- grid-cols-{n} repeat(n, minmax(0, 1fr)) n = 1–12
569
- grid-rows-{n}
570
- grid-flow-row/col/dense/row-dense/col-dense
571
- auto-cols-auto/min/max/fr
572
- auto-rows-auto/min/max/fr
573
- col-span-{n} / col-span-full
574
- col-start-{n}/auto col-end-{n}/auto
575
- row-span-{n} / row-span-full
576
- row-start-{n}/auto row-end-{n}/auto
577
- place-items-start/end/center/stretch
578
- place-content-start/end/center/between/around/evenly/stretch
579
- place-self-start/end/center/auto/stretch
580
- ```
581
-
582
- ### Position
583
- ```
584
- static / relative / absolute / fixed / sticky
585
- inset-{n} inset-x-{n} inset-y-{n}
586
- top-{n} right-{n} bottom-{n} left-{n}
587
- z-0/10/20/30/40/50/auto
588
- ```
589
-
590
- ### Overflow
591
- ```
592
- overflow-hidden/visible/scroll/auto/clip
593
- overflow-x-hidden/visible/scroll/auto/clip
594
- overflow-y-hidden/visible/scroll/auto/clip
595
- ```
596
-
597
- ### Border
598
- ```
599
- border / border-{n} borderWidth: 0 1 2 4 8
600
- border-t/r/b/l
601
- border-{color}
602
- border-solid/dashed/dotted/none
603
- border-collapse / border-separate
604
- rounded / rounded-none/sm/md/lg/xl/2xl/3xl/full
605
- rounded-t/r/b/l rounded-tl/tr/bl/br
606
- ```
607
-
608
- ### Shadow
609
- ```
610
- shadow-sm / shadow / shadow-md / shadow-lg / shadow-xl / shadow-2xl / shadow-inner / shadow-none
611
- ```
612
- 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.
613
-
614
- ### Opacity
615
- ```
616
- opacity-0/5/10/15/20/25/30/40/50/60/70/75/80/90/95/100
617
- ```
618
-
619
- ### Ring
620
- Web: box-shadow ring. Native: approximated via borderWidth/borderColor
621
- (no box-shadow on RN) — affects layout there and shares properties with
622
- `border-*` (last class wins if both are used). `ring-offset-*`/`ring-inset`
623
- stay web-only, with no native equivalent.
624
- ```
625
- ring / ring-{n}(0 1 2 4 8)
626
- ring-{color}
627
- ring-inset (web only)
628
- ring-offset-{n}(0 1 2 4 8) (web only)
629
- ```
630
-
631
- ### Outline
632
- ```
633
- outline-none / outline / outline-{n}(0 1 2 4 8)
634
- outline-{color}
635
- outline-offset-{n}(0 1 2 4 8)
636
- ```
637
-
638
- ### Transforms
639
- ```
640
- scale-{n} scale-x-{n} scale-y-{n}
641
- rotate-{n}
642
- translate-x-{n} translate-y-{n}
643
- skew-x-{n} skew-y-{n}
644
- origin-center/top/top-right/right/bottom-right/bottom/bottom-left/left/top-left
645
- perspective-{n}
646
- ```
647
-
648
- ### Filters
649
- ```
650
- blur-{sm/md/lg/xl/2xl/3xl}
651
- brightness-{n} contrast-{n}
652
- grayscale / grayscale-0
653
- hue-rotate-{n}
654
- invert / invert-0
655
- saturate-{n}
656
- sepia / sepia-0
657
- drop-shadow-{sm/md/lg/xl/2xl/none}
658
-
659
- backdrop-blur-{n} backdrop-brightness-{n} backdrop-contrast-{n}
660
- backdrop-grayscale backdrop-hue-rotate-{n} backdrop-invert
661
- backdrop-opacity-{n} backdrop-saturate-{n} backdrop-sepia
662
- ```
663
-
664
- ### Animation & Transition
665
- ```
666
- animate-spin / animate-ping / animate-pulse / animate-bounce / animate-none
667
- transition / transition-all/none/colors/opacity/shadow/transform
668
- duration-75/100/150/200/300/500/700/1000
669
- delay-75/100/150/200/300/500/700/1000
670
- ease-linear/in/out/in-out ease-[cubic-bezier(...)]
671
- ```
672
-
673
- ### Cursor
674
- ```
675
- cursor-auto/default/pointer/wait/text/move/not-allowed
676
- cursor-grab/grabbing/zoom-in/zoom-out/crosshair/help/none
677
- ```
678
-
679
- ### Pointer events / User select
680
- ```
681
- pointer-events-none / pointer-events-auto
682
- select-none / select-text / select-all / select-auto
683
- ```
684
-
685
- ### Touch action
686
- ```
687
- touch-auto / touch-none / touch-pan-x / touch-pan-y
688
- touch-pan-left / touch-pan-right / touch-pan-up / touch-pan-down
689
- touch-pinch-zoom / touch-manipulation
690
- ```
691
-
692
- ### Scroll
693
- ```
694
- scroll-smooth / scroll-auto
695
- ```
696
-
697
- ### Float & Clear
698
- ```
699
- float-left / float-right / float-start / float-end / float-none
700
- clear-left / clear-right / clear-both / clear-start / clear-end / clear-none
701
- ```
702
-
703
- ### Vertical align
704
- ```
705
- align-baseline / align-top / align-middle / align-bottom
706
- align-text-top / align-text-bottom / align-sub / align-super
707
- ```
708
-
709
- ### Visibility
710
- ```
711
- visible / invisible
712
- sr-only / not-sr-only
713
- ```
714
-
715
- ### Lists
716
- ```
717
- list-none / list-disc / list-decimal
718
- list-inside / list-outside
719
- ```
720
-
721
- ### Misc
722
- ```
723
- appearance-none
724
- resize / resize-none / resize-x / resize-y
725
- box-border / box-content
726
- object-contain/cover/fill/none/scale-down
727
- object-center/top/bottom/left/right/…
728
- aspect-auto / aspect-square / aspect-video / aspect-[4/3]
729
- columns-{n} / columns-auto / columns-{size}
730
- caret-{color} / caret-auto / caret-transparent
731
- accent-{color} / accent-auto
732
- stroke-{color} / stroke-{n} / stroke-none (web only, SVG)
733
- fill-{color} / fill-none (web only, SVG)
734
- mix-blend-{mode}
735
- bg-blend-{mode}
736
- will-change-auto/scroll/contents/transform
737
- divide-x-{n} / divide-y-{n} / divide-{color} / divide-solid/dashed/dotted
738
- space-x-{n} / space-y-{n}
739
- group / peer (standalone marker classes)
740
- ```
741
-
742
- ---
743
-
744
- ## Theme Configuration
745
-
746
- ### kbach.config.js (project root)
747
- ```js
748
- module.exports = {
749
- darkMode: 'attribute', // 'attribute' | 'class' | 'media'
750
-
751
- theme: {
752
- colors: {
753
- brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a8a' },
754
- },
755
- },
756
-
757
- extend: {
758
- theme: {
759
- colors: { brand: { 6: '#6366f1' } },
760
- spacing: { 18: 72, 22: 88 },
761
- fontSize: { '10xl': 160 },
762
- fontFamily: {
763
- sans: 'Inter, sans-serif',
764
- },
765
- shadow: {
766
- // Deep-merged into the existing preset, not replaced — lg keeps its
767
- // native shadowColor/shadowOffset/shadowOpacity/shadowRadius/
768
- // elevation, only boxShadow (the web value) changes here.
769
- lg: { boxShadow: '0 10px 40px -10px rgba(99, 102, 241, 0.4)' },
770
- // A brand-new key adds a new shadow-3xl utility alongside the
771
- // defaults (sm/DEFAULT/md/lg/xl/2xl/inner/none) — web-only here,
772
- // since it has no native shadow*/elevation properties.
773
- '3xl': { boxShadow: '0 35px 60px -15px rgba(0, 0, 0, 0.3)' },
774
- },
775
- },
776
- },
777
-
778
- plugins: [
779
- ({ addUtility, theme }) => {
780
- addUtility('border-brand', {
781
- borderColor: theme('colors.brand.6'),
782
- borderWidth: 2,
783
- });
784
- },
785
- ],
786
- };
787
- ```
788
-
789
- **Global default font (web):** Setting `fontFamily.sans` to anything other than `'System'` auto-injects `body { font-family: <font> }`.
790
-
791
- ### Mode-aware colors (dark mode without `dark:`)
792
-
793
- 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:
794
-
795
- ```js
796
- extend: {
797
- theme: {
798
- colors: {
799
- surface: { light: '#ffffff', dark: '#111827' },
800
- // each side can itself be an alias — resolved independently
801
- accent: { light: 'blue-6', dark: 'blue-4' },
802
- // or derived from another color at an opacity — 'name/opacity', resolved
803
- // once here instead of only being computable at runtime via colors.alpha()
804
- accentSoft: { light: 'accent/30', dark: 'accent/40' },
805
- },
806
- },
807
- },
808
- ```
809
-
810
- ```jsx
811
- <div className="bg-surface text-accent hover:bg-accentSoft" />
812
- // equivalent to writing bg-white dark:bg-gray-9 text-blue-6 dark:text-blue-4
813
- // hover:bg-[rgba(...)] dark:hover:bg-[rgba(...)] by hand
814
- ```
815
-
816
- 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).
817
-
818
- ### Runtime update
819
- ```js
820
- import { updateConfig, clearCache } from '@kbach/ui';
821
- updateConfig({ extend: { theme: { colors: { brand: { 6: '#6366f1' } } } } });
822
- clearCache(); // always call after updateConfig()
823
- ```
824
-
825
- ### Default theme values
826
- ```
827
- spacing: 1 unit = 4px
828
- fontSize: xs(12)–9xl(128)
829
- borderRadius: none(0) sm(2) DEFAULT(4) md(6) lg(8) xl(12) 2xl(16) 3xl(24) full(9999)
830
- borderWidth: DEFAULT(1) 0 2 4 8
831
- opacity: 0 5 10 15 20 25 30 40 50 60 70 75 80 90 95 100
832
- lineHeight: none(1) tight(1.25) snug(1.375) normal(1.5) relaxed(1.625) loose(2)
833
- letterSpacing:tighter(-0.8) tight(-0.4) normal(0) wide(0.4) wider(0.8) widest(1.6)
834
- zIndex: auto 0 10 20 30 40 50
835
- screens: sm(576) md(768) lg(1024) xl(1280) 2xl(1536)
836
- ```
837
-
838
- ---
839
-
840
- ## Common Patterns
841
-
842
- ### Dark mode card
843
- ```jsx
844
- <div className="bg-white dark:bg-gray-9 rounded-2xl p-6 shadow-md">
845
- <h2 className="text-2xl font-bold text-gray-10 dark:text-white">Title</h2>
846
- <p className="text-gray-6 dark:text-gray-4 mt-2">Body text</p>
847
- </div>
848
- ```
849
-
850
- ### Interactive button
851
- ```jsx
852
- <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" />
853
- ```
854
-
855
- ### Responsive layout
856
- ```jsx
857
- <div className="flex flex-col md:flex-row gap-4">
858
- <aside className="w-full md:w-64 lg:w-80">…</aside>
859
- <main className="flex-1">…</main>
860
- </div>
861
- ```
862
-
863
- ### Responsive grid
864
- ```jsx
865
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
866
- {items.map(item => <Card key={item.id} />)}
867
- </div>
868
- ```
869
-
870
- ### Group hover reveal
871
- ```jsx
872
- <div className="group relative overflow-hidden rounded-xl">
873
- <img src="…" className="transition group-hover:scale-105" />
874
- <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center">
875
- <span className="text-white font-bold">View</span>
876
- </div>
877
- </div>
878
- ```
879
-
880
- ### Before/after pseudo-elements
881
- ```jsx
882
- <div className="relative before:absolute before:inset-0 before:bg-blue-6/10 before:rounded-xl" />
883
- ```
884
-
885
- ### Input with caret and focus ring
886
- ```jsx
887
- <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" />
888
- ```
889
-
890
- ### Print-specific styles
891
- ```jsx
892
- <nav className="print:hidden" />
893
- <article className="print:text-black print:bg-white print:shadow-none" />
894
- ```
895
-
896
- ### Reduced-motion safe animation
897
- ```jsx
898
- <div className="motion-safe:animate-spin motion-reduce:opacity-75" />
899
- ```
900
-
901
- ### RTL-aware spacing
902
- ```jsx
903
- <div className="ltr:pl-4 rtl:pr-4 ltr:text-left rtl:text-right" />
904
- ```
905
-
906
- ### Contrast accessibility
907
- ```jsx
908
- <button className="bg-blue-6 contrast-more:bg-blue-9 contrast-more:border-2 text-white">
909
- Submit
910
- </button>
911
- ```
912
-
913
- ---
914
-
915
- ## Caching
916
-
917
- The resolver uses an LRU cache (10,000 entries). Cleared automatically on `updateConfig()`. Manually:
918
- ```js
919
- import { clearCache } from '@kbach/ui';
920
- clearCache();
921
- ```