@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.
- package/AGENTS.md +347 -0
- package/README.md +143 -137
- package/android/src/main/jniLibs/arm64-v8a/libkbach_core_engine.so +0 -0
- package/android/src/main/jniLibs/x86_64/libkbach_core_engine.so +0 -0
- package/dist/chunk-3KLOVONL.mjs +564 -0
- package/dist/chunk-SC3L5I7H.mjs +40 -0
- package/dist/chunk-XOH25F3E.mjs +112 -0
- package/dist/clsx-BLLSnqAa.d.ts +277 -0
- package/dist/darkModeStore.d.ts +73 -0
- package/dist/darkModeStore.js +118 -0
- package/dist/dynamicTokens.d.ts +53 -0
- package/dist/dynamicTokens.js +75 -0
- package/dist/index.d.ts +92 -151
- package/dist/index.js +2119 -393
- package/dist/index.web.d.ts +101 -4
- package/dist/index.web.js +438 -401
- package/dist/index.web.mjs +268 -14
- package/dist/jsx-dev-runtime.js +2238 -332
- package/dist/jsx-dev-runtime.web.js +207 -361
- package/dist/jsx-dev-runtime.web.mjs +19 -7
- package/dist/jsx-runtime.js +2238 -332
- package/dist/jsx-runtime.web.js +191 -353
- package/dist/jsx-runtime.web.mjs +3 -6
- package/dist/theme.d.ts +54 -0
- package/dist/{chunk-AEVOM2NB.mjs → theme.js} +86 -61
- package/package.json +44 -14
- package/src/babel-plugin.js +144 -2
- package/dist/chunk-4KCCEMEN.mjs +0 -66
- package/dist/chunk-DLIXI5OW.mjs +0 -53
- package/dist/chunk-FG5E4NLJ.mjs +0 -18
- package/dist/chunk-KSDFO34H.mjs +0 -32
- package/dist/chunk-OJQAJLYT.mjs +0 -14
- package/dist/chunk-ZUE6SCTE.mjs +0 -14
- package/dist/index.d.mts +0 -161
- package/dist/index.mjs +0 -32
- package/dist/index.web.d.mts +0 -7
- package/dist/jsx-dev-runtime.d.mts +0 -10
- package/dist/jsx-dev-runtime.mjs +0 -17
- package/dist/jsx-dev-runtime.web.d.mts +0 -10
- package/dist/jsx-runtime.d.mts +0 -7
- package/dist/jsx-runtime.mjs +0 -14
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
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
|
-
##
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
33
|
+
The babel plugin, added to your existing `babel.config.js`:
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
//
|
|
75
|
+
// kbach.config.js
|
|
65
76
|
module.exports = {
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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 {
|
|
99
|
+
import { useTheme } from '@kbach/react-native';
|
|
145
100
|
|
|
146
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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 {
|
|
115
|
+
import { setDynamicToken, useDynamicToken } from '@kbach/react-native';
|
|
116
|
+
|
|
117
|
+
setDynamicToken('accent', '#8b5cf6');
|
|
157
118
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
|
Binary file
|
|
Binary file
|