BowlerKit

Customization & Theming

1/22/2026

Complete guide to customizing themes, colors, and design tokens in the Expo boilerplate.

This guide covers how to customize the visual appearance of your app, including creating new themes, modifying colors, and understanding the design token system.

Architecture Overview

The theming system is built on design tokens - a set of semantic variables that define colors, typography, spacing, and other visual properties. Each theme is completely self-contained with its own color palette, making customization straightforward.

design-tokens.ts
index.ts
app-theme.ts
design-tokens.model.ts
text-styles.ts
spacing.ts
colors.ts
modern.theme.ts
retro.theme.ts
cozy.theme.ts
paper.theme.ts

File Responsibilities

FilePurpose
design-tokens.tsBackward-compatible re-export from themes
themes/index.tsBarrel file that exports all theming components
app-theme.tsAppThemeRegistry for accessing tokens
design-tokens.model.tsDesignTokens interface and AppTheme enum
text-styles.tsTypography scale and text style definitions
spacing.tsSpacing, padding, layout, and size tokens
colors.tsAppColors palette and ThemeColorVariables
*theme.tsSelf-contained theme with *Colors object + tokens

Built-in Themes

The boilerplate includes four pre-built themes, each with its own color palette:

ThemeDescriptionBorder RadiusColor Object
ModernClean, professional SaaS aestheticMedium (8px)ModernColors
RetroBold, nostalgic vintage feelSharp (0px)RetroColors
CozyWarm, inviting with soft cornersLarge (18px)CozyColors
PaperMinimalist with pure scaffoldsLarge (18px)PaperColors

Each theme supports both light and dark modes automatically.

Using Design Tokens

Access design tokens anywhere in your app using the useTheme hook:

import { useTheme } from '@/constants/theme-context';

export function MyComponent() {
    const { tokens } = useTheme();

    return (
        <View
            style={{
                padding: tokens.layout.contentPadding,
                backgroundColor: tokens.cardBackground,
                borderRadius: tokens.cardBorderRadius,
            }}
        >
            <Text style={tokens.bodyPrimary}>Hello World</Text>
        </View>
    );
}

Available Token Categories

// Primary & Secondary
tokens.primary      // Brand primary color
tokens.onPrimary    // Text/icons on primary
tokens.secondary    // Brand secondary color

// Semantic Colors
tokens.success      // Success/positive
tokens.error        // Error/negative
tokens.warning      // Warning/caution
tokens.info         // Informational

// Surface Colors
tokens.background   // Main background
tokens.surface      // Cards/elevated surfaces
tokens.onSurface    // Text/icons on surface

// Variants & Utility
tokens.surfaceVariant
tokens.muted        // Disabled/placeholder
tokens.outline      // Borders/dividers

Creating a New Theme

Each theme is self-contained in a single file with its own color palette. Follow these steps to add a custom theme.

Create Theme File

Create a new file in constants/themes/definitions/:

constants/themes/definitions/ocean.theme.ts
/**
 * Ocean Theme
 *
 * A calming, aquatic theme inspired by the ocean.
 */

import { AppTheme, DesignTokens } from '../base/design-tokens.model';
import { AppTextStyles, ModernTypography } from '../base/text-styles';
import { BorderWidth, ComponentSizes, Layout, Padding, Spacing } from '../base/spacing';

// =============================================================================
// OCEAN THEME COLORS
// =============================================================================

/** Ocean theme color palette */
export const OceanColors = {
    // Primary & Secondary
    primary: '#0077B6',
    primaryDark: '#00B4D8',
    secondary: '#90E0EF',
    secondaryDark: '#CAF0F8',

    // Surface
    surfaceLight: '#F0F9FF',
    borderLight: '#ADE8F4',
    borderAccentLight: '#E0F7FF',
    borderAccentDark: '#023E8A',
} as const;

// Shared colors (copy from existing theme)
const SharedColors = {
    success: '#10B981',
    successDark: '#6EE7B7',
    error: '#EF4444',
    errorDark: '#FCA5A5',
    warning: '#F59E0B',
    warningDark: '#FCD34D',
    info: '#3B82F6',
    infoDark: '#60A5FA',
    background: '#FFFFFF',
    backgroundDark: '#03045E',
    surface: '#FFFFFF',
    surfaceDark: '#023E8A',
    borderDark: '#0077B6',
    borderSoft: 'rgba(0, 0, 0, 0.05)',
    borderSoftDark: 'rgba(255, 255, 255, 0.05)',
    onPrimary: '#FFFFFF',
    onPrimaryDark: '#000000',
    onSurface: '#000000',
    onSurfaceDark: '#FFFFFF',
    muted: '#737373',
    mutedDark: '#a1a1a1',
    surfaceVariant: '#F5F5F5',
    surfaceVariantDark: '#2A2A2A',
    outline: '#9CA3AF',
    outlineDark: '#6B7280',
} as const;

// =============================================================================
// OCEAN THEME TOKENS
// =============================================================================

/** Ocean theme (light mode) */
export const oceanLightTokens: DesignTokens = {
    theme: AppTheme.Ocean, // Will be added in next step
    brightness: 'light',
    primary: OceanColors.primary,
    onPrimary: SharedColors.onPrimary,
    secondary: OceanColors.secondary,
    success: SharedColors.success,
    error: SharedColors.error,
    warning: SharedColors.warning,
    info: SharedColors.info,
    background: SharedColors.background,
    surface: OceanColors.surfaceLight,
    surfaceVariant: SharedColors.surfaceVariant,
    outline: SharedColors.outline,
    muted: SharedColors.muted,
    onSurface: SharedColors.onSurface,

    typography: ModernTypography,
    bodyPrimary: AppTextStyles.bodyPrimary,
    bodySecondary: AppTextStyles.bodySecondary,
    mutedText: AppTextStyles.mutedText,

    buttonBorderRadius: 12,
    cardBorderRadius: 16,
    navbarBorderRadius: 24,
    spacing: Spacing,
    padding: Padding,
    layout: Layout,
    componentSizes: ComponentSizes,

    borderWidths: BorderWidth,
    borderColors: {
        default: OceanColors.borderLight,
        accent: OceanColors.borderAccentLight,
        muted: SharedColors.muted,
        soft: SharedColors.borderSoft,
        error: SharedColors.error,
    },
};

/** Ocean theme (dark mode) */
export const oceanDarkTokens: DesignTokens = {
    theme: AppTheme.Ocean,
    brightness: 'dark',
    primary: OceanColors.primaryDark,
    onPrimary: SharedColors.onPrimaryDark,
    secondary: OceanColors.secondaryDark,
    success: SharedColors.successDark,
    error: SharedColors.errorDark,
    warning: SharedColors.warningDark,
    info: SharedColors.infoDark,
    background: SharedColors.backgroundDark,
    surface: SharedColors.surfaceDark,
    surfaceVariant: SharedColors.surfaceVariantDark,
    outline: SharedColors.outlineDark,
    muted: SharedColors.mutedDark,
    onSurface: SharedColors.onSurfaceDark,

    typography: ModernTypography,
    bodyPrimary: AppTextStyles.bodyPrimaryDark,
    bodySecondary: AppTextStyles.bodySecondaryDark,
    mutedText: AppTextStyles.mutedTextDark,

    buttonBorderRadius: 12,
    cardBorderRadius: 16,
    navbarBorderRadius: 24,
    spacing: Spacing,
    padding: Padding,
    layout: Layout,
    componentSizes: ComponentSizes,

    borderWidths: BorderWidth,
    borderColors: {
        default: SharedColors.borderDark,
        accent: OceanColors.borderAccentDark,
        muted: SharedColors.mutedDark,
        soft: SharedColors.borderSoftDark,
        error: SharedColors.errorDark,
    },
};

Register the Theme

Update design-tokens.model.ts to add the theme enum:

constants/themes/base/design-tokens.model.ts
/** Supported application themes */
export enum AppTheme {
    Modern = 'modern',
    Retro = 'retro',
    Cozy = 'cozy',
    Paper = 'paper',
    Ocean = 'ocean', // Add your theme here
}

Update app-theme.ts to include your theme in the maps:

constants/themes/app-theme.ts
import { oceanLightTokens, oceanDarkTokens } from './definitions/ocean.theme';

const lightThemeMap: Record<AppTheme, DesignTokens> = {
    [AppTheme.Modern]: modernLightTokens,
    [AppTheme.Retro]: retroLightTokens,
    [AppTheme.Cozy]: cozyLightTokens,
    [AppTheme.Paper]: paperLightTokens,
    [AppTheme.Ocean]: oceanLightTokens, // Add here
};

const darkThemeMap: Record<AppTheme, DesignTokens> = {
    [AppTheme.Modern]: modernDarkTokens,
    [AppTheme.Retro]: retroDarkTokens,
    [AppTheme.Cozy]: cozyDarkTokens,
    [AppTheme.Paper]: paperDarkTokens,
    [AppTheme.Ocean]: oceanDarkTokens, // Add here
};

Export the Theme

Add export to the barrel file:

constants/themes/index.ts
// Theme definitions
export * from './definitions/modern.theme';
export * from './definitions/retro.theme';
export * from './definitions/cozy.theme';
export * from './definitions/paper.theme';
export * from './definitions/ocean.theme'; // Add this

Add to Theme Selector

Update the appearance settings to include your theme:

app/settings/appearance.tsx
const THEME_STYLE_OPTIONS: { style: AppTheme; label: string }[] = [
    { style: AppTheme.Modern, label: t('appearance.styles.modern') },
    { style: AppTheme.Retro, label: t('appearance.styles.retro') },
    { style: AppTheme.Cozy, label: t('appearance.styles.cozy') },
    { style: AppTheme.Paper, label: t('appearance.styles.paper') },
    { style: AppTheme.Ocean, label: 'Ocean' }, // Add here
];

Customizing Existing Themes

Each theme is self-contained in its own file, making customization easy.

Changing Primary Colors

Modify the color constants in the theme's *Colors object:

constants/themes/definitions/modern.theme.ts
export const ModernColors = {
    // Before: Blue primary
    primary: '#7aaae6',

    // After: Green primary
    primary: '#22C55E',
};

Changing Border Radii

Modify the layout tokens in the theme definition:

constants/themes/definitions/modern.theme.ts
export const modernLightTokens: DesignTokens = {
    // ...other tokens...

    // Change from 8 to 16 for more rounded corners
    buttonBorderRadius: 16,
    cardBorderRadius: 16,
    navbarBorderRadius: 20,
};

Changing Semantic Colors Per Theme

Since each theme defines its own colors, you can have different semantic colors:

const SharedColors = {
    // Retro theme uses different success color for vintage feel
    success: '#059669',  // Slightly different green
    error: '#DC2626',    // Different red shade
};

Accent Color System

The app supports dynamic accent colors that users can choose at runtime. These override certain colors based on the selected accent:

import { ACCENT_COLORS, useTheme } from '@/constants/theme-context';

export function MyComponent() {
    const { accentColor, setAccentColor, tokens } = useTheme();

    return (
        <View style={{ backgroundColor: tokens.accentSurface }}>
            {ACCENT_COLORS.map(color => (
                <Pressable
                    key={color.id}
                    onPress={() => setAccentColor(color)}
                    style={{ backgroundColor: color.color }}
                />
            ))}
        </View>
    );
}

Available Accent Colors

IDNameColor
greenGreen#10B981
blueBlue#3B82F6
indigoIndigo#6366F1
purplePurple#A855F7
pinkPink#EC4899
amberAmber#F59E0B

Adding More Accent Colors

Extend the ACCENT_COLORS array in theme-context.tsx:

constants/theme-context.tsx
export const ACCENT_COLORS: AccentColor[] = [
    // Existing colors...
    { id: 'green', name: 'Green', color: '#10B981', ... },
    
    // Add your colors
    { 
        id: 'teal', 
        name: 'Teal', 
        color: '#14B8A6',
        warmLight: '#F0FDFA',
        warmDark: '#042F2E',
        borderLight: '#CCFBF1',
        borderDark: '#0D3D3D',
    },
];

Theme Persistence

Theme settings are automatically persisted using AsyncStorage:

// Settings are stored with these keys
STORAGE_KEYS.THEME_MODE    // 'light' | 'dark' | 'system'
STORAGE_KEYS.THEME_STYLE   // 'modern' | 'retro' | 'cozy' | 'paper'
STORAGE_KEYS.ACCENT_COLOR  // Color ID

Settings are loaded on app startup and applied automatically.

Best Practices

Do's

  • ✅ Always use design tokens instead of hardcoded values
  • ✅ Keep all theme colors in the theme's *Colors object
  • ✅ Test themes in both light and dark modes
  • ✅ Ensure sufficient contrast for accessibility
  • ✅ Use the useTheme hook for reactive updates

Don'ts

  • ❌ Don't bypass the token system with inline colors
  • ❌ Don't forget to add dark mode variants
  • ❌ Don't use colors that fail accessibility contrast checks
  • ❌ Don't reference colors from other theme files (keep self-contained)

Color Tools & Resources