Skip to main content

Overview

While Uniwind provides light and dark themes by default, you can create unlimited custom themes for advanced use cases. Custom themes are perfect for branding variations, seasonal themes, accessibility modes, or any scenario where you need more than two color schemes.
Custom themes work exactly like the default themes, with full support for theme switching and CSS variable management.

Creating a Custom Theme

Creating a custom theme involves two steps:
  1. Define theme-specific CSS variables in global.css
  2. Register the theme in metro.config.js

Step 1: Define Theme Variables in global.css

Add your custom theme using the @variant directive. All themes must define the same set of variables.
Important: Every theme must define the same CSS variables. If you add a variable to one theme, add it to all themes. Uniwind will warn you in __DEV__ mode if variables are missing.

Example: Adding a “Premium” Theme

global.css
@import 'tailwindcss';
@import 'uniwind';

@layer theme {
  :root {
    /* Dark theme variables */
    @variant dark {
      --color-background: #000000;
      --color-foreground: #ffffff;
      --color-primary: #3b82f6;
      --color-card: #1f2937;
      --color-border: #374151;
    }

    /* Light theme variables */
    @variant light {
      --color-background: #ffffff;
      --color-foreground: #000000;
      --color-primary: #3b82f6;
      --color-card: #ffffff;
      --color-border: #e5e7eb;
    }

    /* Premium theme variables */
    @variant premium {
      --color-background: #1e1b4b;
      --color-foreground: #fef3c7;
      --color-primary: #fbbf24;
      --color-card: #312e81;
      --color-border: #4c1d95;
    }
  }
}

Step 2: Register Theme in metro.config.js

Add your custom theme to the extraThemes array in your Metro configuration:
metro.config.js
module.exports = withUniwindConfig(config, {
  cssEntryFile: './src/global.css',
  dtsFile: './src/uniwind-types.d.ts',
  extraThemes: ['premium'], // Register your custom theme here
});
After adding a new theme, restart your Metro bundler for the changes to take effect.

Step 3: Use Your Custom Theme

Switch to your custom theme programmatically:
import { Uniwind } from 'uniwind'

// Switch to premium theme
Uniwind.setTheme('premium')

Complete Example: Multiple Custom Themes

Here’s a complete example with multiple custom themes for different use cases:

global.css with Multiple Themes

global.css
@import 'tailwindcss';
@import 'uniwind';

@layer theme {
  :root {
    @variant dark {
      --color-background: oklch(0.1316 0.0041 17.69);
      --color-foreground: oklch(0.98 0 0);
      --color-primary: oklch(0.6 0.2 240);
      --color-card: oklch(0.2 0.01 240);
      --color-border: oklch(0.3 0.02 240);
    }

    @variant light {
      --color-background: oklch(1 0 0);
      --color-foreground: oklch(0.2 0 0);
      --color-primary: oklch(0.5 0.2 240);
      --color-card: oklch(0.98 0 0);
      --color-border: oklch(0.9 0.01 240);
    }

    /* Ocean theme - Cool blues and teals */
    @variant ocean {
      --color-background: oklch(0.25 0.05 220);
      --color-foreground: oklch(0.95 0.02 200);
      --color-primary: oklch(0.6 0.15 200);
      --color-card: oklch(0.3 0.06 215);
      --color-border: oklch(0.4 0.08 210);
    }

    /* Sunset theme - Warm oranges and purples */
    @variant sunset {
      --color-background: oklch(0.3 0.08 30);
      --color-foreground: oklch(0.95 0.03 60);
      --color-primary: oklch(0.65 0.18 40);
      --color-card: oklch(0.35 0.1 25);
      --color-border: oklch(0.45 0.12 35);
    }

    /* Forest theme - Natural greens */
    @variant forest {
      --color-background: oklch(0.2 0.05 150);
      --color-foreground: oklch(0.95 0.02 140);
      --color-primary: oklch(0.55 0.15 145);
      --color-card: oklch(0.25 0.06 155);
      --color-border: oklch(0.35 0.08 150);
    }

    /* High Contrast theme - Accessibility focused */
    @variant high-contrast {
      --color-background: oklch(0 0 0);
      --color-foreground: oklch(1 0 0);
      --color-primary: oklch(0.7 0.25 60);
      --color-card: oklch(0.1 0 0);
      --color-border: oklch(1 0 0);
    }
  }
}

metro.config.js with Multiple Themes

metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withUniwindConfig } = require('uniwind/metro');

const config = getDefaultConfig(__dirname);

module.exports = withUniwindConfig(config, {
  cssEntryFile: './src/global.css',
  dtsFile: './src/uniwind-types.d.ts',
  extraThemes: ['ocean', 'sunset', 'forest', 'high-contrast'],
});

Theme Switcher Component

Create a theme switcher that includes your custom themes:
ThemeSwitcher.tsx
import { View, Pressable, Text, ScrollView } from 'react-native'
import { Uniwind, useUniwind } from 'uniwind'

export const ThemeSwitcher = () => {
  const { theme } = useUniwind()

  const themes = [
    { name: 'light', label: 'Light', icon: '☀️' },
    { name: 'dark', label: 'Dark', icon: '🌙' },
    { name: 'ocean', label: 'Ocean', icon: '🌊' },
    { name: 'sunset', label: 'Sunset', icon: '🌅' },
    { name: 'forest', label: 'Forest', icon: '🌲' },
    { name: 'high-contrast', label: 'High Contrast', icon: '♿' },
  ]

  return (
    <ScrollView horizontal className="p-4">
      <View className="flex-row gap-2">
        {themes.map((t) => (
          <Pressable
            key={t.name}
            onPress={() => Uniwind.setTheme(t.name)}
            className={`
              px-4 py-3 rounded-lg
              ${theme === t.name ? 'bg-primary' : 'bg-card border border-border'}
            `}
          >
            <Text className={`text-center ${theme === t.name ? 'text-white' : 'text-foreground'}`}>
              {t.icon}
            </Text>
            <Text className={`text-xs mt-1 ${theme === t.name ? 'text-white' : 'text-foreground'}`}>
              {t.label}
            </Text>
          </Pressable>
        ))}
      </View>
    </ScrollView>
  )
}

Best Practices

Keep variable names consistent: Use the same variable names across all themes. This ensures components look correct regardless of the active theme.
Test all themes: Always test your UI with every theme to ensure proper contrast and readability.
Use OKLCH for better colors: Consider using OKLCH color space for more perceptually uniform themes. See the Global CSS guide for details.
Don’t forget to restart Metro: Changes to metro.config.js require a Metro bundler restart to take effect.

Troubleshooting

Theme not appearing

  1. Check that the theme is registered in extraThemes array
  2. Verify all CSS variables are defined in the @variant
  3. Restart Metro bundler
  4. Clear Metro cache: npx expo start --clear

Missing styles

  1. Ensure all themes define the same set of CSS variables
  2. Check for typos in variable names
  3. Look for warnings in __DEV__ mode about missing variables
I