# Kbach React — Complete AI Reference

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.

Package: `@kbach/react`

---

## Setup

### tsconfig.json
```json
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@kbach/react" } }
```

### 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)
Requires `vite` and `@vitejs/plugin-react` as dev dependencies — not installed by `@kbach/react` itself: `npm install -D vite @vitejs/plugin-react`.
```ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { kbach } from '@kbach/react/vite';

export default defineConfig({
  plugins: [
    react({ jsxImportSource: '@kbach/react' }),
    kbach(),          // generates / updates app.css on every HMR event
  ],
});
```
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:
```css
/* src/kbach.css */
/* kbach:start */
/* kbach:end */
```
```ts
// main.tsx
import './kbach.css';
```

### Runtime setup (any bundler, no Vite plugin)
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.

### Babel (non-Vite)
```js
module.exports = {
  presets: [['@babel/preset-react', { runtime: 'automatic', importSource: '@kbach/react' }]],
};
```

### Per-file (no config needed)
```jsx
/** @jsxImportSource @kbach/react */
```

### Wrap app
```jsx
import { ThemeProvider } from '@kbach/react';
<ThemeProvider defaultMode="system"><App /></ThemeProvider>
```

### Next.js
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/react`'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.

### React Router
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:
```ts
import { reactRouter } from '@react-router/dev/vite';
import { kbach } from '@kbach/react/vite'; // omit if not using static CSS
export default { plugins: [kbach(), reactRouter()] };
```
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.

### React Native / Expo
Same `npm install @kbach/react` — no separate package. `@kbach/native` still exists but is now a deprecated compatibility shim that re-exports this package.

```js
// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return { presets: ['babel-preset-expo', '@kbach/react/babel'] };
};
```
One-liner: `const { createKbachConfig } = require('@kbach/react/native'); module.exports = createKbachConfig();`. Merge into an existing config with `withKbachBabel({ presets: [...] })` (same module). After editing this file: `npx expo start --clear`.

```jsx
import { ThemeProvider } from '@kbach/react/native';
<ThemeProvider defaultMode="system"><App /></ThemeProvider>
```
Native-aware — reads `useColorScheme()`/`useWindowDimensions()` automatically. The plain `ThemeProvider` from `@kbach/react` (no `/native`) has no automatic RN wiring; import the `/native` one on React Native.

`disablePersistence` on `<ThemeProvider>` saves to `AsyncStorage` on native (vs. `localStorage` on web) — same prop, platform-appropriate storage.

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-*`.

In a browser (Expo Web, Metro web), `@kbach/react` 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.

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');`.

---

## Vite Plugin

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.).

- Runs on `buildStart` (initial load) and on every HMR file change
- Stateless: rescans all files fresh every time — removed classes are immediately evicted
- Output is grouped by category with CSS custom properties for theme colors
- 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.

```ts
// vite.config.ts
import { kbach } from '@kbach/react/vite';

kbach({
  darkMode: 'attribute',
  theme: {
    colors: { brand: { 6: '#6366f1' } },
  },
})
```

Generated output format:
```css
/* kbach:start */
/* Generated by Kbach — do not edit */

:root {
  --color-blue-6: #3b82f6;
  --color-gray-10: #111827;
}

/* Layout */
.flex { display: flex }
.items-center { align-items: center }

/* Sizing */
.w-full { width: 100% }
.w-\[200px\] { width: 200px }

/* Dark Mode */
[data-theme="dark"] .dark\:bg-gray-10 { background-color: var(--color-gray-10) }
/* kbach:end */
```

---

## Core API

### className prop
Works on any element once the JSX runtime is active.
```jsx
<div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
<p className="text-gray-10 text-lg font-bold" />
<button className="bg-blue-7 hover:bg-blue-8 rounded-lg px-4 py-2" />
```

### styled(Component, baseClasses)
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.
```jsx
import { styled } from '@kbach/react';

const Card   = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
const Button = styled('button', 'bg-blue-7 hover:bg-blue-8 rounded-xl px-6 py-3');

<Card kb="mt-4">          // merges mt-4 with base classes
<Button kb="w-full" />    // merges w-full with base classes
```

### useStyles(classes)
Resolve classes to a style object inside a component.
```jsx
import { useStyles } from '@kbach/react';
const style = useStyles('bg-blue-6 px-3 py-1 rounded-full');
return <span style={style}>Badge</span>;
```

### kb(classes)
Resolve outside a component (static contexts).
```js
import { kb } from '@kbach/react';
const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
```

### cx(...classes)
Conditionally join class strings. Falsy values ignored.
```jsx
import { cx } from '@kbach/react';
<div className={cx('p-4', isActive && 'border-2 border-blue-6', isDisabled && 'opacity-50')} />
```

### useTheme()
```ts
const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
// mode: 'light' | 'dark' | 'system'
// resolvedMode: 'light' | 'dark'
// isDark: boolean
// setMode(mode): void
// toggle(): void
// config: ResolvedConfig
```

### useIsDark()
```ts
const isDark = useIsDark(); // boolean
```

### useColors()
Returns a proxy over the active theme's color palette.
```ts
const colors = useColors();
colors.blue[6]            // '#3b82f6'
colors.blue['6/50']       // 'rgba(59,130,246,0.5)'
colors.white              // '#ffffff'
colors['white/20']        // 'rgba(255,255,255,0.2)'
colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
```
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.

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:

```ts
import '@kbach/react'; // or '@kbach/native' — either works, native re-exports react's types
declare module '@kbach/react' {
  interface KbachCustomColors {
    brand: ColorScale; // a 1–12 shade scale, like the built-in blue/red
    accent: string;    // a flat color, like the built-in white/black — also what a
                        // mode-aware { light, dark } config color resolves to at read time
  }
  interface KbachCustomSpacing {
    18: true; // only the key is read — value is just a placeholder
  }
}
```
A hand-authored file and the generated one both merge into the exact same interfaces, so either — or both at once — works.

---

## Modifier System

Up to 3 modifiers can be chained in any order before the utility name.

```
dark:hover:bg-blue-8
sm:dark:text-lg
motion-reduce:transition-none
```

### Theme modifiers
| Modifier | Condition |
|---|---|
| `dark:` | Dark mode active |
| `light:` | Light mode active |
| `not-dark:` | Light mode active (alias) |
| `not-light:` | Dark mode active (alias) |

Dark mode strategy set in `ThemeProvider` or config:
- `'attribute'` (default) — `[data-theme="dark"]` on a wrapper element
- `'class'` — `.dark` class on a wrapper element
- `'media'` — `@media (prefers-color-scheme: dark)`

### Interaction modifiers
| Modifier | Triggers on |
|---|---|
| `hover:` | Mouse hover |
| `focus:` | Element focused |
| `focus-within:` | Focus anywhere inside element |
| `focus-visible:` | Keyboard focus ring |
| `active:` | Active state |
| `pressed:` | Click / touch pressed |
| `visited:` | Visited link |
| `disabled:` | Disabled element |
| `checked:` | Checkbox / radio checked |
| `placeholder:` | Input placeholder text |

Negated: `not-hover:`, `not-focus:`, `not-active:`, `not-pressed:`, `not-visited:`, `not-disabled:`, `not-checked:`

### Structural modifiers
| Modifier | Pseudo-class |
|---|---|
| `first:` | `:first-child` |
| `last:` | `:last-child` |
| `odd:` | `:nth-child(odd)` |
| `even:` | `:nth-child(even)` |
| `only:` | `:only-child` |

### Responsive modifiers
| Modifier | Min-width |
|---|---|
| `sm:` | 576 px |
| `md:` | 768 px |
| `lg:` | 1024 px |
| `xl:` | 1280 px |
| `2xl:` | 1536 px |

Responsive styles are handled via `@media (min-width)` CSS rules — no JS breakpoint tracking.

### Group / peer modifiers
Mark a parent with `group`, then use `group-hover:` etc. on children.

```jsx
<div className="group">
  <span className="opacity-0 group-hover:opacity-100 transition" />
</div>
```

| Modifier | Fires when |
|---|---|
| `group-hover:` | Ancestor `.group` is hovered |
| `group-focus:` | Ancestor `.group` is focused |
| `peer-hover:` | Previous sibling `.peer` is hovered |
| `peer-focus:` | Previous sibling `.peer` is focused |

**Named groups/peers** — nested groups need names to avoid an inner element
reacting to the wrong (nearest) ancestor: `group/{name}` + `group-hover/{name}:`,
same for `peer/{name}` + `peer-hover/{name}:`/`peer-focus/{name}:`.
```jsx
<div className="group/card">
  <div className="group/icon">
    <span className="group-hover/icon:opacity-100" />
  </div>
  <span className="group-hover/card:underline" />
</div>
```

### Pseudo-element modifiers
```jsx
<div className="before:content-['*'] before:text-red-6 relative" />
<p className="first-letter:text-4xl first-letter:font-bold" />
<p className="selection:bg-blue-3" />
<input className="placeholder:text-gray-5" />
```

| Modifier | CSS selector |
|---|---|
| `before:` | `::before` |
| `after:` | `::after` |
| `selection:` | `::selection` |
| `first-letter:` | `::first-letter` |
| `first-line:` | `::first-line` |
| `marker:` | `::marker` |
| `placeholder:` | `::placeholder` |

### Print modifier
```jsx
<div className="print:hidden" />
<div className="print:text-black print:bg-white" />
```

### Orientation modifiers
| Modifier | Media query |
|---|---|
| `landscape:` | `@media (orientation: landscape)` |
| `portrait:` | `@media (orientation: portrait)` |

### Accessibility modifiers
| Modifier | Media query |
|---|---|
| `motion-reduce:` | `@media (prefers-reduced-motion: reduce)` |
| `motion-safe:` | `@media (prefers-reduced-motion: no-preference)` |
| `contrast-more:` | `@media (prefers-contrast: more)` |
| `contrast-less:` | `@media (prefers-contrast: less)` |

### Directionality modifiers
| Modifier | CSS selector scope |
|---|---|
| `rtl:` | `[dir="rtl"] .cls` |
| `ltr:` | `[dir="ltr"] .cls` |

### Important modifier
Prefix any class with `!` to add `!important` to every CSS declaration.
```jsx
<div className="!p-0 !m-0 !bg-transparent" />
```

---

## Arbitrary Values

Wrap any value in `[]` to use it directly.
```jsx
<div className="bg-[#6366f1]" />
<div className="p-[14px]" />
<div className="w-[calc(100%-2rem)]" />
<div className="text-[18px]" />
<div className="rounded-[20px]" />
<div className="bg-[rgba(99,102,241,0.15)]" />
<div className="grid-cols-[1fr_2fr_1fr]" />
```

---

## Negative Values
```jsx
<div className="-mt-4" />         // marginTop: -16
<div className="-mx-2" />         // marginHorizontal: -8
<div className="-translate-x-2" />
<div className="-mt-[10px]" />    // marginTop: -10px
```

---

## Color with Opacity
```jsx
<div className="bg-blue-6/50" />     // 50% opacity
<div className="text-gray-10/75" />  // 75% opacity
<div className="bg-black/[0.15]" />  // arbitrary opacity
```

---

## Color System

### 12-shade scale
1 = lightest, 12 = darkest.

```
shade  1   2   3   4   5   6   7   8   9  10  11  12
       ─────────────────────────────────────────────
       light                                    dark
```

Usage: `bg-blue-6`, `text-gray-10`, `border-red-4/50`

### Color families (22 total)
Grays: `slate`, `gray`, `zinc`, `neutral`, `stone`
Colors: `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`
Special: `transparent`, `current` (currentColor), `black`, `white`

---

## Utility Reference

### Background
```
bg-{color}           backgroundColor
bg-{color}/{opacity} backgroundColor with alpha
bg-transparent
bg-clip-border/padding/content/text
bg-gradient-to-{dir} linear gradient (t, tr, r, br, b, bl, l, tl)
  use with: from-{color}, via-{color}, to-{color}
bg-none/auto/cover/contain
bg-center/top/bottom/left/right/left-top/…
bg-repeat/no-repeat/repeat-x/repeat-y
bg-fixed/local/scroll
bg-blend-{mode}      normal, multiply, screen, overlay, darken, lighten, …
```

### Text
```
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)
text-{color}
text-left/right/center/justify/start/end
text-wrap/nowrap/balance/pretty
```

### Font
```
font-thin/extralight/light/normal/medium/semibold/bold/extrabold/black
font-{family}    sans, mono, serif, or custom
```

### Text decoration
```
underline / overline / line-through / no-underline
decoration-{color}
decoration-solid/dashed/dotted/double/wavy
decoration-0/1/2/4/8/auto/from-font
underline-offset-0/1/2/4/8/auto
```

### Text transform
```
uppercase / lowercase / capitalize / normal-case
italic / not-italic
```

### Text overflow
```
truncate
overflow-ellipsis
line-clamp-{n}     n = 1–20
line-clamp-none
whitespace-normal/nowrap/pre/pre-wrap/pre-line
break-normal/words/all
```

### Typography misc
```
leading-none/tight/snug/normal/relaxed/loose (+ numeric 3–10)
tracking-tighter/tight/normal/wide/wider/widest
antialiased / subpixel-antialiased
```

### Spacing — Padding
```
p-{n}  px-{n}  py-{n}  pt-{n}  pr-{n}  pb-{n}  pl-{n}
```

### Spacing — Margin
```
m-{n}  mx-{n}  my-{n}  mt-{n}  mr-{n}  mb-{n}  ml-{n}
mx-auto   (centers element)
```

Spacing scale (1 unit = 4px):
`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`

### Sizing
```
w-{n}  h-{n}  size-{n}  min-w-{n}  min-h-{n}  max-w-{n}  max-h-{n}

Named max-w: none xs(320) sm(384) md(448) lg(512) xl(576) 2xl(672) 3xl(768)
             4xl(896) 5xl(1024) 6xl(1152) 7xl(1280) prose(65ch)

Screen (dvw/dvh — correct on mobile where browser chrome resizes the
visible viewport; vw/vh are pinned to the largest viewport and overflow
behind a shown address bar): w-screen(100dvw)  h-screen(100dvh)
         min-w-screen  max-w-screen  min-h-screen  max-h-screen
```

### Display
```
block / inline / inline-block / flex / inline-flex
grid / inline-grid / hidden / contents / flow-root / table
```

### Flex
```
flex-row/col/row-reverse/col-reverse
flex-wrap/nowrap/wrap-reverse
flex-1 / flex-auto / flex-initial / flex-none
flex-grow/grow-0  flex-shrink/shrink-0
basis-{n}
items-start/end/center/baseline/stretch
justify-start/end/center/between/around/evenly
justify-items-start/end/center/stretch
justify-self-start/end/center/auto
self-start/end/center/auto/stretch/baseline
content-start/end/center/between/around/evenly/stretch
order-{n}
gap-{n}  gap-x-{n}  gap-y-{n}
```

### Grid
```
grid-cols-{n}        repeat(n, minmax(0, 1fr))   n = 1–12
grid-rows-{n}
grid-flow-row/col/dense/row-dense/col-dense
auto-cols-auto/min/max/fr
auto-rows-auto/min/max/fr
col-span-{n} / col-span-full
col-start-{n}/auto   col-end-{n}/auto
row-span-{n} / row-span-full
row-start-{n}/auto   row-end-{n}/auto
place-items-start/end/center/stretch
place-content-start/end/center/between/around/evenly/stretch
place-self-start/end/center/auto/stretch
```

### Position
```
static / relative / absolute / fixed / sticky
inset-{n}  inset-x-{n}  inset-y-{n}
top-{n}  right-{n}  bottom-{n}  left-{n}
z-0/10/20/30/40/50/auto
```

### Overflow
```
overflow-hidden/visible/scroll/auto/clip
overflow-x-hidden/visible/scroll/auto/clip
overflow-y-hidden/visible/scroll/auto/clip
```

### Border
```
border / border-{n}         borderWidth: 0 1 2 4 8
border-t/r/b/l
border-{color}
border-solid/dashed/dotted/none
border-collapse / border-separate
rounded / rounded-none/sm/md/lg/xl/2xl/3xl/full
rounded-t/r/b/l  rounded-tl/tr/bl/br
```

### Shadow
```
shadow-sm / shadow / shadow-md / shadow-lg / shadow-xl / shadow-2xl / shadow-none
```

### Opacity
```
opacity-0/5/10/15/20/25/30/40/50/60/70/75/80/90/95/100
```

### Ring
Web: box-shadow ring. Native: approximated via borderWidth/borderColor
(no box-shadow on RN) — affects layout there and shares properties with
`border-*` (last class wins if both are used). `ring-offset-*`/`ring-inset`
stay web-only, with no native equivalent.
```
ring / ring-{n}(0 1 2 4 8)
ring-{color}
ring-inset            (web only)
ring-offset-{n}(0 1 2 4 8)  (web only)
```

### Outline
```
outline-none / outline / outline-{n}(0 1 2 4 8)
outline-{color}
outline-offset-{n}(0 1 2 4 8)
```

### Transforms
```
scale-{n}  scale-x-{n}  scale-y-{n}
rotate-{n}
translate-x-{n}  translate-y-{n}
skew-x-{n}  skew-y-{n}
origin-center/top/top-right/right/bottom-right/bottom/bottom-left/left/top-left
perspective-{n}
```

### Filters
```
blur-{sm/md/lg/xl/2xl/3xl}
brightness-{n}  contrast-{n}
grayscale / grayscale-0
hue-rotate-{n}
invert / invert-0
saturate-{n}
sepia / sepia-0
drop-shadow-{sm/md/lg/xl/2xl/none}

backdrop-blur-{n}  backdrop-brightness-{n}  backdrop-contrast-{n}
backdrop-grayscale  backdrop-hue-rotate-{n}  backdrop-invert
backdrop-opacity-{n}  backdrop-saturate-{n}  backdrop-sepia
```

### Animation & Transition
```
animate-spin / animate-ping / animate-pulse / animate-bounce / animate-none
transition / transition-all/none/colors/opacity/shadow/transform
duration-75/100/150/200/300/500/700/1000
delay-75/100/150/200/300/500/700/1000
ease-linear/in/out/in-out         ease-[cubic-bezier(...)]
```

### Cursor
```
cursor-auto/default/pointer/wait/text/move/not-allowed
cursor-grab/grabbing/zoom-in/zoom-out/crosshair/help/none
```

### Pointer events / User select
```
pointer-events-none / pointer-events-auto
select-none / select-text / select-all / select-auto
```

### Touch action
```
touch-auto / touch-none / touch-pan-x / touch-pan-y
touch-pan-left / touch-pan-right / touch-pan-up / touch-pan-down
touch-pinch-zoom / touch-manipulation
```

### Scroll
```
scroll-smooth / scroll-auto
```

### Float & Clear
```
float-left / float-right / float-start / float-end / float-none
clear-left / clear-right / clear-both / clear-start / clear-end / clear-none
```

### Vertical align
```
align-baseline / align-top / align-middle / align-bottom
align-text-top / align-text-bottom / align-sub / align-super
```

### Visibility
```
visible / invisible
sr-only / not-sr-only
```

### Lists
```
list-none / list-disc / list-decimal
list-inside / list-outside
```

### Misc
```
appearance-none
resize / resize-none / resize-x / resize-y
box-border / box-content
object-contain/cover/fill/none/scale-down
object-center/top/bottom/left/right/…
aspect-auto / aspect-square / aspect-video / aspect-[4/3]
columns-{n} / columns-auto / columns-{size}
caret-{color} / caret-auto / caret-transparent
accent-{color} / accent-auto
stroke-{color} / stroke-{n} / stroke-none  (web only, SVG)
fill-{color} / fill-none                    (web only, SVG)
mix-blend-{mode}
bg-blend-{mode}
will-change-auto/scroll/contents/transform
divide-x-{n} / divide-y-{n} / divide-{color} / divide-solid/dashed/dotted
space-x-{n} / space-y-{n}
group / peer   (standalone marker classes)
```

---

## Theme Configuration

### kbach.config.js (project root)
```js
module.exports = {
  darkMode: 'attribute', // 'attribute' | 'class' | 'media'

  theme: {
    colors: {
      brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a8a' },
    },
  },

  extend: {
    theme: {
      colors: { brand: { 6: '#6366f1' } },
      spacing: { 18: 72, 22: 88 },
      fontSize: { '10xl': 160 },
      fontFamily: {
        sans: 'Inter, sans-serif',
      },
    },
  },

  plugins: [
    ({ addUtility, theme }) => {
      addUtility('border-brand', {
        borderColor: theme('colors.brand.6'),
        borderWidth: 2,
      });
    },
  ],
};
```

**Global default font (web):** Setting `fontFamily.sans` to anything other than `'System'` auto-injects `body { font-family: <font> }`.

### Mode-aware colors (dark mode without `dark:`)

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:

```js
extend: {
  theme: {
    colors: {
      surface: { light: '#ffffff', dark: '#111827' },
      // each side can itself be an alias — resolved independently
      accent:  { light: 'blue-6', dark: 'blue-4' },
      // or derived from another color at an opacity — 'name/opacity', resolved
      // once here instead of only being computable at runtime via colors.alpha()
      accentSoft: { light: 'accent/30', dark: 'accent/40' },
    },
  },
},
```

```jsx
<div className="bg-surface text-accent hover:bg-accentSoft" />
// equivalent to writing bg-white dark:bg-gray-9 text-blue-6 dark:text-blue-4
// hover:bg-[rgba(...)] dark:hover:bg-[rgba(...)] by hand
```

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).

### Runtime update
```js
import { updateConfig, clearCache } from '@kbach/react';
updateConfig({ extend: { theme: { colors: { brand: { 6: '#6366f1' } } } } });
clearCache(); // always call after updateConfig()
```

### Default theme values
```
spacing:      1 unit = 4px
fontSize:     xs(12)–9xl(128)
borderRadius: none(0) sm(2) DEFAULT(4) md(6) lg(8) xl(12) 2xl(16) 3xl(24) full(9999)
borderWidth:  DEFAULT(1) 0 2 4 8
opacity:      0 5 10 15 20 25 30 40 50 60 70 75 80 90 95 100
lineHeight:   none(1) tight(1.25) snug(1.375) normal(1.5) relaxed(1.625) loose(2)
letterSpacing:tighter(-0.8) tight(-0.4) normal(0) wide(0.4) wider(0.8) widest(1.6)
zIndex:       auto 0 10 20 30 40 50
screens:      sm(576) md(768) lg(1024) xl(1280) 2xl(1536)
```

---

## Common Patterns

### Dark mode card
```jsx
<div className="bg-white dark:bg-gray-9 rounded-2xl p-6 shadow-md">
  <h2 className="text-2xl font-bold text-gray-10 dark:text-white">Title</h2>
  <p className="text-gray-6 dark:text-gray-4 mt-2">Body text</p>
</div>
```

### Interactive button
```jsx
<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" />
```

### Responsive layout
```jsx
<div className="flex flex-col md:flex-row gap-4">
  <aside className="w-full md:w-64 lg:w-80">…</aside>
  <main className="flex-1">…</main>
</div>
```

### Responsive grid
```jsx
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
  {items.map(item => <Card key={item.id} />)}
</div>
```

### Group hover reveal
```jsx
<div className="group relative overflow-hidden rounded-xl">
  <img src="…" className="transition group-hover:scale-105" />
  <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center">
    <span className="text-white font-bold">View</span>
  </div>
</div>
```

### Before/after pseudo-elements
```jsx
<div className="relative before:absolute before:inset-0 before:bg-blue-6/10 before:rounded-xl" />
```

### Input with caret and focus ring
```jsx
<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" />
```

### Print-specific styles
```jsx
<nav className="print:hidden" />
<article className="print:text-black print:bg-white print:shadow-none" />
```

### Reduced-motion safe animation
```jsx
<div className="motion-safe:animate-spin motion-reduce:opacity-75" />
```

### RTL-aware spacing
```jsx
<div className="ltr:pl-4 rtl:pr-4 ltr:text-left rtl:text-right" />
```

### Contrast accessibility
```jsx
<button className="bg-blue-6 contrast-more:bg-blue-9 contrast-more:border-2 text-white">
  Submit
</button>
```

---

## Caching

The resolver uses an LRU cache (10,000 entries). Cleared automatically on `updateConfig()`. Manually:
```js
import { clearCache } from '@kbach/react';
clearCache();
```
