npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codehogs/react-native-theme-engine

v1.0.0

Published

Semantic theming, system mode, persistence, and React Navigation integration for React Native.

Readme

@codehogs/react-native-theme-engine

Theme is product state, not just colors. This library gives you:

  • light | dark | system with first-class OS following via React Native’s useColorScheme
  • Semantic design tokens (background, surfaces, text roles, borders, states)
  • One derived navigation theme aligned with app tokens (React Navigation NavigationContainer)
  • Optional persistence through a small storage adapter (no hard-coded AsyncStorage in core)
  • Minimal rerenders: one context value; useThemedStyles memoizes on the resolved theme object

Peers: react, react-native (≥0.72). @react-navigation/native is optional at install time but recommended when you use navigators.


Install

npm install @codehogs/react-native-theme-engine react react-native

Optional (navigation):

npm install @react-navigation/native

Optional (persistence example):

npm install @react-native-async-storage/async-storage

Quick start

import {
  ThemeEngineProvider,
  useAppTheme,
  defaultLightTokens,
  defaultDarkTokens,
} from "@codehogs/react-native-theme-engine";

export default function App() {
  return (
    <ThemeEngineProvider
      light={defaultLightTokens}
      dark={defaultDarkTokens}
      defaultMode="system"
    >
      <Root />
    </ThemeEngineProvider>
  );
}

function Root() {
  const { theme, mode, resolvedMode, setMode } = useAppTheme();
  return (
    /* use theme.tokens.* for styles */
  );
}

Architecture (layers)

  1. Raw palettes — Your hex / PlatformColor choices live inside token files.
  2. Semantic tokensThemeTokens: colors, spacing, radius, typography with roles like textPrimary, surfaceElevated, borderFocus.
  3. Component tokens — Use mergeTokens or app-specific wrappers to add button/input/modal slices without polluting core.
  4. Runtime engine statemode (user preference), resolvedMode (after system), theme (AppTheme with navigation).

API reference

ThemeEngineProvider

| Prop | Type | Description | |------|------|-------------| | light | ThemeTokens | Tokens when resolved mode is light. | | dark | ThemeTokens | Tokens when resolved mode is dark. | | defaultMode | "light" \| "dark" \| "system" | Initial preference before storage hydrates. Default "system". | | themeId | string | Identifier on AppTheme.id. Default "default". | | storage | { get, set } | Optional persistence (see below). |

Children should read useAppTheme() below this provider.

useAppTheme()

Returns:

| Field | Description | |-------|-------------| | theme | AppTheme: id, mode (resolved "light" | "dark"), isDark, tokens, navigation. | | mode | User preference: "light" | "dark" | "system". | | resolvedMode | Effective mode after resolving system using the OS scheme. | | setMode(mode) | Sets preference and persists when storage is configured. | | toggleMode() | Switches between explicit light and dark (not system). | | isHydrated | false until async storage.get() finishes (always true if no storage). Use to avoid theme flash on cold start. |

useThemeMode()

Subset: mode, resolvedMode, setMode, toggleMode, isHydrated.

createThemeEngine(config)

Returns an isolated engine with its own React context:

  • Provider — same props as ThemeEngineProvider minus duplicate config (config is closed over).
  • useAppTheme / useThemeMode — scoped to that provider.
  • context — advanced testing / bridging.

Use when you need multiple engines or Storybook isolation.

getNavigationTheme(appTheme)

Returns appTheme.navigation, suitable for <NavigationContainer theme={...} />. Keeps navigation colors in sync with semantic tokens.

Also available: getNavigationThemeFromTokens(resolvedMode, tokens) and buildNavigationTheme(resolvedMode, tokens) for tests or tooling.

useThemedStyles(factory)

import { StyleSheet } from "react-native";
import { useThemedStyles } from "@codehogs/react-native-theme-engine";

const styles = useThemedStyles((theme) =>
  StyleSheet.create({
    screen: {
      flex: 1,
      backgroundColor: theme.tokens.colors.background,
    },
  })
);

Recomputes when theme changes (mode switch or new token references). Prefer a stable factory (module-level function or useCallback) if you extract it.

Token helpers

  • defaultLightTokens / defaultDarkTokens — Opinionated defaults (soft neutrals, not pure #000/#fff backgrounds).
  • mergeTokens(base, patch) — Shallow merge per section (colors, spacing, radius, typography).
  • withPrimaryBrand(tokens, primaryHex, { primaryText? }) — Dynamic brand primary with simple contrast default for primaryText.

Persistence: createThemePreferenceStorage(storage, key?)

Pass anything with getItem / setItem (e.g. AsyncStorage, MMKV):

import AsyncStorage from "@react-native-async-storage/async-storage";
import {
  ThemeEngineProvider,
  createThemePreferenceStorage,
} from "@codehogs/react-native-theme-engine";

const themeStorage = createThemePreferenceStorage(AsyncStorage, "@myapp/theme-mode");

export default function App() {
  return (
    <ThemeEngineProvider
      light={light}
      dark={dark}
      defaultMode="system"
      storage={themeStorage}
    >
      {/* ... */}
    </ThemeEngineProvider>
  );
}

React Navigation

import { NavigationContainer } from "@react-navigation/native";
import { useAppTheme, getNavigationTheme } from "@codehogs/react-native-theme-engine";

function AppNavigation() {
  const { theme } = useAppTheme();
  return (
    <NavigationContainer theme={getNavigationTheme(theme)}>
      {/* navigators */}
    </NavigationContainer>
  );
}

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.


Customizing themes

Define full ThemeTokens for each mode, or extend defaults:

import {
  defaultLightTokens,
  defaultDarkTokens,
  mergeTokens,
} from "@codehogs/react-native-theme-engine";

const light = mergeTokens(defaultLightTokens, {
  colors: {
    primary: "#7C3AED",
    primaryText: "#FFFFFF",
  },
});

const dark = mergeTokens(defaultDarkTokens, {
  colors: {
    primary: "#A78BFA",
    primaryText: "#1E1B4B",
  },
});

For platform-native colors, use PlatformColor inside your token objects where a string is expected (same as any React Native style).


React Native Paper (optional)

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.


Accessibility notes

  • Prefer semantic roles (textMuted, textDisabled, borderFocus) over raw grays so contrast stays intentional.
  • Do not rely on color alone for errors; pair danger with icons / copy.
  • Keep spacing and typography in tokens so touch targets stay consistent across themes.

Troubleshooting

  • Flash on launch with storage: Wait for isHydrated === true before rendering themed UI, or show a neutral splash.
  • Too many rerenders: Define light / dark at module scope or memoize; avoid recreating token objects every render.
  • System mode: resolvedMode updates when the OS appearance changes; mode stays "system" until the user picks a fixed mode.

Example

See example/App.example.js for NavigationContainer, AsyncStorage, and useThemedStyles together.


License

MIT.