@kbach/react 1.0.0-beta.5 → 1.0.0-beta.7

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 CHANGED
@@ -32,14 +32,11 @@ export default defineConfig({
32
32
 
33
33
  ```tsx
34
34
  // main.tsx
35
- import { initKbach } from '@kbach/react';
36
35
  import { createRoot } from 'react-dom/client';
37
36
  import App from './App';
38
37
  import './kbach.css';
39
38
 
40
- initKbach().then(() => {
41
- createRoot(document.getElementById('root')!).render(<App />);
42
- });
39
+ createRoot(document.getElementById('root')!).render(<App />);
43
40
  ```
44
41
 
45
42
  ```tsx
@@ -48,6 +45,134 @@ initKbach().then(() => {
48
45
  </div>
49
46
  ```
50
47
 
48
+ That's the whole setup — the Vite plugin config plus a `kbach.css` with the
49
+ two marker comments. No `initKbach()`/async step before your first render:
50
+ `className` here is a plain string prop, resolved entirely by the CSS
51
+ `@kbach/react/vite` already generated at build time, same as any other
52
+ static stylesheet — nothing here calls into the WASM engine at render time,
53
+ so there's nothing to await.
54
+
55
+ `initKbach()` (still exported from `@kbach/react`) is only needed for two
56
+ opt-in features that genuinely do call into the Rust/WASM engine at
57
+ runtime: the dynamic [`kb()`](#dynamic-classes) function, and the optional
58
+ `@kbach/react/jsx-runtime` (a `jsxImportSource` override that intercepts
59
+ *every* element's `className` automatically instead of relying on the
60
+ static build). If you use either, `await initKbach()` once before your
61
+ first render, same as before.
62
+
63
+ ## React Router
64
+
65
+ React Router is a client-side routing library with no build-tool coupling
66
+ of its own, so it layers on top of the exact same Vite + `kbach.css` setup
67
+ either way — but which "either way" depends on which of React Router's two
68
+ genuinely different modes you're using.
69
+
70
+ **Library mode** (you call `createRoot` yourself — `createBrowserRouter` +
71
+ `<RouterProvider>`, or the classic `<BrowserRouter>` component): needs
72
+ nothing beyond the Quick Start above.
73
+
74
+ ```tsx
75
+ // main.tsx
76
+ import { createRoot } from 'react-dom/client';
77
+ import { RouterProvider, createBrowserRouter } from 'react-router';
78
+ import './kbach.css';
79
+ import Home from './routes/home';
80
+ import About from './routes/about';
81
+
82
+ const router = createBrowserRouter([
83
+ { path: '/', Component: Home },
84
+ { path: '/about', Component: About },
85
+ ]);
86
+
87
+ createRoot(document.getElementById('root')!).render(<RouterProvider router={router} />);
88
+ ```
89
+
90
+ **Framework mode** (`npx create-react-router@latest` — file-based routing
91
+ under `app/`, its own Vite plugin, SSR by default): two things differ from
92
+ the Quick Start, both because this mode owns your project's structure and
93
+ render lifecycle, not you:
94
+
95
+ 1. Its convention is `app/`, not `src/` — pass `cssFile` so the plugin
96
+ writes to `app/kbach.css` instead of the (here, nonexistent) default:
97
+ ```ts
98
+ // vite.config.ts
99
+ import { reactRouter } from '@react-router/dev/vite';
100
+ import { kbach } from '@kbach/react/vite';
101
+ import { defineConfig } from 'vite';
102
+
103
+ export default defineConfig({
104
+ plugins: [kbach({ cssFile: 'app/kbach.css' }), reactRouter()],
105
+ });
106
+ ```
107
+ 2. **Don't call `createRoot` yourself in `app/root.tsx`** — the framework's
108
+ own generated client/server entry points already do this; `root.tsx`
109
+ only ever *exports* a `Layout`/default component. Adding your own
110
+ `createRoot(...).render(...)` at that file's top level runs during SSR
111
+ too, where `document` doesn't exist, and crashes the build immediately.
112
+ `app/root.tsx` needs only:
113
+ ```tsx
114
+ import './kbach.css';
115
+ export default function App() {
116
+ return <Outlet />;
117
+ }
118
+ ```
119
+
120
+ Either mode: every route's own components use `className` exactly like any
121
+ other component — routing has no effect on how Kbach resolves classes,
122
+ since resolution already happened at build time, before routing even
123
+ enters the picture.
124
+
125
+ ## Next.js
126
+
127
+ Next.js uses webpack/Turbopack, not Vite, so `@kbach/react/vite` doesn't
128
+ apply — use `@kbach/react/postcss` instead, which does the exact same
129
+ build-time class-string scanning and CSS generation (they share the same
130
+ underlying engine), just wired into PostCSS, which both of Next.js's
131
+ bundlers already run for you with zero extra config.
132
+
133
+ ```js
134
+ // postcss.config.js
135
+ module.exports = {
136
+ plugins: {
137
+ '@kbach/react/postcss': {},
138
+ },
139
+ };
140
+ ```
141
+
142
+ ```css
143
+ /* app/globals.css (App Router) or styles/globals.css (Pages Router) */
144
+ /* kbach:start */
145
+ /* kbach:end */
146
+ ```
147
+
148
+ ```tsx
149
+ // app/layout.tsx
150
+ import './globals.css';
151
+
152
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
153
+ return (
154
+ <html lang="en">
155
+ <body>{children}</body>
156
+ </html>
157
+ );
158
+ }
159
+ ```
160
+
161
+ Plain `className="..."` strings on Server Components work with zero extra
162
+ setup — the CSS already exists at build time, same as any other static
163
+ stylesheet. Hook-based exports (`useTheme`, `useColors`, `<ThemeProvider>`)
164
+ already carry their own `'use client'` directive, so importing them into a
165
+ Client Component just works; importing one into a Server Component is a
166
+ compile-time error the same way importing `useState` into one would be —
167
+ that's React's own rule, not a Kbach limitation.
168
+
169
+ `root`/`theme`/`config`/`include`/`safelist` options all match
170
+ `@kbach/react/vite`'s exactly (see `KbachPluginOptions`, above) — `root`
171
+ additionally defaults to `process.cwd()` rather than a bundler-resolved
172
+ project root, since PostCSS has no equivalent to Vite's own resolved
173
+ `root` to read; pass it explicitly if your `postcss.config.js` doesn't
174
+ live at your project root (e.g. a monorepo app).
175
+
51
176
  ## Dark mode
52
177
 
53
178
  ```tsx
@@ -77,16 +202,20 @@ setTheme({
77
202
  ## Dynamic classes
78
203
 
79
204
  Classes that appear literally in your source are resolved at build time.
80
- For classes assembled from truly dynamic data, use `kb()`:
205
+ For classes assembled from truly dynamic data, use `kb()` — this is the
206
+ one thing in this package that genuinely calls into the WASM engine at
207
+ runtime, so `await initKbach()` once at startup before the first `kb()`
208
+ call (or before rendering, if you can't guarantee it runs first otherwise):
81
209
 
82
210
  ```ts
83
- import { kb } from '@kbach/react';
211
+ import { initKbach, kb } from '@kbach/react';
84
212
 
213
+ await initKbach();
85
214
  const className = kb(`bg-${userColor}-6`);
86
215
  ```
87
216
 
88
217
  Prefer a `safelist` entry in the plugin config when the possible values are
89
- known ahead of time — keeps everything static.
218
+ known ahead of time — keeps everything static, no `initKbach()` needed.
90
219
 
91
220
  ## Reading colors as values
92
221