@kbach/ui 0.1.0-beta.0 → 0.1.0-beta.2
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.md +14 -6
- package/README.md +35 -303
- package/dist/{chunk-BPCFICND.mjs → chunk-LY753XBX.mjs} +90 -18
- package/dist/{chunk-UE54W6ZG.mjs → chunk-UVXTWGHQ.mjs} +1 -1
- package/dist/core/index.d.ts +74 -16
- package/dist/core/index.js +84 -18
- package/dist/index.d.ts +5 -11
- package/dist/index.js +103 -94
- package/dist/index.mjs +14 -4
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/native.js +0 -21
- package/dist/native.mjs +49 -0
- package/dist/vite-plugin.js +105 -15
- package/dist/vite-plugin.mjs +105 -15
- package/kbach-ui.md +14 -3
- package/package.json +3 -2
- package/scripts/postinstall.js +17 -0
- package/dist/native.d.ts +0 -314
package/KBACH.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Kbach is a Tailwind-like utility CSS framework for React (web) and React Native. Classes are written as `className` strings and resolved to inline styles at render time. On web, stateful and structural CSS rules are injected into a `<style>` tag so they work with the browser cascade. On native, only inline-compatible styles are applied.
|
|
4
4
|
|
|
5
|
-
One package, `@kbach/ui`, covers both platforms — React web, React Native, and Expo (Expo Go, Expo web, and native builds).
|
|
5
|
+
One package, `@kbach/ui`, covers both platforms — React web, React Native, and Expo (Expo Go, Expo web, and native builds). `ThemeProvider` auto-detects native at render time, so it's the same import (`@kbach/ui`) on every platform. The Babel preset and Metro/Babel config helpers (Node-only build tooling) live at the `@kbach/ui/native` and `@kbach/ui/babel` subpaths.
|
|
6
6
|
|
|
7
7
|
`@kbach/native` is deprecated and no longer maintained — its last published npm version stays available as a compatibility shim re-exporting `@kbach/ui`, but new setups should install `@kbach/ui` directly (see below).
|
|
8
8
|
|
|
@@ -71,10 +71,10 @@ Or the one-liner helper: `const { createKbachConfig } = require('@kbach/ui/nativ
|
|
|
71
71
|
|
|
72
72
|
### Wrap app
|
|
73
73
|
```jsx
|
|
74
|
-
import { ThemeProvider } from '@kbach/ui
|
|
74
|
+
import { ThemeProvider } from '@kbach/ui';
|
|
75
75
|
<ThemeProvider defaultMode="system"><AppContent /></ThemeProvider>
|
|
76
76
|
```
|
|
77
|
-
|
|
77
|
+
`ThemeProvider` auto-detects React Native at render time and reads `useColorScheme()`/`useWindowDimensions()` automatically — no extra props, no separate `/native` import needed.
|
|
78
78
|
|
|
79
79
|
After changing babel.config.js: `npx expo start --clear`
|
|
80
80
|
|
|
@@ -88,8 +88,7 @@ In a browser (Expo Web, Metro web), `@kbach/ui` switches to the same CSS-class s
|
|
|
88
88
|
- Recommended: use the Vite plugin same as the web Static CSS setup above — `import { kbach } from '@kbach/ui/vite'` — and import `kbach.css` in your entry file, for zero runtime cost on the web target too
|
|
89
89
|
- Not using the Vite plugin (the common case for Expo/Metro web, no Vite build step)? Render `<KbachReset />` once near your root — e.g. Expo Router's root `app/_layout.tsx`, inside `<ThemeProvider>`:
|
|
90
90
|
```jsx
|
|
91
|
-
import { KbachReset } from '@kbach/ui';
|
|
92
|
-
import { ThemeProvider } from '@kbach/ui/native';
|
|
91
|
+
import { KbachReset, ThemeProvider } from '@kbach/ui';
|
|
93
92
|
<ThemeProvider defaultMode="system"><KbachReset /><Slot /></ThemeProvider>
|
|
94
93
|
```
|
|
95
94
|
|
|
@@ -626,8 +625,9 @@ rounded-tl/tr/bl/br border radius on one corner
|
|
|
626
625
|
|
|
627
626
|
### Shadow
|
|
628
627
|
```
|
|
629
|
-
shadow-sm/DEFAULT/md/lg/xl/2xl/none
|
|
628
|
+
shadow-sm/DEFAULT/md/lg/xl/2xl/inner/none
|
|
630
629
|
```
|
|
630
|
+
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. `shadow-inner` is web-only (no native inset-shadow equivalent). Customize/add sizes via `theme.extend.shadow` — each preset key merges independently (see Theme Configuration below).
|
|
631
631
|
|
|
632
632
|
### Opacity
|
|
633
633
|
```
|
|
@@ -907,6 +907,14 @@ module.exports = {
|
|
|
907
907
|
colors: { brand: { 6: '#6366f1' } },
|
|
908
908
|
spacing: { 18: 72, 22: 88 },
|
|
909
909
|
fontSize: { '10xl': 160 },
|
|
910
|
+
// Each preset key merges independently — overriding lg's boxShadow
|
|
911
|
+
// (the web value) leaves its native shadowColor/shadowOffset/etc.
|
|
912
|
+
// untouched, and a brand-new key (e.g. '3xl') adds a new shadow-3xl
|
|
913
|
+
// utility alongside the defaults.
|
|
914
|
+
shadow: {
|
|
915
|
+
lg: { boxShadow: '0 10px 40px -10px rgba(99, 102, 241, 0.4)' },
|
|
916
|
+
'3xl': { boxShadow: '0 35px 60px -15px rgba(0, 0, 0, 0.3)' },
|
|
917
|
+
},
|
|
910
918
|
// Custom @keyframes (web only) — declaration values are plain CSS strings.
|
|
911
919
|
// Reference by name from `animation`, then use that name as animate-{name}.
|
|
912
920
|
keyframes: {
|
package/README.md
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
# @kbach/ui
|
|
2
2
|
|
|
3
|
-
Tailwind-like utility classes for React
|
|
3
|
+
Tailwind-like utility classes for React — web, React Native, and Expo. Write `className` strings once; a custom JSX runtime resolves them at render time on every platform. An optional Vite plugin outputs a static `kbach.css` for zero runtime cost on web.
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
<div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
|
|
7
7
|
<div className="bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-lg px-6 py-3" />
|
|
8
|
-
<div className="group">
|
|
9
|
-
<span className="opacity-0 group-hover:opacity-100 transition" />
|
|
10
|
-
</div>
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Install
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
npm install @kbach/ui
|
|
17
8
|
```
|
|
18
9
|
|
|
19
10
|
[npm package](https://www.npmjs.com/package/@kbach/ui)
|
|
20
11
|
|
|
21
12
|
## Setup
|
|
22
13
|
|
|
23
|
-
|
|
14
|
+
```
|
|
15
|
+
npm install @kbach/ui
|
|
16
|
+
```
|
|
24
17
|
|
|
25
|
-
###
|
|
18
|
+
### JSX runtime (always required)
|
|
26
19
|
|
|
27
20
|
**tsconfig.json:**
|
|
28
21
|
|
|
@@ -36,12 +29,12 @@ That's the only setting needed — Vite, Next.js, and React Router all read it.
|
|
|
36
29
|
|
|
37
30
|
| Framework | Use |
|
|
38
31
|
|---|---|
|
|
39
|
-
| Vite, React Router library mode, CRA, other Vite-based | **[Static CSS
|
|
40
|
-
| React Router, framework mode | **[Static CSS
|
|
41
|
-
| Next.js | **[Next.js
|
|
42
|
-
| React Native, Expo | **[React Native / Expo
|
|
32
|
+
| Vite, React Router library mode, CRA, other Vite-based | **[Static CSS](#static-css)** (recommended) — zero runtime cost, catches typos at build time |
|
|
33
|
+
| React Router, framework mode | **[Static CSS](#static-css)** — and skip `@vitejs/plugin-react`, see note below |
|
|
34
|
+
| Next.js | **[Next.js](#nextjs)** — Runtime setup, plus one App Router-specific detail |
|
|
35
|
+
| React Native, Expo | **[React Native / Expo](#react-native--expo)** — different setup entirely (Babel preset, not the JSX runtime step above) |
|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
### Static CSS
|
|
45
38
|
|
|
46
39
|
Vite only, and the recommended setup for any Vite-based app — a build-time plugin writes real CSS into a file you import at build time, so nothing is generated client-side and there's zero runtime cost. Three pieces, all required:
|
|
47
40
|
|
|
@@ -70,7 +63,7 @@ import './kbach.css';
|
|
|
70
63
|
|
|
71
64
|
This import is what actually switches the app over to Static CSS — the plugin alone only generates the file; without importing it, runtime injection stays active and you get both at once.
|
|
72
65
|
|
|
73
|
-
**3. Wrap your app — no `<KbachReset />` here, `kbach.css` already includes the reset:**
|
|
66
|
+
**3. Wrap your app — no `<KbachReset />` here, `kbach.css` already includes the base reset:**
|
|
74
67
|
|
|
75
68
|
```jsx
|
|
76
69
|
import { ThemeProvider } from '@kbach/ui';
|
|
@@ -80,9 +73,9 @@ export default function Root() {
|
|
|
80
73
|
}
|
|
81
74
|
```
|
|
82
75
|
|
|
83
|
-
Done. The plugin scans your source at build time and writes CSS between the markers
|
|
76
|
+
Done. The plugin scans your source at build time and writes CSS between the markers, and warns in the terminal (with a clickable `file:line`) for any class it doesn't recognize — usually a typo.
|
|
84
77
|
|
|
85
|
-
Using a custom `kbach.config.js`?
|
|
78
|
+
Using a custom `kbach.config.js`? See [Wiring a custom config in](#wiring-a-custom-config-in) below — it needs to be passed in twice for Static CSS specifically.
|
|
86
79
|
|
|
87
80
|
**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`).
|
|
88
81
|
|
|
@@ -97,9 +90,9 @@ export default defineConfig({ plugins: [kbach(), reactRouter()] });
|
|
|
97
90
|
|
|
98
91
|
(React Router library mode — `createBrowserRouter`, no SSR — has no such conflict; set it up like any Vite + React app.)
|
|
99
92
|
|
|
100
|
-
|
|
93
|
+
### Runtime
|
|
101
94
|
|
|
102
|
-
Client-side CSS injection — works with any bundler (Vite, webpack, Turbopack, Metro-for-web, …), no build plugin. Next.js always uses this
|
|
95
|
+
Client-side CSS injection — works with any bundler (Vite, webpack, Turbopack, Metro-for-web, …), no build plugin. Next.js always uses this, or use it on Vite if you'd rather not wire up the plugin yet:
|
|
103
96
|
|
|
104
97
|
```jsx
|
|
105
98
|
import { ThemeProvider, KbachReset } from '@kbach/ui';
|
|
@@ -114,17 +107,17 @@ export default function Root() {
|
|
|
114
107
|
}
|
|
115
108
|
```
|
|
116
109
|
|
|
117
|
-
That's it — done. `<KbachReset />` renders the base reset
|
|
110
|
+
That's it — done. `<KbachReset />` renders the base reset as real markup instead of waiting on client JS — matters most for SSR, where it avoids a flash of unstyled browser defaults before hydration.
|
|
118
111
|
|
|
119
|
-
Using a custom `kbach.config.js`? Pass it to `ThemeProvider`
|
|
112
|
+
Using a custom `kbach.config.js`? Pass it to `ThemeProvider` — see [Wiring a custom config in](#wiring-a-custom-config-in).
|
|
120
113
|
|
|
121
114
|
Don't also set up Static CSS above in the same app — pick one.
|
|
122
115
|
|
|
123
|
-
|
|
116
|
+
### Next.js
|
|
124
117
|
|
|
125
|
-
|
|
118
|
+
Always [Runtime setup](#runtime) above — Static CSS doesn't apply (webpack/Turbopack, not Vite). The `tsconfig.json` step from [Setup](#setup) applies as-is; SWC reads `jsxImportSource` the same way Vite does.
|
|
126
119
|
|
|
127
|
-
The one Next.js-specific detail: render `<KbachReset />` once in the root App Router `layout.tsx
|
|
120
|
+
The one Next.js-specific detail: render `<KbachReset />` once in the root App Router `layout.tsx`:
|
|
128
121
|
|
|
129
122
|
```jsx
|
|
130
123
|
// app/layout.tsx
|
|
@@ -144,13 +137,11 @@ export default function RootLayout({ children }) {
|
|
|
144
137
|
}
|
|
145
138
|
```
|
|
146
139
|
|
|
147
|
-
Without
|
|
140
|
+
Without it, expect a flash of raw browser defaults on first paint until hydration completes. `@kbach/ui`'s compiled output ships its own `"use client"` directive, so App Router Server Components can use `className`, `styled()`, hooks, `<ThemeProvider>`, and `<KbachReset>` directly — no manual `'use client'` wrapper needed.
|
|
148
141
|
|
|
149
|
-
|
|
142
|
+
### React Native / Expo
|
|
150
143
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Same `npm install @kbach/ui` — no separate package. Everything below (API, modifiers, color system) is the same import as web; only setup differs.
|
|
144
|
+
Same `npm install @kbach/ui` — no separate package. Everything else (API, modifiers, color system) is the same import as web; only setup differs.
|
|
154
145
|
|
|
155
146
|
**1. babel.config.js:**
|
|
156
147
|
|
|
@@ -166,12 +157,12 @@ module.exports = function (api) {
|
|
|
166
157
|
};
|
|
167
158
|
```
|
|
168
159
|
|
|
169
|
-
Or the one-liner helper: `const { createKbachConfig } = require('@kbach/ui/native'); module.exports = createKbachConfig()
|
|
160
|
+
Or the one-liner helper: `const { createKbachConfig } = require('@kbach/ui/native'); module.exports = createKbachConfig();`. After changing this file, clear the Metro cache: `npx expo start --clear`.
|
|
170
161
|
|
|
171
162
|
**2. Wrap your app:**
|
|
172
163
|
|
|
173
164
|
```jsx
|
|
174
|
-
import { ThemeProvider } from '@kbach/ui
|
|
165
|
+
import { ThemeProvider } from '@kbach/ui';
|
|
175
166
|
|
|
176
167
|
export default function App() {
|
|
177
168
|
return (
|
|
@@ -182,292 +173,33 @@ export default function App() {
|
|
|
182
173
|
}
|
|
183
174
|
```
|
|
184
175
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
### Platform differences
|
|
188
|
-
|
|
189
|
-
A handful of utilities are native-only or web-only:
|
|
190
|
-
|
|
191
|
-
| | |
|
|
192
|
-
|---|---|
|
|
193
|
-
| Native-only | `tint-{color}` (Image/icon tinting), `perspective-{n}`, `backface-hidden`, `text-shadow`/`text-shadow-lg` |
|
|
194
|
-
| Web-only, ignored on native (no warning) | `caret-*` `accent-*` `stroke-*` `fill-*` `touch-*` `float-*` `clear-*` `line-clamp-*` `scroll-*` `animate-*` `transition` `filter` `backdrop-filter` `print:` `before:` `after:` `selection:` `first-letter:` `first-line:` `marker:` `landscape:` `portrait:` `motion-reduce:` `motion-safe:` `contrast-more:` `contrast-less:` `rtl:` `ltr:` `grid` `grid-cols-*` `ring-offset-*` `outline-*` `cursor-*` `bg-gradient-*` |
|
|
195
|
-
|
|
196
|
-
`ring`/`ring-{n}`/`ring-{color}` is a partial exception — RN has no box-shadow, so it falls back to `borderWidth`/`borderColor`, which *does* affect layout and shares properties with `border-*` (whichever class comes last wins if you combine both).
|
|
176
|
+
`ThemeProvider` auto-detects React Native at render time and reads `useColorScheme()`/`useWindowDimensions()` automatically — same import as web, no `/native` subpath needed.
|
|
197
177
|
|
|
198
|
-
|
|
178
|
+
A handful of utilities are native-only or web-only, and Expo Web/React Native Web has its own notes — see [KBACH.md](./KBACH.md#native-only-utilities) for the full platform-differences reference.
|
|
199
179
|
|
|
200
|
-
###
|
|
180
|
+
### Wiring a custom config in
|
|
201
181
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
- RN components substitute to HTML: `View`/`ScrollView`→`div`, `Text`→`span`, `TextInput`→`input`/`textarea`, `Image`→`img`, `Pressable`/`TouchableOpacity`→`div[role=button]`
|
|
205
|
-
- RN-only props (`onChangeText`, `source`, `secureTextEntry`, …) map to HTML equivalents
|
|
206
|
-
- Register more: `registerWebElement(Animated.View, 'div')`
|
|
207
|
-
- Recommended: use the Vite plugin same as [Static CSS setup](#static-css-setup) above — `import { kbach } from '@kbach/ui/vite'` — and import `kbach.css` in your entry file, for zero runtime cost on the web target too
|
|
208
|
-
- Not using the Vite plugin (the common case for Expo/Metro web, which has no Vite build step)? Render `<KbachReset />` once near your root — e.g. Expo Router's root `app/_layout.tsx`, inside `<ThemeProvider>`:
|
|
209
|
-
|
|
210
|
-
```jsx
|
|
211
|
-
import { KbachReset } from '@kbach/ui';
|
|
212
|
-
import { ThemeProvider } from '@kbach/ui/native';
|
|
213
|
-
|
|
214
|
-
export default function RootLayout() {
|
|
215
|
-
return (
|
|
216
|
-
<ThemeProvider defaultMode="system">
|
|
217
|
-
<KbachReset />
|
|
218
|
-
<Slot />
|
|
219
|
-
</ThemeProvider>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
This ships the base reset as real markup instead of relying solely on the runtime injector.
|
|
225
|
-
|
|
226
|
-
## Dark mode
|
|
227
|
-
|
|
228
|
-
`<ThemeProvider>` powers every `dark:` class — detects OS color scheme, persists the user's choice, re-renders on change.
|
|
229
|
-
|
|
230
|
-
```jsx
|
|
231
|
-
<ThemeProvider
|
|
232
|
-
defaultMode="system" // 'light' | 'dark' | 'system'
|
|
233
|
-
disablePersistence={false} // true = don't remember across reloads
|
|
234
|
-
>
|
|
235
|
-
<App />
|
|
236
|
-
</ThemeProvider>
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
| Prop | Type | Default | Description |
|
|
240
|
-
|---|---|---|---|
|
|
241
|
-
| `defaultMode` | `'light' \| 'dark' \| 'system'` | `'system'` | Starting mode |
|
|
242
|
-
| `disablePersistence` | `boolean` | `false` | Skip saving to `localStorage` (web) / `AsyncStorage` (native) |
|
|
243
|
-
| `config` | `FrameworkConfig` | global config | Scope a different config to this subtree |
|
|
244
|
-
|
|
245
|
-
`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).
|
|
246
|
-
|
|
247
|
-
## API
|
|
248
|
-
|
|
249
|
-
### className / kb
|
|
250
|
-
|
|
251
|
-
`kb` is an alias for `className` — works on any element.
|
|
252
|
-
|
|
253
|
-
```jsx
|
|
254
|
-
<button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-9 rounded-lg px-4 py-2" />
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### styled(Component, classes)
|
|
258
|
-
|
|
259
|
-
```jsx
|
|
260
|
-
import { styled } from '@kbach/ui';
|
|
261
|
-
|
|
262
|
-
const Card = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
|
|
263
|
-
const Button = styled('button', 'bg-blue-7 hover:bg-blue-8 rounded-xl px-6 py-3');
|
|
264
|
-
|
|
265
|
-
<Card kb="mt-4">
|
|
266
|
-
<Button kb="w-full">Submit</Button>
|
|
267
|
-
</Card>
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
Extra classes at use time via `kb` merge with the base classes.
|
|
271
|
-
|
|
272
|
-
### cx(...classes)
|
|
273
|
-
|
|
274
|
-
```jsx
|
|
275
|
-
import { cx } from '@kbach/ui';
|
|
276
|
-
|
|
277
|
-
<div className={cx('p-4 rounded-xl', isSelected && 'border-2 border-blue-6', isDisabled && 'opacity-50')} />
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
Falsy values ignored. Also works as pre-built style constants:
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
export const container = cx('flex-1 bg-white dark:bg-gray-9 p-4');
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### useStyles(classes, state?)
|
|
287
|
-
|
|
288
|
-
```jsx
|
|
289
|
-
const style = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');
|
|
290
|
-
const style2 = useStyles('bg-blue-5 pressed:bg-blue-7 rounded-lg', { pressed });
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### kb(classes)
|
|
294
|
-
|
|
295
|
-
Resolve outside a component:
|
|
296
|
-
|
|
297
|
-
```js
|
|
298
|
-
const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### useTheme()
|
|
302
|
-
|
|
303
|
-
```js
|
|
304
|
-
const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
| Value | Type | Description |
|
|
308
|
-
|---|---|---|
|
|
309
|
-
| `mode` | `'light' \| 'dark' \| 'system'` | User-selected mode |
|
|
310
|
-
| `resolvedMode` | `'light' \| 'dark'` | Resolved after system lookup |
|
|
311
|
-
| `isDark` | `boolean` | `resolvedMode === 'dark'` |
|
|
312
|
-
| `setMode` | `fn` | Set mode explicitly |
|
|
313
|
-
| `toggle` | `fn` | Toggle light/dark |
|
|
314
|
-
| `config` | `ResolvedConfig` | Full resolved config |
|
|
315
|
-
|
|
316
|
-
### useIsDark() / useColors()
|
|
317
|
-
|
|
318
|
-
```js
|
|
319
|
-
const isDark = useIsDark();
|
|
320
|
-
|
|
321
|
-
const colors = useColors();
|
|
322
|
-
colors.blue[6] // '#3b82f6'
|
|
323
|
-
colors.blue['6/50'] // 'rgba(59,130,246,0.5)'
|
|
324
|
-
colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Typed theme tokens
|
|
328
|
-
|
|
329
|
-
`useColors()` and `useSpacing()` are typed against the built-in theme by default (`DefaultColorName`/`DefaultSpacingKey`), so TypeScript autocompletes real color/spacing names and flags a typo (`colors.blu`, `spacing.ful`) as an error — no setup needed if you're on the default theme.
|
|
330
|
-
|
|
331
|
-
```ts
|
|
332
|
-
import { useSpacing } from '@kbach/ui';
|
|
333
|
-
|
|
334
|
-
const spacing = useSpacing();
|
|
335
|
-
spacing[4] // 16
|
|
336
|
-
spacing.full // '100%'
|
|
337
|
-
spacing['1/2'] // '50%'
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
A customized `kbach.config.js` isn't visible to TypeScript — it's a plain `.js` file loaded at runtime, not a statically-analyzed module — so a project with extra colors or spacing keys needs to widen the type parameter by hand:
|
|
341
|
-
|
|
342
|
-
```ts
|
|
343
|
-
import { useColors, type DefaultColorName } from '@kbach/ui';
|
|
344
|
-
|
|
345
|
-
const colors = useColors<DefaultColorName | 'brand'>();
|
|
346
|
-
colors.brand[6] // now type-checks
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
This only affects the exported *types* — `useColors()`/`useSpacing()` called with no type argument behave exactly as before at runtime. If existing code was relying on a color/spacing name TypeScript couldn't previously catch (the old types had a blanket `[key: string]: any`), this may surface a new type error — the fix is the escape-hatch pattern above, not a code change.
|
|
350
|
-
|
|
351
|
-
## Modifiers
|
|
352
|
-
|
|
353
|
-
Chain in any order: `<div className="dark:sm:hover:p-4" />`
|
|
354
|
-
|
|
355
|
-
| Category | Modifiers |
|
|
356
|
-
|---|---|
|
|
357
|
-
| Theme | `dark:` `light:` / `not-dark:` |
|
|
358
|
-
| Interactive | `hover:` `focus:` `pressed:` `active:` `disabled:` `checked:` `visited:` `placeholder:` (all have `not-` variants) |
|
|
359
|
-
| Structural | `first:` `last:` `odd:` `even:` `only:` `focus-within:` `focus-visible:` |
|
|
360
|
-
| Pseudo-elements | `before:` `after:` `selection:` `first-letter:` `first-line:` `marker:` |
|
|
361
|
-
| Responsive | `sm:`(576px) `md:`(768px) `lg:`(1024px) `xl:`(1280px) `2xl:`(1536px) |
|
|
362
|
-
| Other | `print:` `landscape:`/`portrait:` `motion-reduce:`/`motion-safe:` `contrast-more:`/`contrast-less:` `rtl:`/`ltr:` `!` (important) |
|
|
363
|
-
|
|
364
|
-
```jsx
|
|
365
|
-
<div className="before:content-['*'] before:text-red-6 relative" />
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
**Group / peer:**
|
|
369
|
-
|
|
370
|
-
```jsx
|
|
371
|
-
<div className="group">
|
|
372
|
-
<span className="opacity-0 group-hover:opacity-100 transition" />
|
|
373
|
-
</div>
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
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.
|
|
377
|
-
|
|
378
|
-
## Arbitrary values
|
|
379
|
-
|
|
380
|
-
```jsx
|
|
381
|
-
<div className="bg-[#6366f1] p-[14px] w-[calc(100%-2rem)] text-[18px]" />
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
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]`.
|
|
385
|
-
|
|
386
|
-
## Color system
|
|
387
|
-
|
|
388
|
-
12-shade scale, 1 lightest → 12 darkest: `bg-blue-6`, `text-gray-10`, `border-red-4/50`.
|
|
389
|
-
|
|
390
|
-
Families: `slate gray zinc neutral stone red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose`
|
|
391
|
-
Special: `transparent` `current` `black` `white`
|
|
392
|
-
Opacity: `bg-blue-6/50` or `bg-blue-6/[0.15]`
|
|
393
|
-
|
|
394
|
-
## CSS resets
|
|
395
|
-
|
|
396
|
-
Included in `kbach.css`, runtime injection, and `<KbachReset />` alike:
|
|
397
|
-
|
|
398
|
-
- Border-box everywhere; `border-*` utilities work without needing `border-solid`
|
|
399
|
-
- `body` margin/padding cleared; headings/`p`/`ul`/`ol`/`a` styling cleared to inherit
|
|
400
|
-
- `img`/`video`/`svg` block + max-width 100%
|
|
401
|
-
- `button`/text inputs/`textarea` stripped of native appearance so `bg-`/`rounded-`/`p-` fully restyle them
|
|
402
|
-
- Checkbox/radio/`select` keep native rendering (just typography/spacing normalized + `accent-color: currentColor`)
|
|
403
|
-
|
|
404
|
-
## Configuration
|
|
405
|
-
|
|
406
|
-
```js
|
|
407
|
-
// kbach.config.js
|
|
408
|
-
module.exports = {
|
|
409
|
-
darkMode: 'attribute', // 'attribute' | 'class' | 'media'
|
|
410
|
-
|
|
411
|
-
theme: {
|
|
412
|
-
colors: { brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' } }, // replaces the section
|
|
413
|
-
},
|
|
414
|
-
|
|
415
|
-
extend: {
|
|
416
|
-
colors: { brand: { 6: '#6366f1' } }, // adds to defaults
|
|
417
|
-
spacing: { 18: '72px' },
|
|
418
|
-
screens: { '3xl': '1920px' },
|
|
419
|
-
fontFamily: { sans: 'Inter, sans-serif' },
|
|
420
|
-
keyframes: {
|
|
421
|
-
wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' } },
|
|
422
|
-
},
|
|
423
|
-
animation: { wiggle: 'wiggle 1s ease-in-out infinite' },
|
|
424
|
-
},
|
|
425
|
-
|
|
426
|
-
plugins: [
|
|
427
|
-
({ addUtility, addVariant, theme }) => {
|
|
428
|
-
addUtility('border-brand', { borderColor: theme('colors.brand.6'), borderWidth: 2 });
|
|
429
|
-
addVariant('hocus', ':hover, :focus');
|
|
430
|
-
},
|
|
431
|
-
],
|
|
432
|
-
};
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
- `fontFamily.sans` set to anything but `'System'` auto-injects `body { font-family: … }`
|
|
436
|
-
- Custom `@keyframes` are used as `animate-{name}`, and can be overridden inline: `animate-[wiggle_2s_ease-in-out]`
|
|
437
|
-
- Colors can alias each other: `primary: 'blue-6'`, `brand: { 6: 'primary' }`
|
|
438
|
-
- Runtime update: `updateConfig({ extend: { ... } }); clearCache();`
|
|
439
|
-
|
|
440
|
-
### Wiring the config in — required for both setups
|
|
441
|
-
|
|
442
|
-
`kbach.config.js` isn't picked up automatically. It has to be imported and passed in explicitly, and **where** depends on which of the two things it affects:
|
|
443
|
-
|
|
444
|
-
- **Runtime** — dark mode strategy, `useColors()`, custom `@keyframes`/`animation`, and (for SSR) the default-font fallback rendered before your stylesheet takes over. Needed by **both** [Runtime setup](#runtime-setup) and [Static CSS setup](#static-css-setup) — pass it to `ThemeProvider`:
|
|
182
|
+
`kbach.config.js` isn't picked up automatically — it has to be passed in explicitly, and **where** depends on what it affects:
|
|
445
183
|
|
|
184
|
+
- **Runtime** (dark mode, `useColors()`, animations) — needed by every setup above, pass it to `ThemeProvider`:
|
|
446
185
|
```jsx
|
|
447
186
|
import { ThemeProvider } from '@kbach/ui';
|
|
448
187
|
import kbachConfig from '../kbach.config';
|
|
449
188
|
|
|
450
|
-
<ThemeProvider defaultMode="system" config={kbachConfig}>
|
|
451
|
-
<App />
|
|
452
|
-
</ThemeProvider>
|
|
189
|
+
<ThemeProvider defaultMode="system" config={kbachConfig}><App /></ThemeProvider>
|
|
453
190
|
```
|
|
454
|
-
|
|
455
|
-
(Equivalent to calling `updateConfig(kbachConfig)` once before anything renders — `ThemeProvider`'s `config` prop does this for you and keeps the global store in sync if it ever changes.)
|
|
456
|
-
|
|
457
|
-
- **Build-time** — what the Vite plugin actually scans your source against and generates CSS for. Only relevant to [Static CSS setup](#static-css-setup) — pass it to the plugin itself:
|
|
458
|
-
|
|
191
|
+
- **Build-time** (what the Vite plugin scans against) — only for [Static CSS](#static-css), pass it to the plugin:
|
|
459
192
|
```ts
|
|
460
|
-
// vite.config.ts
|
|
461
193
|
import { kbach } from '@kbach/ui/vite';
|
|
462
194
|
import kbachConfig from './kbach.config';
|
|
463
195
|
|
|
464
196
|
export default defineConfig({ plugins: [kbach(kbachConfig)] });
|
|
465
197
|
```
|
|
466
198
|
|
|
467
|
-
Skipping the runtime one is an easy mistake
|
|
199
|
+
Skipping the runtime one under Static CSS is an easy mistake — the generated `kbach.css` looks correct, but dark mode/`useColors()`/animations silently fall back to defaults since nothing told the running app what you customized.
|
|
468
200
|
|
|
469
|
-
##
|
|
201
|
+
## More information
|
|
470
202
|
|
|
471
|
-
[kbach-ui.md](./kbach-ui.md) — complete
|
|
203
|
+
[kbach-ui.md](./kbach-ui.md) — the complete reference: `ThemeProvider`/`useTheme`/dark mode, the full API (`styled`, `cx`, `useStyles`, `kb`, `useColors`, typed theme tokens), every modifier, the color system, CSS resets, and all `kbach.config.js` options — covers web and React Native/Expo.
|
|
472
204
|
|
|
473
205
|
`@kbach/native` is deprecated and no longer maintained — its last published npm version is frozen as a compatibility shim re-exporting this package. Install `@kbach/ui` directly for new projects.
|