@kbach/react-native 1.0.0-beta.5 → 1.0.0-beta.50

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.
Files changed (42) hide show
  1. package/AGENTS.md +347 -0
  2. package/README.md +143 -137
  3. package/android/src/main/jniLibs/arm64-v8a/libkbach_core_engine.so +0 -0
  4. package/android/src/main/jniLibs/x86_64/libkbach_core_engine.so +0 -0
  5. package/dist/chunk-3KLOVONL.mjs +564 -0
  6. package/dist/chunk-SC3L5I7H.mjs +40 -0
  7. package/dist/chunk-XOH25F3E.mjs +112 -0
  8. package/dist/clsx-BLLSnqAa.d.ts +277 -0
  9. package/dist/darkModeStore.d.ts +73 -0
  10. package/dist/darkModeStore.js +118 -0
  11. package/dist/dynamicTokens.d.ts +53 -0
  12. package/dist/dynamicTokens.js +75 -0
  13. package/dist/index.d.ts +92 -151
  14. package/dist/index.js +2119 -393
  15. package/dist/index.web.d.ts +101 -4
  16. package/dist/index.web.js +438 -401
  17. package/dist/index.web.mjs +268 -14
  18. package/dist/jsx-dev-runtime.js +2238 -332
  19. package/dist/jsx-dev-runtime.web.js +207 -361
  20. package/dist/jsx-dev-runtime.web.mjs +19 -7
  21. package/dist/jsx-runtime.js +2238 -332
  22. package/dist/jsx-runtime.web.js +191 -353
  23. package/dist/jsx-runtime.web.mjs +3 -6
  24. package/dist/theme.d.ts +54 -0
  25. package/dist/{chunk-AEVOM2NB.mjs → theme.js} +86 -61
  26. package/package.json +44 -14
  27. package/src/babel-plugin.js +144 -2
  28. package/dist/chunk-4KCCEMEN.mjs +0 -66
  29. package/dist/chunk-DLIXI5OW.mjs +0 -53
  30. package/dist/chunk-FG5E4NLJ.mjs +0 -18
  31. package/dist/chunk-KSDFO34H.mjs +0 -32
  32. package/dist/chunk-OJQAJLYT.mjs +0 -14
  33. package/dist/chunk-ZUE6SCTE.mjs +0 -14
  34. package/dist/index.d.mts +0 -161
  35. package/dist/index.mjs +0 -32
  36. package/dist/index.web.d.mts +0 -7
  37. package/dist/jsx-dev-runtime.d.mts +0 -10
  38. package/dist/jsx-dev-runtime.mjs +0 -17
  39. package/dist/jsx-dev-runtime.web.d.mts +0 -10
  40. package/dist/jsx-runtime.d.mts +0 -7
  41. package/dist/jsx-runtime.mjs +0 -14
  42. package/dist/jsx-runtime.web.d.mts +0 -7
package/README.md CHANGED
@@ -1,179 +1,185 @@
1
1
  # @kbach/react-native
2
2
 
3
- Kbach's utility-class styling engine for React Native — the same class
4
- names as [`@kbach/react`](https://www.npmjs.com/package/@kbach/react),
5
- resolved to a real `StyleSheet`-ready style object instead of injected CSS.
3
+ Utility-class styling for React Native — the same class names as
4
+ [`@kbach/react`](https://www.npmjs.com/package/@kbach/react), resolved to a
5
+ `StyleSheet`-ready style object instead of CSS.
6
6
 
7
- > **Beta.** Native (Android-only, no iOS bridge yet) resolves through a
8
- > Kotlin/JNI TurboModule; Expo Web / react-native-web resolves through the
9
- > same Rust engine compiled to WASM instead — both call the identical
10
- > resolution logic, so class semantics stay identical everywhere. The
11
- > native module ships prebuilt (see Setup below) — no Rust toolchain or
12
- > manual native wiring required on any platform.
7
+ > **Beta.** Native is Android-only (no iOS bridge yet) — on iOS, every
8
+ > build (dev client, CLI, EAS) runs the same reduced-coverage JS-fallback
9
+ > engine as Expo Go, not the full native engine. Ships a prebuilt native
10
+ > module — no Rust toolchain or manual linking.
13
11
 
14
- ## Setup
12
+ ## Install
15
13
 
16
14
  ```sh
17
- npm install @kbach/react-native
15
+ npm install @kbach/react-native@beta
18
16
  ```
19
17
 
20
- One package, nothing else to install — the Kotlin TurboModule and its
21
- prebuilt native `.so` ship inside this same package's own `android/`
22
- folder. Android's autolinking discovers it automatically the moment it's
23
- in `node_modules`; no manual `MainApplication.kt` edit needed. Works the
24
- same way whether your app was created with the React Native CLI or with
25
- Expo — **except Expo Go**, which can never load this (or any) custom
26
- native module; see below.
18
+ ## Setup
19
+
20
+ The babel plugin (below) is **required on every target, including Expo
21
+ Go** — it's the only thing that makes `className` get intercepted at all;
22
+ without it, `className` is just an unrecognized prop RN silently ignores
23
+ (no styling, no warning). What differs per target is what happens *after*
24
+ that:
25
+
26
+ | Target | Setup |
27
+ | :-- | :-- |
28
+ | **Expo Go** | Add the babel plugin (below). A pure-JS fallback engine loads automatically after that — no other setup. |
29
+ | **Expo Web** / `react-native-web` | Add the babel plugin (below). Resolves through the same CSS engine `@kbach/react` uses after that — no other setup. |
30
+ | **Expo dev client / prebuild / EAS** | Add the babel plugin (below), then `npx expo prebuild && npx expo run:android`. |
31
+ | **React Native CLI** | Add the babel plugin (below), then restart Metro with `--reset-cache` and rebuild the app. |
27
32
 
28
- ### React Native CLI
33
+ The babel plugin, added to your existing `babel.config.js`:
29
34
 
30
- Add the Babel plugin so your app's JSX routes through Kbach's JSX runtime
31
- instead of `react/jsx-runtime`:
35
+ ```js
36
+ // Expo — keep the function form your project already has:
37
+ module.exports = function (api) {
38
+ api.cache(true);
39
+ return {
40
+ presets: ['babel-preset-expo'],
41
+ plugins: ['@kbach/react-native/babel-plugin'],
42
+ };
43
+ };
44
+ ```
32
45
 
33
46
  ```js
34
- // babel.config.js
47
+ // React Native CLI:
35
48
  module.exports = {
36
49
  presets: ['module:@react-native/babel-preset'],
37
50
  plugins: ['@kbach/react-native/babel-plugin'],
38
51
  };
39
52
  ```
40
53
 
41
- Then rebuild the Android app (`npm run android`, or a clean Gradle build if
42
- you're re-running one that's already built) so Gradle picks up the new
43
- native module. See `apps/native-sandbox` in the
44
- [repo](https://github.com/semsakadanupol/kbach) for a full working
45
- reference project, including the Metro config a monorepo setup like this
46
- one needs (a plain single-app install doesn't need any of that — it's
47
- only relevant if you're linking packages locally instead of installing
48
- from npm).
49
-
50
- ### Expo (native — Android)
51
-
52
- **Expo Go can never work with this package, and that isn't specific to
53
- Kbach.** Expo Go is a single fixed binary Expo pre-builds and ships
54
- through the app stores, containing only the native code Expo bundled into
55
- it at their own build time — there is no mechanism for *any* third-party
56
- package to add native code to it at runtime, whether that's a raw
57
- TurboModule (what this package uses) or even Expo's own Modules API. This
58
- is exactly why the **dev client** concept exists: a custom build of your
59
- app — built once via `expo prebuild`/`expo run:android`, functionally the
60
- same "real native project" as the CLI path above — that *does* include
61
- this module:
54
+ A plain JS reload isn't enough after adding the plugin or editing
55
+ `kbach.config.js` — Metro's transform cache doesn't reliably pick up a
56
+ babel config change on its own. Restart with `--reset-cache` (`npx
57
+ react-native start --reset-cache` / `npx expo start --clear`).
58
+
59
+ Then write classes:
60
+
61
+ ```tsx
62
+ import { View, Text } from 'react-native';
63
+
64
+ <View className="flex-1 items-center justify-center bg-white dark:bg-black">
65
+ <Text className="text-lg font-bold">Hello Kbach</Text>
66
+ </View>;
67
+ ```
68
+
69
+ ## Theme
70
+
71
+ Optional. Create `kbach.config.js` at your project root — the babel plugin
72
+ picks it up automatically, no import or `applyKbachConfig()` call needed:
62
73
 
63
74
  ```js
64
- // babel.config.js
75
+ // kbach.config.js
65
76
  module.exports = {
66
- presets: ['babel-preset-expo'], // not '@react-native/babel-preset'
67
- plugins: ['@kbach/react-native/babel-plugin'],
77
+ extend: {
78
+ colors: {
79
+ brand: '#ff6b35',
80
+ surface: 'gray-2',
81
+ dark: { surface: 'gray-11' }, // makes `bg-surface` mode-aware
82
+ },
83
+ },
68
84
  };
69
85
  ```
70
86
 
71
- ```sh
72
- npx expo prebuild # generates android/ (and ios/, which this package doesn't support yet)
73
- npx expo run:android # builds and installs a dev client with the native module included
74
- ```
87
+ It's plain JavaScript — **quote any key containing a hyphen**
88
+ (`'surface-dim': '#121318'`).
75
89
 
76
- Also requires the New Architecture enabled (Expo's default in current SDK
77
- versions) — the native module is a TurboModule, which doesn't exist under
78
- the legacy bridge at all. If you're on an older SDK/config with the New
79
- Architecture off, turn it on in `app.json`
80
- (`"newArchEnabled": true` under `expo`) before prebuilding.
81
-
82
- EAS Build works the same way — it runs the same `prebuild` step, so no
83
- extra configuration beyond the two steps above.
84
-
85
- See `apps/expo-sandbox` in the [repo](https://github.com/semsakadanupol/kbach)
86
- for a full working reference project covering both this section and Expo
87
- Web below — including the `metro.config.js` a monorepo setup like this one
88
- needs (a plain single-app install doesn't need it).
89
-
90
- ### Expo Web
91
-
92
- **Works — no extra setup, no separate package, no init call.** `expo start
93
- --web` (and EAS's web builds) render through `react-native-web` —
94
- `View`/`Text`/etc. become real DOM elements with real CSS, not a
95
- Kotlin/JNI bridge, so on web this package resolves classes through the
96
- same Rust engine [`@kbach/react`](https://www.npmjs.com/package/@kbach/react)
97
- uses — WASM instead of JNI — automatically. Metro picks the right
98
- implementation per platform on its own via this package's `"browser"`
99
- export condition; your app code and JSX never change. The WASM module
100
- initializes itself eagerly (synchronously, at import time — no `await`
101
- needed before your first render, unlike a from-scratch WASM setup).
102
-
103
- The only thing this doesn't cover on web is an iOS-parity feature gap —
104
- everything web-side resolves through the exact same
105
- [`resolve_style.rs`](https://github.com/semsakadanupol/kbach/blob/main/packages/core-engine/src/resolve_style.rs)
106
- logic real native uses, so see this README's "What this is" section above
107
- for what's covered.
108
-
109
- ## What this is
110
-
111
- - A JSX runtime (`@kbach/react-native/jsx-runtime`) that intercepts
112
- `className`/`kb` props on every element and calls into the native/WASM
113
- bridge (whichever platform you're on) to resolve them to a style object,
114
- synchronously, on every render.
115
- - A Babel plugin (`@kbach/react-native/babel-plugin`) that routes your
116
- app's own files (never `node_modules`) through that JSX runtime via a
117
- per-file `@jsxImportSource` pragma.
118
- - `setTheme()`/`getTheme()` for customizing the color palette and spacing
119
- scale — the same `ThemeConfig` shape `@kbach/react` uses, kept as an
120
- independent type here so a native-only project isn't forced to depend on
121
- a web-oriented package.
122
- - `shadow-*` resolves to real native shadows — RN's discrete
123
- `shadowColor`/`shadowOffset`/`shadowOpacity`/`shadowRadius` props plus
124
- Android's `elevation` (both emitted together; `elevation` is what
125
- actually renders a shadow on Android — the `shadow*` props are iOS-only
126
- in RN itself, ready for whenever this package gets an iOS bridge),
127
- derived from the same named tiers (`sm`/`DEFAULT`/`md`/`lg`/`xl`/`2xl`)
128
- `@kbach/react`'s CSS `box-shadow` scale uses. `shadow-inner` and
129
- arbitrary `shadow-[...]` values aren't supported — RN's native shadow
130
- props can't express an inset shadow, and an arbitrary value is a raw CSS
131
- string with no mechanical translation into discrete native props.
132
- - Responsive breakpoints (`sm:`/`md:`/`lg:`/`xl:`/`2xl:`) — gated live
133
- against the current window width (`Dimensions.get('window').width`),
134
- same mobile-first "min-width and up" semantics as web, and freely
135
- combinable with `dark:`/`active:` (e.g. `dark:md:bg-blue-8`). Call RN's
136
- own `useWindowDimensions()` somewhere in your tree to re-render on
137
- rotation/resize — same role `useColorScheme()` plays for `dark:` below.
90
+ Full config reference (colors, spacing, breakpoints, fonts, dark mode) is
91
+ in [AGENTS.md](https://github.com/semsakadanupol/kbach/blob/main/AGENTS.md).
138
92
 
139
93
  ## Dark mode
140
94
 
141
- Works standalone, with zero provider — same architecture as `@kbach/react`:
95
+ Zero setup on every target. `dark:` classes react to the OS setting;
96
+ `useTheme()` gives you a manual toggle:
142
97
 
143
98
  ```tsx
144
- import { useGlobalDarkMode, toggleGlobalDarkMode } from '@kbach/react-native';
99
+ import { useTheme } from '@kbach/react-native';
145
100
 
146
- function ThemeToggle() {
147
- const isDark = useGlobalDarkMode();
148
- return <Pressable onPress={toggleGlobalDarkMode}><Text>{isDark ? 'Dark' : 'Light'}</Text></Pressable>;
149
- }
101
+ const { mode, isDark, setMode, toggle } = useTheme();
150
102
  ```
151
103
 
152
- Or the optional `<ThemeProvider>` / `useTheme()` pair, reading and writing
153
- the exact same global store:
104
+ Works anywhere, no provider. `<ThemeProvider>` is optional — for seeding a
105
+ startup default and opt-in persistence (`<ThemeProvider persist={AsyncStorage}>`).
106
+ Mount at most one — a second instance logs a dev warning, since dark mode
107
+ is one global store, not scoped per provider.
108
+
109
+ ## Dynamic values
110
+
111
+ For a value that changes at runtime without touching component state —
112
+ a live accent color, a user-picked theme:
154
113
 
155
114
  ```tsx
156
- import { ThemeProvider, useTheme } from '@kbach/react-native';
115
+ import { setDynamicToken, useDynamicToken } from '@kbach/react-native';
116
+
117
+ setDynamicToken('accent', '#8b5cf6');
157
118
 
158
- function Toggle() {
159
- const { mode, isDark, setMode, toggle } = useTheme();
160
- return <Pressable onPress={toggle}><Text>{mode}</Text></Pressable>;
161
- }
119
+ <View className="bg-[var(--accent)]" />; // updates instantly, everywhere it's used
120
+ const accent = useDynamicToken('accent'); // '#8b5cf6' | undefined, reactive
121
+ ```
122
+
123
+ Call `setDynamicToken` from a component's own render (guarded to fire
124
+ once), not from module top-level — see
125
+ [AGENTS.md §8](https://github.com/semsakadanupol/kbach/blob/main/packages/react-native/AGENTS.md)
126
+ for why and the exact pattern.
127
+
128
+ ## Colors as values
129
+
130
+ For a real color string rather than a class — a chart prop, a shadow color:
131
+
132
+ ```tsx
133
+ import { useColors } from '@kbach/react-native';
162
134
 
163
- <ThemeProvider defaultMode="system">
164
- <Toggle />
165
- </ThemeProvider>;
135
+ const colors = useColors();
136
+ colors.blue[6]; // '#2563eb'
137
+ colors.blue['6/50']; // same at 50% opacity
138
+ colors.get('brand'); // a custom color, typed as plain `string`
166
139
  ```
167
140
 
168
- By default the mode follows the OS `Appearance` setting live until you
169
- explicitly override it — and **isn't persisted across app launches**.
170
- There's no React-Native-builtin equivalent of `localStorage`, and adding
171
- one would mean forcing a real native dependency (e.g.
172
- `@react-native-async-storage/async-storage`) on every consumer, even ones
173
- that don't want it. If you want persistence, read your own storage at
174
- startup and call `setGlobalThemeMode()` with the result before your first
175
- render — the same "seed once" shape `<ThemeProvider defaultMode>` itself
176
- uses internally.
141
+ ## What differs on native
142
+
143
+ React Native has no CSS cascade, so a fixed subset of modifiers is
144
+ supported — `dark:`, `sm:`–`2xl:`, `active:`, `hover:`, `focus:`,
145
+ `disabled:`, and `data-[…]:`/`aria-[…]:`. `group-*`/`peer-*`/`has-[…]` and
146
+ container queries parse without error but do not apply. When two classes
147
+ set the same property the more-specific one wins regardless of source
148
+ order (`dark:bg-black bg-white` → black in dark mode).
149
+
150
+ `hover:` only fires for pointer-capable input (mouse/trackpad) — it's
151
+ inert on pure touch, so don't expect it to do anything on a phone/tablet
152
+ with no attached pointer.
153
+
154
+ `disabled:`/`aria-[…]:`/`data-[…]:` read that exact element's own props —
155
+ never a parent's, since there's no `group-disabled:`-style cascade to
156
+ inherit through. `<Pressable disabled><Text className="disabled:text-gray-5">`
157
+ does nothing to the `Text`, silently, because `Text` never received a
158
+ `disabled` prop of its own; the prop has to be repeated on whichever
159
+ element the modifier is written on:
160
+ `<Pressable disabled><Text disabled className="disabled:text-gray-5">`.
161
+
162
+ Expo Go, and iOS in ANY build type (no native engine there yet — see the
163
+ callout at the top), cover the same documented subset of utilities (no
164
+ grid, filters, gradients, or DOM-only typography). Expo Web and a real
165
+ **Android** dev-client/CLI build cover the full set.
166
+
167
+ A malformed or misused value fails safely — a deduped, dev-only
168
+ `console.warn` naming the offending class and the fix, never a crash or
169
+ silently-wrong output. Confirmed across CSS-injection attempts, invalid
170
+ `calc()` operands, and double-mounting.
171
+
172
+ `clsx()` — this package's own bundled copy, `import { clsx } from
173
+ '@kbach/react-native'` — composes conditional class strings the same way
174
+ the standalone `clsx` package does. You don't need it for `dark:`/`sm:`/
175
+ etc. (those work as plain classes), but it's there for `isActive &&
176
+ 'bg-blue-6'`-style composition.
177
+
178
+ See [this package's own AGENTS.md](https://github.com/semsakadanupol/kbach/blob/main/packages/react-native/AGENTS.md)
179
+ for the complete reference — modifier list, `calc()`/`min()`/`max()`/
180
+ `clamp()` support, dynamic tokens, monorepo setup, and failure modes; see
181
+ the [root AGENTS.md](https://github.com/semsakadanupol/kbach/blob/main/AGENTS.md)
182
+ for `kbach.config.js` and how the underlying engine is built.
177
183
 
178
184
  ## License
179
185