@kbach/react 0.6.14 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -262
- package/dist/{chunk-HCRWLOJE.mjs → chunk-GJSZUC6Q.mjs} +83 -48
- package/dist/{chunk-7DHBNCL5.mjs → chunk-YUX2JPTM.mjs} +12 -10
- package/dist/index.js +143 -65
- package/dist/index.mjs +67 -18
- package/dist/jsx-dev-runtime.js +84 -60
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.js +84 -60
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/vite-plugin.js +25 -29
- package/dist/vite-plugin.mjs +25 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kbach/react
|
|
2
2
|
|
|
3
|
-
Tailwind-like utility classes for React (web). Write
|
|
3
|
+
Tailwind-like utility classes for React (web). Write `className` strings — a custom JSX runtime resolves them at render time. An optional Vite plugin outputs a static `kbach.css` for zero runtime cost.
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
<div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
|
|
@@ -12,36 +12,37 @@ Tailwind-like utility classes for React (web). Write classes as `className` stri
|
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
|
-
[npm package](https://www.npmjs.com/package/@kbach/react)
|
|
16
|
-
|
|
17
15
|
```
|
|
18
16
|
npm install @kbach/react
|
|
19
17
|
```
|
|
20
18
|
|
|
19
|
+
[npm package](https://www.npmjs.com/package/@kbach/react)
|
|
20
|
+
|
|
21
21
|
## Setup
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Two steps: configure the JSX runtime (always), then pick one styling method.
|
|
24
24
|
|
|
25
|
-
### 1
|
|
25
|
+
### Step 1 — JSX runtime
|
|
26
26
|
|
|
27
|
-
**tsconfig.json
|
|
27
|
+
**tsconfig.json:**
|
|
28
28
|
|
|
29
29
|
```json
|
|
30
|
-
{
|
|
31
|
-
"compilerOptions": {
|
|
32
|
-
"jsx": "react-jsx",
|
|
33
|
-
"jsxImportSource": "@kbach/react"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
30
|
+
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@kbach/react" } }
|
|
36
31
|
```
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
That's the only setting needed — Vite, Next.js, and React Router all read it. Don't *also* set `jsxImportSource` on a bundler plugin (e.g. `@vitejs/plugin-react`); one source of truth avoids conflicts.
|
|
39
34
|
|
|
40
|
-
|
|
35
|
+
### Step 2 — Styling method
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
Not sure which? Check your framework first:
|
|
43
38
|
|
|
44
|
-
|
|
39
|
+
| Framework | Use |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Next.js | **Quick setup** — Static CSS doesn't apply (webpack/Turbopack, not Vite) |
|
|
42
|
+
| React Router, framework mode | **Static CSS setup** — and skip `@vitejs/plugin-react`, see note below |
|
|
43
|
+
| Vite, React Router library mode, CRA, other | Either — Quick is faster to try, Static CSS is zero-cost for production |
|
|
44
|
+
|
|
45
|
+
#### Quick setup — any bundler, client-side CSS injection
|
|
45
46
|
|
|
46
47
|
```jsx
|
|
47
48
|
import { ThemeProvider, KbachReset } from '@kbach/react';
|
|
@@ -56,32 +57,20 @@ export default function Root() {
|
|
|
56
57
|
}
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
`<KbachReset />` renders
|
|
60
|
-
|
|
61
|
-
That's the whole setup. `@kbach/react` ships its own `"use client"` directive, so both of these work directly in a Next.js App Router Server Component tree — no manual client wrapper needed.
|
|
60
|
+
Done. `<KbachReset />` renders the base reset (see [CSS resets](#css-resets)) as real markup instead of waiting on client JS — matters most for SSR, where it avoids a flash of unstyled browser defaults before hydration. `@kbach/react` ships its own `"use client"` directive, so this works in a Next.js Server Component tree with no wrapper needed — in Next.js, render it once in the root `layout.tsx`.
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
#### Static CSS setup — Vite only, zero runtime cost
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
A build-time plugin writes real CSS into a file you import — nothing generated client-side.
|
|
66
65
|
|
|
67
66
|
```ts
|
|
68
67
|
// vite.config.ts
|
|
69
68
|
import { defineConfig } from 'vite';
|
|
70
69
|
import { kbach } from '@kbach/react/vite';
|
|
71
70
|
|
|
72
|
-
export default defineConfig({
|
|
73
|
-
plugins: [
|
|
74
|
-
// ...your framework's own plugin(s) — see the framework notes below
|
|
75
|
-
// before adding @vitejs/plugin-react yourself.
|
|
76
|
-
kbach(),
|
|
77
|
-
],
|
|
78
|
-
});
|
|
71
|
+
export default defineConfig({ plugins: [kbach()] });
|
|
79
72
|
```
|
|
80
73
|
|
|
81
|
-
Adding the plugin also declares `@kbach/react` in Vite's `optimizeDeps.include` upfront, so Vite pre-bundles it during its initial dependency scan instead of discovering it lazily on the first page that imports it — avoiding a forced full-reload mid-render, which is what a rare "Invalid hook call"-style crash on first load (but never again after) usually turns out to be.
|
|
82
|
-
|
|
83
|
-
Then create an empty `kbach.css` anywhere in your project and import it once in your app entry:
|
|
84
|
-
|
|
85
74
|
```css
|
|
86
75
|
/* src/kbach.css */
|
|
87
76
|
/* kbach:start */
|
|
@@ -95,142 +84,106 @@ import './kbach.css';
|
|
|
95
84
|
|
|
96
85
|
```jsx
|
|
97
86
|
import { ThemeProvider } from '@kbach/react';
|
|
98
|
-
// No <KbachReset />
|
|
87
|
+
// No <KbachReset /> — kbach.css already includes the reset.
|
|
99
88
|
|
|
100
89
|
export default function Root() {
|
|
101
|
-
return
|
|
102
|
-
<ThemeProvider defaultMode="system">
|
|
103
|
-
<App />
|
|
104
|
-
</ThemeProvider>
|
|
105
|
-
);
|
|
90
|
+
return <ThemeProvider defaultMode="system"><App /></ThemeProvider>;
|
|
106
91
|
}
|
|
107
92
|
```
|
|
108
93
|
|
|
109
|
-
The plugin scans your source
|
|
94
|
+
The plugin scans your source at build time and writes CSS between the markers — importing `kbach.css` auto-disables runtime injection. It also warns in the terminal (with a clickable `file:line`) for any class it doesn't recognize as a real utility or an existing CSS rule elsewhere in the project — usually a typo.
|
|
110
95
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
## Next.js
|
|
114
|
-
|
|
115
|
-
Step 1 (tsconfig `jsxImportSource`) above applies as-is — Next.js's SWC compiler reads `jsxImportSource` from `tsconfig.json` the same way Vite does. For step 2, use **Quick setup (2a)** — Next.js builds with webpack/Turbopack, not Vite, so the static-CSS plugin (2b) doesn't apply. Render `<KbachReset />` once in the root App Router `layout.tsx` (inside `<head>`, or right after `<ThemeProvider>` opens) so Server Component HTML gets the base reset without waiting on hydration; `@kbach/react` shipping its own `"use client"` directive means both `ThemeProvider` and `KbachReset` work directly in that Server Component tree with no wrapper of your own needed.
|
|
116
|
-
|
|
117
|
-
## React Router
|
|
118
|
-
|
|
119
|
-
**Framework mode (v7+, SSR by default):** `@react-router/dev`'s own `reactRouter()` Vite plugin already includes its own React JSX transform and Fast Refresh integration — **do not also add `@vitejs/plugin-react`.** Having both active makes each one inject its own Fast Refresh preamble into the same module, which crashes the page at runtime (`Identifier 'RefreshRuntime' has already been declared`) before React ever hydrates — every class on the page will silently fail to style because the app never actually mounts. The correct setup is just:
|
|
96
|
+
**React Router framework mode:** don't add `@vitejs/plugin-react` — `reactRouter()` already provides JSX handling, and both together crash the page (`Identifier 'RefreshRuntime' has already been declared`).
|
|
120
97
|
|
|
121
98
|
```ts
|
|
122
99
|
// vite.config.ts
|
|
123
100
|
import { reactRouter } from '@react-router/dev/vite';
|
|
124
101
|
import { defineConfig } from 'vite';
|
|
125
|
-
import { kbach } from '@kbach/react/vite'; // omit if
|
|
102
|
+
import { kbach } from '@kbach/react/vite'; // omit if using Quick setup instead
|
|
126
103
|
|
|
127
|
-
export default defineConfig({
|
|
128
|
-
plugins: [kbach(), reactRouter()],
|
|
129
|
-
});
|
|
104
|
+
export default defineConfig({ plugins: [kbach(), reactRouter()] });
|
|
130
105
|
```
|
|
131
106
|
|
|
132
|
-
|
|
107
|
+
(React Router library mode — `createBrowserRouter`, no SSR — has no such conflict; set it up like any Vite + React app.)
|
|
108
|
+
|
|
109
|
+
## Dark mode
|
|
133
110
|
|
|
134
|
-
|
|
111
|
+
`<ThemeProvider>` powers every `dark:` class — detects OS color scheme, persists the user's choice, re-renders on change.
|
|
135
112
|
|
|
136
|
-
|
|
113
|
+
```jsx
|
|
114
|
+
<ThemeProvider
|
|
115
|
+
defaultMode="system" // 'light' | 'dark' | 'system'
|
|
116
|
+
disablePersistence={false} // true = don't remember across reloads
|
|
117
|
+
>
|
|
118
|
+
<App />
|
|
119
|
+
</ThemeProvider>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| `defaultMode` | `'light' \| 'dark' \| 'system'` | `'system'` | Starting mode |
|
|
125
|
+
| `disablePersistence` | `boolean` | `false` | Skip saving to `localStorage` |
|
|
126
|
+
| `config` | `FrameworkConfig` | global config | Scope a different config to this subtree |
|
|
137
127
|
|
|
138
|
-
|
|
128
|
+
`darkMode` in `kbach.config.js` picks the matching strategy: `'attribute'` (default), `'class'`, or `'media'` (system-only). Toggle it with `useTheme()`'s `toggle()`/`setMode()` — see [API](#api).
|
|
139
129
|
|
|
140
130
|
## API
|
|
141
131
|
|
|
142
|
-
### className / kb
|
|
132
|
+
### className / kb
|
|
143
133
|
|
|
144
|
-
|
|
134
|
+
`kb` is an alias for `className` — works on any element.
|
|
145
135
|
|
|
146
136
|
```jsx
|
|
147
|
-
<div className="bg-white dark:bg-gray-10 p-4 rounded-xl" />
|
|
148
|
-
<p kb="text-gray-10 dark:text-white text-lg font-bold" />
|
|
149
137
|
<button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-9 rounded-lg px-4 py-2" />
|
|
150
138
|
```
|
|
151
139
|
|
|
152
140
|
### styled(Component, classes)
|
|
153
141
|
|
|
154
|
-
Pre-style any component. Returns a new component that merges the base classes with any `kb` prop passed at use time.
|
|
155
|
-
|
|
156
142
|
```jsx
|
|
157
143
|
import { styled } from '@kbach/react';
|
|
158
144
|
|
|
159
145
|
const Card = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
|
|
160
|
-
const Button = styled(
|
|
161
|
-
'button',
|
|
162
|
-
'bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-xl px-6 py-3'
|
|
163
|
-
);
|
|
146
|
+
const Button = styled('button', 'bg-blue-7 hover:bg-blue-8 rounded-xl px-6 py-3');
|
|
164
147
|
|
|
165
148
|
<Card kb="mt-4">
|
|
166
149
|
<Button kb="w-full">Submit</Button>
|
|
167
150
|
</Card>
|
|
168
151
|
```
|
|
169
152
|
|
|
170
|
-
|
|
153
|
+
Extra classes at use time via `kb` merge with the base classes.
|
|
171
154
|
|
|
172
|
-
|
|
155
|
+
### cx(...classes)
|
|
173
156
|
|
|
174
157
|
```jsx
|
|
175
158
|
import { cx } from '@kbach/react';
|
|
176
159
|
|
|
177
|
-
<div className={cx(
|
|
178
|
-
'p-4 rounded-xl',
|
|
179
|
-
isSelected && 'border-2 border-blue-6',
|
|
180
|
-
isDisabled && 'opacity-50',
|
|
181
|
-
)} />
|
|
160
|
+
<div className={cx('p-4 rounded-xl', isSelected && 'border-2 border-blue-6', isDisabled && 'opacity-50')} />
|
|
182
161
|
```
|
|
183
162
|
|
|
184
|
-
|
|
163
|
+
Falsy values ignored. Also works as pre-built style constants:
|
|
185
164
|
|
|
186
165
|
```ts
|
|
187
|
-
// styles.ts
|
|
188
|
-
import { cx } from '@kbach/react';
|
|
189
|
-
|
|
190
166
|
export const container = cx('flex-1 bg-white dark:bg-gray-9 p-4');
|
|
191
|
-
export const heading = cx('text-2xl font-bold text-gray-10 dark:text-white');
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
```jsx
|
|
195
|
-
import { container, heading } from './styles';
|
|
196
|
-
|
|
197
|
-
<div className={container}>
|
|
198
|
-
<h1 className={heading}>Hello</h1>
|
|
199
|
-
</div>
|
|
200
167
|
```
|
|
201
168
|
|
|
202
169
|
### useStyles(classes, state?)
|
|
203
170
|
|
|
204
|
-
Resolve classes to a style object inside a component.
|
|
205
|
-
|
|
206
171
|
```jsx
|
|
207
|
-
import { useStyles } from '@kbach/react';
|
|
208
|
-
|
|
209
172
|
const style = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');
|
|
210
|
-
|
|
211
|
-
// Multiple strings merged left-to-right
|
|
212
|
-
const style = useStyles(['bg-white p-4', 'dark:bg-gray-9 rounded-xl']);
|
|
213
|
-
|
|
214
|
-
// With interaction state
|
|
215
|
-
const [pressed, setPressed] = useState(false);
|
|
216
|
-
const style = useStyles('bg-blue-5 pressed:bg-blue-7 rounded-lg', { pressed });
|
|
173
|
+
const style2 = useStyles('bg-blue-5 pressed:bg-blue-7 rounded-lg', { pressed });
|
|
217
174
|
```
|
|
218
175
|
|
|
219
176
|
### kb(classes)
|
|
220
177
|
|
|
221
|
-
Resolve
|
|
178
|
+
Resolve outside a component:
|
|
222
179
|
|
|
223
180
|
```js
|
|
224
|
-
import { kb } from '@kbach/react';
|
|
225
|
-
|
|
226
181
|
const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
|
|
227
182
|
```
|
|
228
183
|
|
|
229
184
|
### useTheme()
|
|
230
185
|
|
|
231
186
|
```js
|
|
232
|
-
import { useTheme } from '@kbach/react';
|
|
233
|
-
|
|
234
187
|
const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
|
|
235
188
|
```
|
|
236
189
|
|
|
@@ -238,164 +191,74 @@ const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
|
|
|
238
191
|
|---|---|---|
|
|
239
192
|
| `mode` | `'light' \| 'dark' \| 'system'` | User-selected mode |
|
|
240
193
|
| `resolvedMode` | `'light' \| 'dark'` | Resolved after system lookup |
|
|
241
|
-
| `isDark` | `boolean` |
|
|
194
|
+
| `isDark` | `boolean` | `resolvedMode === 'dark'` |
|
|
242
195
|
| `setMode` | `fn` | Set mode explicitly |
|
|
243
|
-
| `toggle` | `fn` | Toggle
|
|
196
|
+
| `toggle` | `fn` | Toggle light/dark |
|
|
244
197
|
| `config` | `ResolvedConfig` | Full resolved config |
|
|
245
198
|
|
|
246
|
-
### useIsDark()
|
|
199
|
+
### useIsDark() / useColors()
|
|
247
200
|
|
|
248
201
|
```js
|
|
249
|
-
import { useIsDark } from '@kbach/react';
|
|
250
202
|
const isDark = useIsDark();
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### useColors()
|
|
254
|
-
|
|
255
|
-
Returns the active theme's color palette as a smart proxy.
|
|
256
|
-
|
|
257
|
-
```js
|
|
258
|
-
import { useColors } from '@kbach/react';
|
|
259
203
|
|
|
260
204
|
const colors = useColors();
|
|
261
205
|
colors.blue[6] // '#3b82f6'
|
|
262
206
|
colors.blue['6/50'] // 'rgba(59,130,246,0.5)'
|
|
263
|
-
colors.white // '#ffffff'
|
|
264
|
-
colors['white/20'] // 'rgba(255,255,255,0.2)'
|
|
265
207
|
colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
|
|
266
208
|
```
|
|
267
209
|
|
|
268
210
|
## Modifiers
|
|
269
211
|
|
|
270
|
-
Chain
|
|
271
|
-
|
|
272
|
-
```jsx
|
|
273
|
-
<div className="dark:sm:hover:p-4" />
|
|
274
|
-
<input className="focus:ring-2 focus:ring-blue-5 focus:outline-none" />
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### Theme
|
|
212
|
+
Chain in any order: `<div className="dark:sm:hover:p-4" />`
|
|
278
213
|
|
|
279
|
-
|
|
|
214
|
+
| Category | Modifiers |
|
|
280
215
|
|---|---|
|
|
281
|
-
| `dark:`
|
|
282
|
-
| `
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
|
287
|
-
|---|---|
|
|
288
|
-
| `hover:` | Mouse hover |
|
|
289
|
-
| `focus:` | Element focused |
|
|
290
|
-
| `pressed:` | Click / touch down |
|
|
291
|
-
| `active:` | Active state |
|
|
292
|
-
| `disabled:` | Disabled |
|
|
293
|
-
| `checked:` | Checkbox / radio checked |
|
|
294
|
-
| `visited:` | Visited link |
|
|
295
|
-
| `placeholder:` | Placeholder text |
|
|
296
|
-
|
|
297
|
-
Negated: `not-hover:`, `not-focus:`, `not-pressed:`, `not-active:`, `not-disabled:`, `not-checked:`
|
|
298
|
-
|
|
299
|
-
### Structural
|
|
300
|
-
|
|
301
|
-
`first:` `last:` `odd:` `even:` `only:` `focus-within:` `focus-visible:`
|
|
302
|
-
|
|
303
|
-
### Pseudo-elements
|
|
304
|
-
|
|
305
|
-
`before:` `after:` `selection:` `first-letter:` `first-line:` `marker:` `placeholder:`
|
|
216
|
+
| Theme | `dark:` `light:` / `not-dark:` |
|
|
217
|
+
| Interactive | `hover:` `focus:` `pressed:` `active:` `disabled:` `checked:` `visited:` `placeholder:` (all have `not-` variants) |
|
|
218
|
+
| Structural | `first:` `last:` `odd:` `even:` `only:` `focus-within:` `focus-visible:` |
|
|
219
|
+
| Pseudo-elements | `before:` `after:` `selection:` `first-letter:` `first-line:` `marker:` |
|
|
220
|
+
| Responsive | `sm:`(576px) `md:`(768px) `lg:`(1024px) `xl:`(1280px) `2xl:`(1536px) |
|
|
221
|
+
| Other | `print:` `landscape:`/`portrait:` `motion-reduce:`/`motion-safe:` `contrast-more:`/`contrast-less:` `rtl:`/`ltr:` `!` (important) |
|
|
306
222
|
|
|
307
223
|
```jsx
|
|
308
224
|
<div className="before:content-['*'] before:text-red-6 relative" />
|
|
309
|
-
<p className="selection:bg-blue-3" />
|
|
310
|
-
<input className="placeholder:text-gray-5" />
|
|
311
225
|
```
|
|
312
226
|
|
|
313
|
-
|
|
227
|
+
**Group / peer:**
|
|
314
228
|
|
|
315
229
|
```jsx
|
|
316
230
|
<div className="group">
|
|
317
231
|
<span className="opacity-0 group-hover:opacity-100 transition" />
|
|
318
232
|
</div>
|
|
319
|
-
<input className="peer" />
|
|
320
|
-
<p className="peer-focus:text-blue-6" />
|
|
321
233
|
```
|
|
322
234
|
|
|
323
|
-
Nested groups need names
|
|
324
|
-
|
|
325
|
-
```jsx
|
|
326
|
-
<div className="group/card">
|
|
327
|
-
<div className="group/icon">
|
|
328
|
-
<span className="group-hover/icon:opacity-100" /> {/* only the inner group */}
|
|
329
|
-
</div>
|
|
330
|
-
<span className="group-hover/card:underline" /> {/* only the outer group */}
|
|
331
|
-
</div>
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Responsive
|
|
335
|
-
|
|
336
|
-
| Modifier | Min-width |
|
|
337
|
-
|---|---|
|
|
338
|
-
| `sm:` | 576 px |
|
|
339
|
-
| `md:` | 768 px |
|
|
340
|
-
| `lg:` | 1024 px |
|
|
341
|
-
| `xl:` | 1280 px |
|
|
342
|
-
| `2xl:` | 1536 px |
|
|
343
|
-
|
|
344
|
-
### Other
|
|
345
|
-
|
|
346
|
-
| Modifier | Effect |
|
|
347
|
-
|---|---|
|
|
348
|
-
| `print:hidden` | Visible only on screen |
|
|
349
|
-
| `landscape:` `portrait:` | Orientation |
|
|
350
|
-
| `motion-reduce:` `motion-safe:` | Reduced-motion preference |
|
|
351
|
-
| `contrast-more:` `contrast-less:` | Contrast preference |
|
|
352
|
-
| `rtl:` `ltr:` | Text direction |
|
|
353
|
-
| `!p-0` | `!important` on every declaration |
|
|
235
|
+
Nested groups need names (`group/card`, `group-hover/card:`) or the inner element reacts to whichever `.group` is nearest, not necessarily the one you meant.
|
|
354
236
|
|
|
355
237
|
## Arbitrary values
|
|
356
238
|
|
|
357
239
|
```jsx
|
|
358
|
-
<div className="bg-[#6366f1]" />
|
|
359
|
-
<div className="p-[14px]" />
|
|
360
|
-
<div className="w-[calc(100%-2rem)]" />
|
|
361
|
-
<div className="bg-[rgba(99,102,241,0.15)]" />
|
|
362
|
-
<div className="text-[18px]" />
|
|
240
|
+
<div className="bg-[#6366f1] p-[14px] w-[calc(100%-2rem)] text-[18px]" />
|
|
363
241
|
```
|
|
364
242
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
For the rare CSS property with no named utility, write `[property:value]` directly — no utility prefix:
|
|
368
|
-
|
|
369
|
-
```jsx
|
|
370
|
-
<div className="[mask-type:luminance]" />
|
|
371
|
-
<div className="[-webkit-line-clamp:3]" />
|
|
372
|
-
<div className="[--my-var:10px]" /> {/* CSS custom property */}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Underscores become spaces, same as any other arbitrary value: `[background:url(/a.png)_no-repeat]`. Only the first `:` splits the property from the value, so a value that itself contains a colon (a URL's `http://`) still comes through intact.
|
|
243
|
+
For a property with no named utility: `[property:value]` — e.g. `[mask-type:luminance]`, `[--my-var:10px]`. Underscores become spaces: `[background:url(/a.png)_no-repeat]`.
|
|
376
244
|
|
|
377
245
|
## Color system
|
|
378
246
|
|
|
379
|
-
12-shade scale
|
|
247
|
+
12-shade scale, 1 lightest → 12 darkest: `bg-blue-6`, `text-gray-10`, `border-red-4/50`.
|
|
380
248
|
|
|
381
|
-
Families: `slate gray zinc neutral stone red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose`
|
|
249
|
+
Families: `slate gray zinc neutral stone red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose`
|
|
382
250
|
Special: `transparent` `current` `black` `white`
|
|
383
|
-
|
|
384
|
-
Opacity modifier: `bg-blue-6/50` (50% alpha), `bg-blue-6/[0.15]` (arbitrary)
|
|
251
|
+
Opacity: `bg-blue-6/50` or `bg-blue-6/[0.15]`
|
|
385
252
|
|
|
386
253
|
## CSS resets
|
|
387
254
|
|
|
388
|
-
Included
|
|
255
|
+
Included in `kbach.css`, runtime injection, and `<KbachReset />` alike:
|
|
389
256
|
|
|
390
|
-
-
|
|
391
|
-
- `body
|
|
392
|
-
- `
|
|
393
|
-
- `
|
|
394
|
-
- `
|
|
395
|
-
- `img`, `video`, `svg` — `display: block; max-width: 100%`
|
|
396
|
-
- `button`, text-like `input`s (not checkbox/radio), `textarea` — appearance, border, padding cleared, so `border-`/`bg-`/`rounded-`/`p-` utilities fully restyle them
|
|
397
|
-
- `input[type=checkbox]`, `input[type=radio]`, `select` — native rendering kept (stripping it would hide the checkmark/dot/dropdown-arrow with nothing to replace them); only typography and spacing are normalized, and checkbox/radio/range get `accent-color: currentColor` so they still pick up your text color instead of the OS default blue
|
|
398
|
-
- `fieldset`, `table` — padding/margin/border-collapse cleared
|
|
257
|
+
- Border-box everywhere; `border-*` utilities work without needing `border-solid`
|
|
258
|
+
- `body` margin/padding cleared; headings/`p`/`ul`/`ol`/`a` styling cleared to inherit
|
|
259
|
+
- `img`/`video`/`svg` block + max-width 100%
|
|
260
|
+
- `button`/text inputs/`textarea` stripped of native appearance so `bg-`/`rounded-`/`p-` fully restyle them
|
|
261
|
+
- Checkbox/radio/`select` keep native rendering (just typography/spacing normalized + `accent-color: currentColor`)
|
|
399
262
|
|
|
400
263
|
## Configuration
|
|
401
264
|
|
|
@@ -405,71 +268,36 @@ module.exports = {
|
|
|
405
268
|
darkMode: 'attribute', // 'attribute' | 'class' | 'media'
|
|
406
269
|
|
|
407
270
|
theme: {
|
|
408
|
-
//
|
|
409
|
-
colors: { brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' } },
|
|
271
|
+
colors: { brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' } }, // replaces the section
|
|
410
272
|
},
|
|
411
273
|
|
|
412
274
|
extend: {
|
|
413
|
-
//
|
|
414
|
-
colors: { brand: { 6: '#6366f1' } },
|
|
275
|
+
colors: { brand: { 6: '#6366f1' } }, // adds to defaults
|
|
415
276
|
spacing: { 18: '72px' },
|
|
416
277
|
screens: { '3xl': '1920px' },
|
|
417
|
-
fontFamily: {
|
|
418
|
-
sans: 'Inter, sans-serif',
|
|
419
|
-
'sans-bold': 'Inter_700Bold, sans-serif',
|
|
420
|
-
},
|
|
421
|
-
// Custom @keyframes (web only) — reference by name from `animation`,
|
|
422
|
-
// then use that name as animate-{name}. Declaration values are plain
|
|
423
|
-
// CSS strings, same idea as a style object but for a keyframe step.
|
|
278
|
+
fontFamily: { sans: 'Inter, sans-serif' },
|
|
424
279
|
keyframes: {
|
|
425
280
|
wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } },
|
|
426
281
|
},
|
|
427
|
-
animation: {
|
|
428
|
-
wiggle: 'wiggle 1s ease-in-out infinite',
|
|
429
|
-
},
|
|
282
|
+
animation: { wiggle: 'wiggle 1s ease-in-out infinite' },
|
|
430
283
|
},
|
|
431
284
|
|
|
432
285
|
plugins: [
|
|
433
286
|
({ addUtility, addVariant, theme }) => {
|
|
434
|
-
addUtility('border-brand', {
|
|
435
|
-
borderColor: theme('colors.brand.6'),
|
|
436
|
-
borderWidth: 2,
|
|
437
|
-
});
|
|
287
|
+
addUtility('border-brand', { borderColor: theme('colors.brand.6'), borderWidth: 2 });
|
|
438
288
|
addVariant('hocus', ':hover, :focus');
|
|
439
289
|
},
|
|
440
290
|
],
|
|
441
291
|
};
|
|
442
292
|
```
|
|
443
293
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
{/* Override duration/timing/repeat inline, keeps the same wiggle @keyframes: */}
|
|
449
|
-
<div className="animate-[wiggle_2s_ease-in-out]" />
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
Color aliases:
|
|
453
|
-
|
|
454
|
-
```js
|
|
455
|
-
extend: {
|
|
456
|
-
colors: {
|
|
457
|
-
primary: 'blue-6', // resolves to blue shade 6's hex
|
|
458
|
-
brand: { 6: 'primary' }, // follows through to blue-6's hex
|
|
459
|
-
},
|
|
460
|
-
},
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
Runtime update:
|
|
464
|
-
|
|
465
|
-
```js
|
|
466
|
-
import { updateConfig, clearCache } from '@kbach/react';
|
|
467
|
-
updateConfig({ extend: { colors: { brand: { 6: '#6366f1' } } } });
|
|
468
|
-
clearCache();
|
|
469
|
-
```
|
|
294
|
+
- `fontFamily.sans` set to anything but `'System'` auto-injects `body { font-family: … }`
|
|
295
|
+
- Custom `@keyframes` are used as `animate-{name}`, and can be overridden inline: `animate-[wiggle_2s_ease-in-out]`
|
|
296
|
+
- Colors can alias each other: `primary: 'blue-6'`, `brand: { 6: 'primary' }`
|
|
297
|
+
- Runtime update: `updateConfig({ extend: { ... } }); clearCache();`
|
|
470
298
|
|
|
471
299
|
## Full reference
|
|
472
300
|
|
|
473
|
-
|
|
301
|
+
[kbach-react.md](./kbach-react.md) — complete utility list, every modifier, all config options.
|
|
474
302
|
|
|
475
|
-
|
|
303
|
+
React Native / Expo: [`@kbach/native`](https://www.npmjs.com/package/@kbach/native).
|