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:
- Define theme-specific CSS variables in
global.css
- 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
@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:
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
@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
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:
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
- Check that the theme is registered in
extraThemes
array
- Verify all CSS variables are defined in the
@variant
- Restart Metro bundler
- Clear Metro cache:
npx expo start --clear
Missing styles
- Ensure all themes define the same set of CSS variables
- Check for typos in variable names
- Look for warnings in
__DEV__
mode about missing variables