CraftReactNativeCraftReactNative

Theming

Learn how to switch between themes and customize colors, typography, spacing, and more using Unistyles.

View theme source

The theming system in CraftReactNative is built on Unistyles, providing a centralized approach to styling with support for multiple theme variants and dynamic theme switching.

Available themes

CraftReactNative comes with 4 pre-configured themes:

  • lightTeal - Light mode with teal color scheme
  • darkTeal - Dark mode with teal color scheme
  • lightOrange - Light mode with orange color scheme
  • darkOrange - Dark mode with orange color scheme

The theme configuration is defined in the craftrn-ui/themes folder and integrated with Unistyles for seamless access across your entire application.

What themes can customize

Themes in CraftReactNative aren't limited to colors. Each theme can customize:

  • Colors: Background, text, and interactive element colors
  • Typography: Font sizes, line heights, and font weights for all text variants
  • Spacing: Padding, margins, and gaps between elements
  • Border Radius: Corner rounding for cards, buttons, and other elements

This makes themes powerful for accessibility and personalization. For example, you could create:

  • Large Text Theme: Increased font sizes for better readability
  • Bold Theme: Heavier font weights for users who prefer stronger text
  • Compact Theme: Reduced spacing for more content density
  • High Contrast Theme: Stronger color contrasts for visual accessibility

Initial setup

During installation, the Unistyles configuration is set up automatically. The unistyles.ts file configures all available themes:

import { StyleSheet } from 'react-native-unistyles';
import {
  darkOrangeTheme,
  darkTealTheme,
  lightOrangeTheme,
  lightTealTheme,
} from './config';
 
StyleSheet.configure({
  themes: {
    lightTeal: lightTealTheme,
    darkTeal: darkTealTheme,
    lightOrange: lightOrangeTheme,
    darkOrange: darkOrangeTheme,
  },
  settings: {
    initialTheme: 'lightTeal',
  },
});

Make sure to import this file in your app's entry point:

App.tsx
import './craftrn-ui/themes/unistyles';

Switching themes

Using UnistylesRuntime

The UnistylesRuntime provides methods to programmatically switch themes:

import { UnistylesRuntime } from 'react-native-unistyles';
 
// Switch to a specific theme
UnistylesRuntime.setTheme('darkTeal');
 
// Get the current theme name
const currentTheme = UnistylesRuntime.themeName;
 
// Get all available theme names
const availableThemes = UnistylesRuntime.themeNames;

Example: Theme toggle component

Here's a complete example of a theme switcher component using the recommended StyleSheet.create approach:

import React from 'react';
import { View, Pressable, Text } from 'react-native';
import { UnistylesRuntime } from 'react-native-unistyles';
import { StyleSheet } from 'react-native-unistyles';
 
const ThemeSwitcher = () => {
  const currentTheme = UnistylesRuntime.themeName;
 
  const themes = [
    { name: 'lightTeal', label: 'Light Teal' },
    { name: 'darkTeal', label: 'Dark Teal' },
    { name: 'lightOrange', label: 'Light Orange' },
    { name: 'darkOrange', label: 'Dark Orange' },
  ] as const;
 
  return (
    <View>
      {themes.map(({ name, label }) => (
        <Pressable
          key={name}
          onPress={() => UnistylesRuntime.setTheme(name)}
          style={styles.button({ isActive: currentTheme === name })}
        >
          <Text style={styles.buttonText({ isActive: currentTheme === name })}>
            {label}
          </Text>
        </Pressable>
      ))}
    </View>
  );
};
 
const styles = StyleSheet.create(theme => ({
  button: ({ isActive }: { isActive: boolean }) => ({
    backgroundColor: isActive
      ? theme.colors.interactivePrimary
      : theme.colors.backgroundNeutral,
    padding: theme.spacings.medium,
    borderRadius: theme.borderRadius.medium,
  }),
  buttonText: ({ isActive }: { isActive: boolean }) => ({
    color: isActive
      ? theme.colors.interactivePrimaryContent
      : theme.colors.contentPrimary,
  }),
}));

Listening to theme changes

Recommended: When using StyleSheet.create, styles automatically update when the theme changes without causing component re-renders. This is the most performant approach:

import { StyleSheet } from 'react-native-unistyles';
import { UnistylesRuntime } from 'react-native-unistyles';
import { View, Text } from 'react-native';
 
const MyComponent = () => {
  const currentTheme = UnistylesRuntime.themeName;
 
  // Styles update automatically when theme changes - no re-render needed!
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Current theme: {currentTheme}</Text>
    </View>
  );
};
 
const styles = StyleSheet.create(theme => ({
  container: {
    backgroundColor: theme.colors.backgroundScreen,
  },
  text: {
    color: theme.colors.contentPrimary,
  },
}));

Note: If you need to access the theme name or other runtime values, use UnistylesRuntime.themeName directly instead of the useUnistyles hook to avoid unnecessary re-renders.

System theme detection

You can configure Unistyles to automatically follow the system theme:

import { UnistylesRuntime } from 'react-native-unistyles';
import { useColorScheme } from 'react-native';
 
// In your app initialization
const colorScheme = useColorScheme();
if (colorScheme === 'dark') {
  UnistylesRuntime.setTheme('darkTeal');
} else {
  UnistylesRuntime.setTheme('lightTeal');
}

Or use Unistyles' built-in adaptive theme feature:

StyleSheet.configure({
  themes: {
    lightTeal: lightTealTheme,
    darkTeal: darkTealTheme,
    // ...
  },
  settings: {
    adaptiveThemes: true, // Automatically switch based on system theme
  },
});

Customizing themes

To create your own theme variant, extend the theme configuration:

  1. Create a new theme object in craftrn-ui/themes/config.ts
  2. Add it to the Unistyles configuration in craftrn-ui/themes/unistyles.ts
  3. Update the TypeScript types to include your new theme

Example: Custom color theme

// In config.ts
export const lightCustomTheme = {
  colors: {
    // Your custom color tokens
    backgroundScreen: '#F5F5F5',
    contentPrimary: '#1A1A1A',
    interactivePrimary: '#6B46C1',
    // ... other color tokens
  },
  spacing: { /* ... */ },
  borderRadius: { /* ... */ },
  textVariants: { /* ... */ },
};
 
// In unistyles.ts
StyleSheet.configure({
  themes: {
    lightTeal: lightTealTheme,
    darkTeal: darkTealTheme,
    lightCustom: lightCustomTheme, // Add your custom theme
    // ...
  },
});

Example: Large text theme for accessibility

Create a theme with larger text for better readability:

// In config.ts
import { lightTealTheme } from './config';
 
export const largeTealTheme = {
  // Inherit all properties from lightTeal
  ...lightTealTheme,
  // Override text variants with larger sizes
  textVariants: {
    heading1: {
      fontSize: 36, // increased from 32
      lineHeight: 44, // increased from 40
      fontWeight: '700' as const,
    },
    heading2: {
      fontSize: 28, // increased from 24
      lineHeight: 36, // increased from 32
      fontWeight: '600' as const,
    },
    body1: {
      fontSize: 18, // increased from 16
      lineHeight: 26, // increased from 24
      fontWeight: '400' as const,
    },
    // ... other text variants
  },
};

Example: Bold theme

Create a theme with bolder text for users who prefer stronger typography:

export const boldTealTheme = {
  ...lightTealTheme,
  textVariants: {
    heading1: {
      ...lightTealTheme.textVariants.heading1,
      fontWeight: '800' as const, // increased from 700
    },
    heading2: {
      ...lightTealTheme.textVariants.heading2,
      fontWeight: '700' as const, // increased from 600
    },
    body1: {
      ...lightTealTheme.textVariants.body1,
      fontWeight: '500' as const, // increased from 400
    },
    // ... other text variants
  },
};

Example: Compact theme

Create a theme with reduced spacing for more content density:

export const compactTealTheme = {
  ...lightTealTheme,
  spacing: {
    xxsmall: 1, // reduced from 2
    xsmall: 2,  // reduced from 4
    small: 4,   // reduced from 8
    medium: 8,  // reduced from 12
    large: 12,  // reduced from 16
    xlarge: 16, // reduced from 24
    xxlarge: 24, // reduced from 32
  },
};

Best practices

  1. Use semantic tokens: Always use theme tokens (e.g., theme.colors.interactivePrimary, theme.textVariants.body1) instead of hardcoded values
  2. Test all themes: Ensure your components work correctly in all theme variants
  3. Persist user preference: Save the user's theme choice and restore it on app launch
  4. Respect system settings: Consider using adaptive themes to match user's system preferences
  5. Consider accessibility: Offer themes with larger text or bolder fonts for users who need them
  6. Maintain consistency: When creating custom themes, keep the same semantic structure as the base themes
  7. Smooth transitions: Unistyles handles theme transitions automatically, but you can add custom animations if needed

Accessibility

All themes in CraftReactNative are designed with accessibility in mind. Color contrasts are designed following WCAG guidelines, aiming to ensure that text remains readable and interactive elements are clearly distinguishable across all theme variants and modes.

Beyond colors, themes can be customized for additional accessibility needs:

  • Larger text sizes for users with visual impairments
  • Bolder font weights for improved readability
  • Increased spacing for easier touch targets
  • Reduced motion through theme-controlled animation preferences

Consider offering accessibility-focused theme variants alongside your standard themes to make your app usable by a wider audience.