Imported from youversion/platform-sdk-reactnative-expo (
AGENTS.md). Install upstream withnpx skills add youversion/platform-sdk-reactnative-expo. Copyright stays with the author.
YouVersion Platform React Native Expo SDK
Wraps @youversion/platform-react-ui as Expo DOM components for React Native. Two packages: @youversion/platform-react-native-expo-ui and @youversion/platform-react-native-expo-core.
Keep this file brief. Put task-specific guidance behind a pointer.
Setup, Metro, native rebuild: CONTRIBUTING.md.
Consumer API: README.md.
Gotchas
- Worktree.
pnpm installat the worktree root first — iOS pods resolve via:path:into that worktree'snode_modules. Copyapps/example/.env. - Metro cache. Shared at
$TMPDIR/metro-cache. A DOM bundling error that names another worktree:cd apps/example && pnpm exec expo start --dev-client -c. - Android
localStorage. KeepensureDomLocalStorage().@expo/dom-webviewleaveslocalStoragenull; the Web SDK throws and the component paints blank. - Fonts. Brand fonts are SDK-owned via the Fonts API inside
YouVersionProvider. Children wait for bundled Inter; serif still loads in the background. There is no public ready API. Allowapi.youversion.comandcdn.youversion.com. If those hosts are blocked, serif falls back to Source Serif 4. Same path as web ADR 0004. After adding theexpo-fontpeer, rebuild the dev client. - Tests. Layers 1 (pure) and 3 (native). Do not mount
'use dom'in RNTL — swap DOM / NativeSheet / sibling sheets throughcomponent-implsand assert the bridge withlatestDomProps. Steer hooks throughhookOverrides. Do notjest.mockapp modules.jest.setup.jsmay shim native runtimes that cannot load in Jest. - Lint.
pnpm lintis type-aware oxlint (Expo DOM, native i18n, anti-slop). Do not suppress anti-slop rules. How to run:CONTRIBUTING.md.
Guardrails
- Mount Web SDK components only inside an Expo DOM wrapper, never in React Native.
Supply-Chain Protection
- Lift the cooldown with
pnpm install --config.minimumReleaseAge=0.--forcedoes not. A lockfile that resolved a too-new version reds CI until that version ages — lockfile verification runs on--frozen-lockfiletoo. - pnpm 11 blocks postinstall unless listed in
allowBuilds. Preferfalsefor packages that ship prebuilt binaries (unrs-resolver). - After
expo install --fix, re-pin the~ranges it wrote. Publisheddependencies/devDependenciesstay exact;peerDependenciesstay ranges. - Third-party version bumps: pick a release ≥3 days old.
@youversion/*is exempt.
Domain
Planning or domain language: CONTEXT.md and docs/adr/. Grill the plan with grill-with-docs.
Auth
Auth, grants, or data exchange: CONTEXT.md and ADRs 0014, 0015.
Highlights
Highlights, queue, drain, or permission flow: CONTEXT.md and ADRs 0013, 0016, 0017, 0018.
Bible Content Cache
Content cache, Cache-Control, lifetime, or sweep: CONTEXT.md and ADR 0020.
Sheets
NativeSheet, pickers, or verse actions: ADRs 0005, 0006, 0010, 0017.
Design Tokens
Tokens, color scheme, or palette: ADR 0021. Values live in packages/ui/src/theme/ — palette.ts (named hex) feeds semantic.ts (a role per scheme), and getTokens(scheme) returns one frozen object per scheme, by identity. The hook is packages/ui/src/hooks/use-tokens.ts; scheme comes from YouVersionProvider, and there is no second theme provider.
- Hex only.
theme/__tests__/tokens.test.tsscans the directory and fails onoklch(,rgb(, orrem. Alpha fills go throughwithAlphaat the call site. - Public, unlike the primitives.
getTokensanduseTokensare on the package namespace (pinned byexports.test.ts); the components that consume them are not. - No spacing scale. Radius, type, and font family only. Add a step with the component that needs it.
UI Primitives
Internal design-system primitives (Text, Button, …) live in packages/ui/src/components/ui/, exported from that barrel only — never src/index.ts (pinned by exports.test.ts).
- Compound pattern.
Object.assign(Root, { Slot, … }); auseXContext()that throws outside its root. RN inherits no text styles from parent views, so roots resolve foreground/size once and publish via context; slots consume it.Buttonis the reference: root publishes{ foreground, iconSize }. - Styling. Tokens via
useTokens(), variants viacreateVariants— importlib/variantsdirectly, not thelibbarrel (it drags the Web SDK into the native bundle). Callerstylemerges after the variant styles, but state styles (pressed, disabled) merge last so a caller cannot leave a dead control looking live. - Gotchas. Bare strings must sit inside
<Text>. Faces go throughsansFace(family, weight)— never a hand-writtenfontFamilyorfontWeight. The provider holds children until Inter registers, so primitives always name the mapped face. Alias RN'sTextwhere both are imported.
Localization
Native copy or locale keys: docs/contributing/native-i18n.md.
Distribution
Package entry, publishConfig, or tsconfig split: ADR 0011. react-dom stays a peer — Dependency Boundary in CONTEXT.md.
Release
Changeset or publish: PUBLISHING.md. RN publish failure: RELEASE-RUNBOOK.md.
