Skip to main content

Design system with CSS variables

NativeWind v4+ uses CSS variables (custom properties) to create flexible, customizable themes. This approach allows you to define design tokens once and reuse them throughout your application.

The @theme directive

Define your design tokens using the @theme directive in your CSS file:
global.css
@theme {
  --elevation-xs: 1;
  --elevation-sm: 3;
  --elevation-md: 6;
  --elevation-lg: 8;
  --elevation-xl: 13;
  --elevation-2xl: 24;
  --elevation-none: 0;
}
These variables can then be referenced in your custom utilities:
@utility elevation-* {
  -rn-elevation: --value(--elevation-*);
}
The @theme directive is processed at build time by the Tailwind CSS compiler, ensuring optimal performance.

Color customization

NativeWind supports the full Tailwind color palette, but you can customize it to match your brand.

Using default colors

All Tailwind color utilities work out of the box:
<Text className="text-blue-500">Blue text</Text>
<Text className="text-red-600">Red text</Text>
<Text className="text-green-400">Green text</Text>
<Text className="text-gray-700">Gray text</Text>

Custom color utilities

Create custom color utilities that reference your theme variables:
theme.css
@utility color-* {
  color: --value(--color-*);
}

@utility tint-* {
  -rn-tint: --value(--color-*);
  @prop -rn-tint tint;
}
Now you can use these utilities in your components:
<Text className="color-primary">Primary color text</Text>
<Image source={icon} className="tint-accent" />

Platform-specific theming

Define different design tokens for different platforms using media queries:

Font families

theme.css
:root {
  @media ios {
    --font-sans: System;
    --font-serif: Georgia;
    --font-mono: Menlo;
  }

  @media android {
    --font-sans: SystemAndroid;
    --font-serif: sans-serif;
    --font-mono: monospace;
  }
}
Use these fonts in your components:
<Text className="font-sans">System font for each platform</Text>
<Text className="font-mono">Monospace font for each platform</Text>
Platform-specific CSS variables automatically apply the correct value based on where your app is running.

Spacing and sizing

Create a consistent spacing system:
theme.css
@theme {
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  --spacing-2xl: 3rem;
}
Reference these in custom utilities:
@utility gap-* {
  gap: --value(--spacing-*);
}

Creating a design system

Build a comprehensive design system by organizing your theme variables:
1

Define your color palette

Create semantic color names that describe their purpose:
theme.css
@theme {
  /* Brand colors */
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  --color-accent: #f59e0b;
  
  /* Semantic colors */
  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;
  --color-info: #3b82f6;
  
  /* Neutral colors */
  --color-background: #ffffff;
  --color-surface: #f9fafb;
  --color-border: #e5e7eb;
  --color-text-primary: #111827;
  --color-text-secondary: #6b7280;
}
2

Create typography tokens

Define font sizes, weights, and line heights:
theme.css
@theme {
  /* Font sizes */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;
  
  /* Font weights */
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;
}
3

Add spacing and layout tokens

Establish consistent spacing throughout your app:
theme.css
@theme {
  /* Spacing scale */
  --space-0: 0;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  
  /* Border radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;
  --radius-full: 9999px;
}
4

Define shadow and elevation

Create depth with consistent shadow styles:
theme.css
@theme {
  /* Elevation for Android */
  --elevation-xs: 1;
  --elevation-sm: 3;
  --elevation-md: 6;
  --elevation-lg: 8;
  --elevation-xl: 13;
  --elevation-2xl: 24;
  
  /* Shadow colors (for iOS/web) */
  --shadow-color: rgba(0, 0, 0, 0.1);
  --shadow-color-dark: rgba(0, 0, 0, 0.3);
}

Using theme variables in components

Once you’ve defined your design system, use it consistently across your components:
import { Pressable, Text } from 'react-native';

export function Button({ children, variant = 'primary' }) {
  const baseClasses = 'px-6 py-3 rounded-lg';
  const variantClasses = {
    primary: 'bg-blue-500 active:bg-blue-600',
    secondary: 'bg-purple-500 active:bg-purple-600',
    outline: 'border-2 border-blue-500 active:bg-blue-50',
  };
  
  return (
    <Pressable className={`${baseClasses} ${variantClasses[variant]}`}>
      <Text className="text-white font-semibold text-center">
        {children}
      </Text>
    </Pressable>
  );
}

Dynamic theming with context

For runtime theme switching, use React Context with NativeWind’s VariableContextProvider:
theme-provider.tsx
import { createContext, useContext, useState } from 'react';
import { VariableContextProvider } from 'nativewind';

const ThemeContext = createContext(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const themeVars = {
    '--color-background': theme === 'light' ? '#ffffff' : '#111827',
    '--color-text': theme === 'light' ? '#111827' : '#f9fafb',
  };
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <VariableContextProvider variables={themeVars}>
        {children}
      </VariableContextProvider>
    </ThemeContext.Provider>
  );
}

export const useTheme = () => useContext(ThemeContext);
Runtime theme switching with Context has performance implications. For most apps, use the built-in dark mode support instead.

Safe area support

NativeWind includes support for safe area utilities through the tailwindcss-safe-area plugin:
global.css
@import "tailwindcss-safe-area";
Use safe area utilities in your components:
<View className="pt-safe">
  <Text>Content respects safe area</Text>
</View>

<View className="pb-safe px-4">
  <Text>Bottom safe area padding</Text>
</View>

Best practices

Name your theme variables based on their purpose, not their appearance:
/* Good: Semantic names */
--color-primary
--color-danger
--color-text-body

/* Avoid: Appearance-based names */
--color-blue
--color-red
--color-dark-gray
Only use platform-specific values when truly necessary. Most design tokens should work across all platforms:
/* Good: Universal tokens */
@theme {
  --color-primary: #3b82f6;
  --spacing-md: 1rem;
}

/* Platform-specific only when needed */
:root {
  @media ios {
    --font-sans: System;
  }
  @media android {
    --font-sans: SystemAndroid;
  }
}
Create a reference for your design tokens so your team understands when to use each one:
// design-system.ts
export const designTokens = {
  colors: {
    primary: 'Primary brand color for CTAs and key actions',
    secondary: 'Secondary brand color for accents',
    // ...
  },
  spacing: {
    sm: 'Use for tight spacing between related elements',
    md: 'Default spacing for most UI elements',
    // ...
  },
};
Design tokens may render differently on iOS, Android, and web. Always test your theme across platforms:
  • Check font rendering and sizing
  • Verify shadow and elevation appearance
  • Test color contrast ratios
  • Validate spacing and layout

Next steps

Dark mode

Add dark mode support to your theme

CSS variables

Deep dive into CSS variable customization

Tailwind config

Extend Tailwind with custom configuration

Plugins

Create custom Tailwind plugins