Skip to content

KJ-GM/theme-csx

Repository files navigation

theme-csx

NPM Downloads GitHub code size Status - active GitHub license

⭐️ Enjoying the library? Support it with a star on GitHub β€” thank you!

iOS Demo Android Demo

πŸ§ͺ Check out the Demo App Showcase to see theme-csx in action.

✨ Features

  • ⚑️ Light / Dark / System theme support
  • 🍏 Dynamic iOS color adaptation
  • πŸ’Ύ MMKV-based persistent theme storage
  • 🧠 Memoized themed styles with createThemedStyles
  • πŸ” Type-safe access to theme tokens (with autocomplete)
  • πŸš€ Scalable for monorepos and multi-app setups
  • 🧩 Extendable (spacing, typography, shadows, etc.)

βœ… iOS: theme changes apply instantly
πŸ” Android: theme changes apply on app restart

πŸ“¦ Installation

# npm
npm install theme-csx

# yarn
yarn add theme-csx

# pnpm
pnpm add theme-csx

πŸš€ Quick Start

1. Define your own theme

Create your own theme object.

βœ… colors.light is required and defines the base color palette.

βœ… colors.dark is optional, but must only override keys already defined in colors.light.

🎨 Everything else is optional and fully customizable β€” feel free to add anything like spacing, typography, radius, etc.

// theme/theme.ts

export const theme = {
  colors: {
    light: {
      background: '#ffffff',
      text: '#111111',
    },
    dark: {
      background: '#000000', // βœ… valid override
      text: '#ffffff', // βœ… valid override
      // error if an unknown key like "accent" is added here!
    },
  },
  spacing: {
    sm: 8,
    md: 16,
    lg: 24,
  },
  // Add any other tokens you want (typography, radius, etc.)
};

2. Create your theme system

Use createAppTheme() to initialize your theming system.

🚨 Critical: createAppTheme() must be called only once in your entire app. Calling it multiple times can cause unexpected behavior & theme conflicts.

You can enable persistent theme mode storage (optional) by setting { storage: true }.

⚠️ Requires react-native-mmkv if storage is enabled.

// theme/index.ts

import { createAppTheme } from 'theme-csx';
import { theme } from './theme';

export const {
  AppThemeProvider,
  useTheme,
  useThemeMode,
  useSetThemeMode,
  useResetThemeMode,
  useToggleThemeMode,
  useCycleThemeMode,
  createThemedStyles,
  createStaticStyles,
  types,
} = createAppTheme(theme, {
  storage: true, // Optional: disables persistence if omitted or set to false
});

export type Theme = typeof types.Theme;
export type ThemeMode = typeof types.ThemeMode;

3. Wrap your app

Wrap your app with AppThemeProvider and you are all set πŸš€.

// App.tsx
import { AppThemeProvider } from '@theme';

export default function App() {
  return (
    <AppThemeProvider>
      {/* your app code */}
    </AppThemeProvider>
  );
}

🎨 Usage

- Access current theme

import { useTheme } from '@theme';

const MyComponent = () => {
  const theme = useTheme();
  return <View style={{ backgroundColor: theme.colors.background }} />;
};

- Themed & Static Styles (Responsive vs Fixed)

import { View, Text } from 'react-native';
import { createThemedStyles, createStaticStyles } from '@theme';

// 🎨 Styles that respond to theme mode (light/dark/system)
const useThemedStyles = createThemedStyles((theme) => ({
  container: {
    flex: 1,
    backgroundColor: theme.colors.background,
    padding: theme.spacing.md,
  },
  text: {
    color: theme.colors.text,
  },
}));

// 🧱 Styles that use theme tokens but remain static across theme modes
const staticStyles = createStaticStyles((theme) => ({
  text: {
    fontSize: 18,
    fontWeight: 'bold',
    color: theme.colors.light.primary, // fixed value from light mode
  },
}));

const MyComponent = () => {
  const styles = useThemedStyles();

  return (
    <View style={styles.container}>
      <Text style={styles.text}>
        I react to theme mode changes
      </Text>
      <Text style={staticStyles.text}>
        I stay the same across all modes
      </Text>
    </View>
  );
};

- Toggle theme mode

import { useToggleThemeMode  } from '@theme';

const ToggleButton = () => {
  const toggleTheme = useToggleThemeMode();

  return <Button title="Toggle Theme" onPress={toggleTheme} />;
};

πŸ”§ Other Utilities

Once you initialize your theme system with createAppTheme(), you get access to the following utilities:

Utility Description
useTheme() Access the current theme (colors, colorMode, and custom tokens).
useThemeMode() Get the current theme mode (light, dark, or system).
useSetThemeMode() Change the theme mode programmatically.
useResetThemeMode() Reset to system theme mode (and clear stored preference if storage: true).
useToggleThemeMode() Toggle strictly between light and dark modes.
useCycleThemeMode() Cycle through modes: light β†’ dark β†’ system β†’ light.
createThemedStyles() Create memoized themed styles using your theme object.
createStaticStyles() Create static styles using your theme object.(non-reactive)

All of these must be used within your AppThemeProvider tree.

🧩 Best Practices

βœ… Use useTheme() for direct access to the theme

βœ… Use createThemedStyles() for most of your app β€” these styles respond to light/dark mode and adapt dynamically.

βœ… Use createStaticStyles() only when you need styles that remain fixed across all theme modes but still leverage theme tokens.

πŸ’‘ Define createThemedStyles() and createStaticStyles() outside of components for maximum efficiency & performance

🚫 Do not call createAppTheme() more than once per app

πŸ“œ License

MIT Β© KJ-GM

About

🌈 Powerful theming framework for React Native with dark mode, dynamic colors, and persistence.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published