@kbach/react-native 1.0.0-beta.7 → 1.0.0-beta.9
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 +70 -169
- 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-OJQAJLYT.mjs → chunk-DOWW44F7.mjs} +1 -1
- package/dist/chunk-H5Z24XX5.mjs +192 -0
- package/dist/{chunk-AEVOM2NB.mjs → chunk-HK6PDJJK.mjs} +54 -58
- package/dist/chunk-SY5OAY2C.mjs +1211 -0
- package/dist/chunk-X4UQCEOE.mjs +186 -0
- package/dist/chunk-YOC3SHF5.mjs +40 -0
- package/dist/chunk-Z664AKW7.mjs +54 -0
- package/dist/index.d.mts +24 -102
- package/dist/index.d.ts +24 -102
- package/dist/index.js +1387 -36
- package/dist/index.mjs +14 -8
- package/dist/index.web.d.mts +12 -4
- package/dist/index.web.d.ts +12 -4
- package/dist/index.web.js +266 -69
- package/dist/index.web.mjs +17 -11
- package/dist/jsx-dev-runtime.js +1246 -6
- package/dist/jsx-dev-runtime.mjs +4 -3
- package/dist/jsx-dev-runtime.web.js +124 -72
- package/dist/jsx-dev-runtime.web.mjs +4 -4
- package/dist/jsx-runtime.js +1246 -6
- package/dist/jsx-runtime.mjs +4 -3
- package/dist/jsx-runtime.web.js +109 -62
- package/dist/jsx-runtime.web.mjs +4 -6
- package/dist/useColors-CeP5_HvM.d.mts +230 -0
- package/dist/useColors-CeP5_HvM.d.ts +230 -0
- package/package.json +22 -4
- package/src/babel-plugin.js +74 -2
- package/dist/chunk-4KCCEMEN.mjs +0 -66
- package/dist/chunk-5WK55Z56.mjs +0 -14
- package/dist/chunk-FG5E4NLJ.mjs +0 -18
- package/dist/chunk-T3HPTH62.mjs +0 -161
package/README.md
CHANGED
|
@@ -2,34 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Kbach's utility-class styling engine for React Native — the same class
|
|
4
4
|
names as [`@kbach/react`](https://www.npmjs.com/package/@kbach/react),
|
|
5
|
-
resolved to a
|
|
5
|
+
resolved to a `StyleSheet`-ready style object instead of injected CSS.
|
|
6
6
|
|
|
7
|
-
>
|
|
8
|
-
>
|
|
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). Ships a prebuilt native
|
|
8
|
+
> module — no Rust toolchain or manual native wiring required.
|
|
13
9
|
|
|
14
|
-
##
|
|
10
|
+
## Install
|
|
15
11
|
|
|
16
12
|
```sh
|
|
17
13
|
npm install @kbach/react-native
|
|
18
14
|
```
|
|
19
15
|
|
|
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.
|
|
27
|
-
|
|
28
16
|
### React Native CLI
|
|
29
17
|
|
|
30
|
-
Add the Babel plugin so your app's JSX routes through Kbach's JSX runtime
|
|
31
|
-
instead of `react/jsx-runtime`:
|
|
32
|
-
|
|
33
18
|
```js
|
|
34
19
|
// babel.config.js
|
|
35
20
|
module.exports = {
|
|
@@ -38,188 +23,104 @@ module.exports = {
|
|
|
38
23
|
};
|
|
39
24
|
```
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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:
|
|
26
|
+
Rebuild the Android app afterward so Gradle picks up the native module.
|
|
27
|
+
|
|
28
|
+
### Expo Go
|
|
29
|
+
|
|
30
|
+
Works out of the box — no prebuild, no dev client, nothing to configure.
|
|
31
|
+
Expo Go can't load third-party native code, so this falls back to a pure-JS
|
|
32
|
+
engine automatically. Covers layout, spacing, border/radius, plain colors,
|
|
33
|
+
font-weight, text-transform/decoration, line-height, letter-spacing, and
|
|
34
|
+
shadows. Doesn't cover grid, transforms, filters, gradients, or DOM-only
|
|
35
|
+
typography — same scope native itself excludes.
|
|
36
|
+
|
|
37
|
+
### Expo (dev client / prebuild)
|
|
38
|
+
|
|
39
|
+
Gets you the full native engine instead of the JS fallback:
|
|
62
40
|
|
|
63
41
|
```js
|
|
64
42
|
// babel.config.js
|
|
65
43
|
module.exports = {
|
|
66
|
-
presets: ['babel-preset-expo'],
|
|
44
|
+
presets: ['babel-preset-expo'],
|
|
67
45
|
plugins: ['@kbach/react-native/babel-plugin'],
|
|
68
46
|
};
|
|
69
47
|
```
|
|
70
48
|
|
|
71
49
|
```sh
|
|
72
|
-
npx expo prebuild
|
|
73
|
-
npx expo run:android
|
|
50
|
+
npx expo prebuild
|
|
51
|
+
npx expo run:android
|
|
74
52
|
```
|
|
75
53
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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).
|
|
54
|
+
Requires the New Architecture enabled (`"newArchEnabled": true` in
|
|
55
|
+
`app.json`, on by default in current Expo SDKs). EAS Build works the same
|
|
56
|
+
way, no extra config.
|
|
89
57
|
|
|
90
58
|
### Expo Web
|
|
91
59
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`nativeBridge.web.ts`'s own doc comment) export conditions; your app code
|
|
102
|
-
and JSX never change. The WASM module initializes itself eagerly
|
|
103
|
-
(synchronously, at import time — no `await` needed before your first
|
|
104
|
-
render, unlike a from-scratch WASM setup).
|
|
105
|
-
|
|
106
|
-
The only thing this doesn't cover on web is an iOS-parity feature gap —
|
|
107
|
-
everything web-side resolves through the exact same
|
|
108
|
-
[`resolve_style.rs`](https://github.com/semsakadanupol/kbach/blob/main/packages/core-engine/src/resolve_style.rs)
|
|
109
|
-
logic real native uses, so see this README's "What this is" section above
|
|
110
|
-
for what's covered.
|
|
111
|
-
|
|
112
|
-
#### Expo Router static rendering: `dark:`/responsive on the FIRST paint
|
|
113
|
-
|
|
114
|
-
If you use [static rendering](https://docs.expo.dev/router/reference/static-rendering/)
|
|
115
|
-
(`web.output: "static"` — Expo Router's SSR/pre-render feature), any
|
|
116
|
-
`dark:`/`sm:`/etc. class on content that's part of the **initial,
|
|
117
|
-
pre-rendered HTML** resolves against whatever the **server** (Node.js, no
|
|
118
|
-
real display/viewport) reports — not the visiting browser's actual OS
|
|
119
|
-
preference or window size. React's hydration then adopts that
|
|
120
|
-
server-rendered markup as-is rather than silently overwriting it with the
|
|
121
|
-
client's own (correct) values, because a `style`-attribute-only difference
|
|
122
|
-
doesn't fail hydration the way a text-content mismatch does. Concretely: a
|
|
123
|
-
user with their OS in dark mode can see a `dark:` class resolve to its
|
|
124
|
-
*light* value on first paint, with no error or warning anywhere.
|
|
125
|
-
|
|
126
|
-
**This isn't a Kbach bug** — it's the same fundamental constraint every
|
|
127
|
-
OS-preference-based styling system has under SSR (Next.js's own dark-mode
|
|
128
|
-
docs describe the identical problem and fix). The standard fix applies
|
|
129
|
-
here unchanged: don't resolve preference-dependent classes on
|
|
130
|
-
initially-server-rendered content at all; defer them to after your app has
|
|
131
|
-
mounted client-side, e.g.:
|
|
60
|
+
Works out of the box — resolves through the same real-CSS engine
|
|
61
|
+
`@kbach/react` uses on the DOM, with full modifier support (`hover:`,
|
|
62
|
+
`focus:`, `group-hover:`, container queries, etc.), not just the
|
|
63
|
+
`dark:`/`active:`/responsive subset native supports.
|
|
64
|
+
|
|
65
|
+
**Static rendering (SSR) caveat:** `dark:`/responsive classes on content
|
|
66
|
+
that's part of the initial pre-rendered HTML resolve against the *server*,
|
|
67
|
+
not the visiting browser — so a user in dark mode can briefly see the light
|
|
68
|
+
version on first paint. Defer preference-dependent classes until mounted:
|
|
132
69
|
|
|
133
70
|
```tsx
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
function ThemedScreen() {
|
|
138
|
-
const [mounted, setMounted] = useState(false);
|
|
139
|
-
useEffect(() => setMounted(true), []);
|
|
140
|
-
|
|
141
|
-
// Neutral on the server AND on the client's first paint (matches exactly,
|
|
142
|
-
// no hydration mismatch) — only becomes preference-aware once mounted.
|
|
143
|
-
return (
|
|
144
|
-
<View className={mounted ? 'bg-gray-1 dark:bg-gray-12' : 'bg-gray-1'}>
|
|
145
|
-
<Text>Hello</Text>
|
|
146
|
-
</View>
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
```
|
|
71
|
+
const [mounted, setMounted] = useState(false);
|
|
72
|
+
useEffect(() => setMounted(true), []);
|
|
150
73
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
before your first `useEffect` runs.
|
|
154
|
-
|
|
155
|
-
## What this is
|
|
156
|
-
|
|
157
|
-
- A JSX runtime (`@kbach/react-native/jsx-runtime`) that intercepts
|
|
158
|
-
`className`/`kb` props on every element and calls into the native/WASM
|
|
159
|
-
bridge (whichever platform you're on) to resolve them to a style object,
|
|
160
|
-
synchronously, on every render.
|
|
161
|
-
- A Babel plugin (`@kbach/react-native/babel-plugin`) that routes your
|
|
162
|
-
app's own files (never `node_modules`) through that JSX runtime via a
|
|
163
|
-
per-file `@jsxImportSource` pragma.
|
|
164
|
-
- `setTheme()`/`getTheme()` for customizing the color palette and spacing
|
|
165
|
-
scale — the same `ThemeConfig` shape `@kbach/react` uses, kept as an
|
|
166
|
-
independent type here so a native-only project isn't forced to depend on
|
|
167
|
-
a web-oriented package.
|
|
168
|
-
- `shadow-*` resolves to real native shadows — RN's discrete
|
|
169
|
-
`shadowColor`/`shadowOffset`/`shadowOpacity`/`shadowRadius` props plus
|
|
170
|
-
Android's `elevation` (both emitted together; `elevation` is what
|
|
171
|
-
actually renders a shadow on Android — the `shadow*` props are iOS-only
|
|
172
|
-
in RN itself, ready for whenever this package gets an iOS bridge),
|
|
173
|
-
derived from the same named tiers (`sm`/`DEFAULT`/`md`/`lg`/`xl`/`2xl`)
|
|
174
|
-
`@kbach/react`'s CSS `box-shadow` scale uses. `shadow-inner` and
|
|
175
|
-
arbitrary `shadow-[...]` values aren't supported — RN's native shadow
|
|
176
|
-
props can't express an inset shadow, and an arbitrary value is a raw CSS
|
|
177
|
-
string with no mechanical translation into discrete native props.
|
|
178
|
-
- Responsive breakpoints (`sm:`/`md:`/`lg:`/`xl:`/`2xl:`) — gated live
|
|
179
|
-
against the current window width (`Dimensions.get('window').width`),
|
|
180
|
-
same mobile-first "min-width and up" semantics as web, and freely
|
|
181
|
-
combinable with `dark:`/`active:` (e.g. `dark:md:bg-blue-8`). Call RN's
|
|
182
|
-
own `useWindowDimensions()` somewhere in your tree to re-render on
|
|
183
|
-
rotation/resize — same role `useColorScheme()` plays for `dark:` below.
|
|
74
|
+
<View className={mounted ? 'bg-gray-1 dark:bg-gray-12' : 'bg-gray-1'} />;
|
|
75
|
+
```
|
|
184
76
|
|
|
185
77
|
## Dark mode
|
|
186
78
|
|
|
187
|
-
Works standalone, with zero provider — same architecture as `@kbach/react`:
|
|
188
|
-
|
|
189
79
|
```tsx
|
|
190
|
-
import {
|
|
80
|
+
import { useTheme } from '@kbach/react-native';
|
|
191
81
|
|
|
192
82
|
function ThemeToggle() {
|
|
193
|
-
const isDark =
|
|
194
|
-
return <Pressable onPress={
|
|
83
|
+
const { mode, isDark, setMode, toggle } = useTheme();
|
|
84
|
+
return <Pressable onPress={toggle}><Text>{mode}</Text></Pressable>;
|
|
195
85
|
}
|
|
196
86
|
```
|
|
197
87
|
|
|
198
|
-
|
|
199
|
-
|
|
88
|
+
Works anywhere, no provider needed. `<ThemeProvider defaultMode="dark">` is
|
|
89
|
+
optional, for seeding a startup default. Outside React, use
|
|
90
|
+
`getGlobalDarkMode()`/`toggleGlobalDarkMode()` instead.
|
|
91
|
+
|
|
92
|
+
Follows the OS setting live until overridden, and isn't persisted across
|
|
93
|
+
app launches (no built-in storage dependency). To persist, read your own
|
|
94
|
+
storage at startup and call `setGlobalThemeMode()` before your first render.
|
|
95
|
+
|
|
96
|
+
## Reading colors as values
|
|
97
|
+
|
|
98
|
+
For anywhere you need a real color, not a `className` — a chart prop, a
|
|
99
|
+
native shadow color:
|
|
200
100
|
|
|
201
101
|
```tsx
|
|
202
|
-
import {
|
|
102
|
+
import { useColors } from '@kbach/react-native';
|
|
203
103
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
104
|
+
const colors = useColors();
|
|
105
|
+
colors.blue[6]; // '#2563eb' — static, same value in both modes
|
|
106
|
+
colors.blue['6/50']; // same, at 50% opacity
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Any color can be defined in the theme as a plain string (static) or a
|
|
110
|
+
`{ light, dark }` pair — fully manual, resolved per the current dark-mode
|
|
111
|
+
state:
|
|
208
112
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
113
|
+
```ts
|
|
114
|
+
setTheme({
|
|
115
|
+
...defaultTheme,
|
|
116
|
+
colors: { ...defaultTheme.colors, brand: { light: '#2563eb', dark: '#93c5fd' } },
|
|
117
|
+
});
|
|
212
118
|
```
|
|
213
119
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`@react-native-async-storage/async-storage`) on every consumer, even ones
|
|
219
|
-
that don't want it. If you want persistence, read your own storage at
|
|
220
|
-
startup and call `setGlobalThemeMode()` with the result before your first
|
|
221
|
-
render — the same "seed once" shape `<ThemeProvider defaultMode>` itself
|
|
222
|
-
uses internally.
|
|
120
|
+
A shade-family lookup (`colors.blue[n]`) auto-mirrors to shade `13 - n` in
|
|
121
|
+
dark mode — this palette's own convention is 1 = lightest, 12 = darkest, so
|
|
122
|
+
that's the same visual weight relative to its own background, no manual
|
|
123
|
+
`colors.blue[isDark ? 7 : 6]` needed.
|
|
223
124
|
|
|
224
125
|
## License
|
|
225
126
|
|
|
Binary file
|
|
Binary file
|