{"_id":"@codehogs/react-native-theme-engine","name":"@codehogs/react-native-theme-engine","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@codehogs/react-native-theme-engine","version":"1.0.0","description":"Semantic theming, system mode, persistence, and React Navigation integration for React Native.","license":"MIT","author":"","repository":{"type":"git","url":"git+https://github.com/codehogs/react-native-theme-engine.git"},"keywords":["react-native","theme","dark-mode","navigation","design-tokens","semantic-colors"],"main":"./src/index.js","react-native":"./src/index.js","exports":{".":{"react-native":"./src/index.js","default":"./src/index.js"},"./package.json":"./package.json"},"sideEffects":false,"peerDependencies":{"@react-navigation/native":">=6.0.0","react":">=18.0.0","react-native":">=0.72.0"},"peerDependenciesMeta":{"@react-navigation/native":{"optional":true}},"engines":{"node":">=18"},"_id":"@codehogs/react-native-theme-engine@1.0.0","gitHead":"bb6ed6ebc2023552c0ec786f9aadd95d625206d9","bugs":{"url":"https://github.com/codehogs/react-native-theme-engine/issues"},"homepage":"https://github.com/codehogs/react-native-theme-engine#readme","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-/2sBdbRq8/njPgwCGQH45jJmckDQkCTeLv6nLMTOPla3sv8bbImQPaAIO59by8mfLmvrH4pIHjQ+i+xpBnnQuw==","shasum":"e76f5b4dd2866a0b17e6c4f26380eb5d5c9032ca","tarball":"https://registry.npmjs.org/@codehogs/react-native-theme-engine/-/react-native-theme-engine-1.0.0.tgz","fileCount":19,"unpackedSize":31601,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCPU670YtLKGekpGeZs4RWU8Al2kPS//e73xRm9nhtvSgIgVwnTIbAoGNRU6DLA8EI5KULO1EDNPB6bnhnAuO0y6NQ="}]},"_npmUser":{"name":"codehogs","email":"jananijayasuriya330@gmail.com"},"directories":{},"maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-theme-engine_1.0.0_1774334981032_0.9164360413868597"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-24T06:49:40.968Z","1.0.0":"2026-03-24T06:49:41.181Z","modified":"2026-03-24T06:49:41.355Z"},"maintainers":[{"name":"codehogs","email":"jananijayasuriya330@gmail.com"}],"description":"Semantic theming, system mode, persistence, and React Navigation integration for React Native.","homepage":"https://github.com/codehogs/react-native-theme-engine#readme","keywords":["react-native","theme","dark-mode","navigation","design-tokens","semantic-colors"],"repository":{"type":"git","url":"git+https://github.com/codehogs/react-native-theme-engine.git"},"bugs":{"url":"https://github.com/codehogs/react-native-theme-engine/issues"},"license":"MIT","readme":"# @codehogs/react-native-theme-engine\n\n**Theme is product state, not just colors.** This library gives you:\n\n- **`light` | `dark` | `system`** with first-class OS following via React Native’s `useColorScheme`\n- **Semantic design tokens** (background, surfaces, text roles, borders, states)\n- **One derived navigation theme** aligned with app tokens (React Navigation `NavigationContainer`)\n- **Optional persistence** through a small storage adapter (no hard-coded AsyncStorage in core)\n- **Minimal rerenders**: one context value; `useThemedStyles` memoizes on the resolved `theme` object\n\nPeers: `react`, `react-native` (≥0.72). `@react-navigation/native` is optional at install time but recommended when you use navigators.\n\n---\n\n## Install\n\n```bash\nnpm install @codehogs/react-native-theme-engine react react-native\n```\n\nOptional (navigation):\n\n```bash\nnpm install @react-navigation/native\n```\n\nOptional (persistence example):\n\n```bash\nnpm install @react-native-async-storage/async-storage\n```\n\n---\n\n## Quick start\n\n```javascript\nimport {\n  ThemeEngineProvider,\n  useAppTheme,\n  defaultLightTokens,\n  defaultDarkTokens,\n} from \"@codehogs/react-native-theme-engine\";\n\nexport default function App() {\n  return (\n    <ThemeEngineProvider\n      light={defaultLightTokens}\n      dark={defaultDarkTokens}\n      defaultMode=\"system\"\n    >\n      <Root />\n    </ThemeEngineProvider>\n  );\n}\n\nfunction Root() {\n  const { theme, mode, resolvedMode, setMode } = useAppTheme();\n  return (\n    /* use theme.tokens.* for styles */\n  );\n}\n```\n\n---\n\n## Architecture (layers)\n\n1. **Raw palettes** — Your hex / `PlatformColor` choices live inside token files.\n2. **Semantic tokens** — `ThemeTokens`: `colors`, `spacing`, `radius`, `typography` with roles like `textPrimary`, `surfaceElevated`, `borderFocus`.\n3. **Component tokens** — Use `mergeTokens` or app-specific wrappers to add button/input/modal slices without polluting core.\n4. **Runtime engine state** — `mode` (user preference), `resolvedMode` (after `system`), `theme` (`AppTheme` with `navigation`).\n\n---\n\n## API reference\n\n### `ThemeEngineProvider`\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `light` | `ThemeTokens` | Tokens when resolved mode is light. |\n| `dark` | `ThemeTokens` | Tokens when resolved mode is dark. |\n| `defaultMode` | `\"light\" \\| \"dark\" \\| \"system\"` | Initial preference before storage hydrates. Default `\"system\"`. |\n| `themeId` | `string` | Identifier on `AppTheme.id`. Default `\"default\"`. |\n| `storage` | `{ get, set }` | Optional persistence (see below). |\n\nChildren should read `useAppTheme()` **below** this provider.\n\n### `useAppTheme()`\n\nReturns:\n\n| Field | Description |\n|-------|-------------|\n| `theme` | `AppTheme`: `id`, `mode` (resolved `\"light\"` \\| `\"dark\"`), `isDark`, `tokens`, `navigation`. |\n| `mode` | User preference: `\"light\"` \\| `\"dark\"` \\| `\"system\"`. |\n| `resolvedMode` | Effective mode after resolving `system` using the OS scheme. |\n| `setMode(mode)` | Sets preference and persists when `storage` is configured. |\n| `toggleMode()` | Switches between explicit `light` and `dark` (not `system`). |\n| `isHydrated` | `false` until async `storage.get()` finishes (always `true` if no `storage`). Use to avoid theme flash on cold start. |\n\n### `useThemeMode()`\n\nSubset: `mode`, `resolvedMode`, `setMode`, `toggleMode`, `isHydrated`.\n\n### `createThemeEngine(config)`\n\nReturns an isolated engine with its **own** React context:\n\n- `Provider` — same props as `ThemeEngineProvider` minus duplicate config (config is closed over).\n- `useAppTheme` / `useThemeMode` — scoped to that provider.\n- `context` — advanced testing / bridging.\n\nUse when you need multiple engines or Storybook isolation.\n\n### `getNavigationTheme(appTheme)`\n\nReturns `appTheme.navigation`, suitable for `<NavigationContainer theme={...} />`. Keeps navigation colors in sync with semantic tokens.\n\nAlso available: `getNavigationThemeFromTokens(resolvedMode, tokens)` and `buildNavigationTheme(resolvedMode, tokens)` for tests or tooling.\n\n### `useThemedStyles(factory)`\n\n```javascript\nimport { StyleSheet } from \"react-native\";\nimport { useThemedStyles } from \"@codehogs/react-native-theme-engine\";\n\nconst styles = useThemedStyles((theme) =>\n  StyleSheet.create({\n    screen: {\n      flex: 1,\n      backgroundColor: theme.tokens.colors.background,\n    },\n  })\n);\n```\n\nRecomputes when `theme` changes (mode switch or new token references). Prefer a **stable** `factory` (module-level function or `useCallback`) if you extract it.\n\n### Token helpers\n\n- `defaultLightTokens` / `defaultDarkTokens` — Opinionated defaults (soft neutrals, not pure #000/#fff backgrounds).\n- `mergeTokens(base, patch)` — Shallow merge per section (`colors`, `spacing`, `radius`, `typography`).\n- `withPrimaryBrand(tokens, primaryHex, { primaryText? })` — Dynamic brand primary with simple contrast default for `primaryText`.\n\n### Persistence: `createThemePreferenceStorage(storage, key?)`\n\nPass anything with `getItem` / `setItem` (e.g. AsyncStorage, MMKV):\n\n```javascript\nimport AsyncStorage from \"@react-native-async-storage/async-storage\";\nimport {\n  ThemeEngineProvider,\n  createThemePreferenceStorage,\n} from \"@codehogs/react-native-theme-engine\";\n\nconst themeStorage = createThemePreferenceStorage(AsyncStorage, \"@myapp/theme-mode\");\n\nexport default function App() {\n  return (\n    <ThemeEngineProvider\n      light={light}\n      dark={dark}\n      defaultMode=\"system\"\n      storage={themeStorage}\n    >\n      {/* ... */}\n    </ThemeEngineProvider>\n  );\n}\n```\n\n---\n\n## React Navigation\n\n```javascript\nimport { NavigationContainer } from \"@react-navigation/native\";\nimport { useAppTheme, getNavigationTheme } from \"@codehogs/react-native-theme-engine\";\n\nfunction AppNavigation() {\n  const { theme } = useAppTheme();\n  return (\n    <NavigationContainer theme={getNavigationTheme(theme)}>\n      {/* navigators */}\n    </NavigationContainer>\n  );\n}\n```\n\n`theme.navigation` matches React Navigation’s theme shape (`dark`, `colors.primary`, `colors.background`, `colors.card`, `colors.text`, `colors.border`, `colors.notification`). Navigator UI and your screens share the same source of truth.\n\n---\n\n## Customizing themes\n\nDefine **full** `ThemeTokens` for each mode, or extend defaults:\n\n```javascript\nimport {\n  defaultLightTokens,\n  defaultDarkTokens,\n  mergeTokens,\n} from \"@codehogs/react-native-theme-engine\";\n\nconst light = mergeTokens(defaultLightTokens, {\n  colors: {\n    primary: \"#7C3AED\",\n    primaryText: \"#FFFFFF\",\n  },\n});\n\nconst dark = mergeTokens(defaultDarkTokens, {\n  colors: {\n    primary: \"#A78BFA\",\n    primaryText: \"#1E1B4B\",\n  },\n});\n```\n\nFor **platform-native** colors, use `PlatformColor` inside your token objects where a string is expected (same as any React Native style).\n\n---\n\n## React Native Paper (optional)\n\n`getPaperThemeOverrides(appTheme)` returns a partial you can merge with Paper’s MD3 theme. Refine fonts and component mappings in your app; Paper is not a required dependency.\n\n---\n\n## Accessibility notes\n\n- Prefer **semantic** roles (`textMuted`, `textDisabled`, `borderFocus`) over raw grays so contrast stays intentional.\n- Do not rely on color alone for errors; pair `danger` with icons / copy.\n- Keep spacing and typography in tokens so touch targets stay consistent across themes.\n\n---\n\n## Troubleshooting\n\n- **Flash on launch with storage**: Wait for `isHydrated === true` before rendering themed UI, or show a neutral splash.\n- **Too many rerenders**: Define `light` / `dark` at module scope or memoize; avoid recreating token objects every render.\n- **System mode**: `resolvedMode` updates when the OS appearance changes; `mode` stays `\"system\"` until the user picks a fixed mode.\n\n---\n\n## Example\n\nSee `example/App.example.js` for `NavigationContainer`, `AsyncStorage`, and `useThemedStyles` together.\n\n---\n\n## License\n\nMIT.\n","readmeFilename":"README.md","_rev":"1-d28fac6870021a789b48cc1e2d4c4797"}