---
title: Theming
description: Customize HeroUI Native's design system with CSS variables and global styles
icon: palette
links:
  themes: true
  tailwind: theme
---

HeroUI Native uses CSS variables for theming. Customize everything from colors to component styles using standard CSS.

## How It Works

HeroUI Native's theming system is built on top of [Tailwind CSS v4](https://tailwindcss.com/docs/theme)'s theme via [Uniwind](https://uniwind.dev/). When you import `heroui-native/styles`, it uses Tailwind's built-in color palettes, maps them to semantic variables, automatically switches between light and dark themes, and uses CSS layers and the `@theme` directive for organization.

**Naming pattern:**

- Colors without a suffix are backgrounds (e.g., `--accent`)
- Colors with `-foreground` are for text on that background (e.g., `--accent-foreground`)

## Quick Start

**Apply colors in your components:**

```tsx
import { View, Text } from 'react-native';

<View className="bg-background flex-1">
  <Text className="text-foreground">Your app content</Text>
</View>;
```

**Switch themes:**

HeroUI Native automatically supports dark mode through [Uniwind](https://docs.uniwind.dev/theming/basics). The theme switches between light and dark variants based on system preferences or manual selection:

```tsx
import { Uniwind, useUniwind } from 'uniwind';
import { Button } from 'heroui-native';

function ThemeToggle() {
  const { theme } = useUniwind();

  return (
    <Button
      onPress={() => Uniwind.setTheme(theme === 'light' ? 'dark' : 'light')}
    >
      <Button.Label>
        Toggle {theme === 'light' ? 'Dark' : 'Light'} Mode
      </Button.Label>
    </Button>
  );
}
```

**Override colors:**

```css
/* global.css */
@layer theme {
  @variant light {
    /* Override any color variable */
    --accent: oklch(0.65 0.25 270); /* Custom indigo accent */
    --success: oklch(0.65 0.15 155);
  }

  @variant dark {
    --accent: oklch(0.65 0.25 270);
    --success: oklch(0.75 0.12 155);
  }
}
```

> **Note**: See [Colors](/docs/native/getting-started/colors) for the complete color palette and visual reference.

**Create your own theme:**

Create multiple themes using Uniwind's variant system. For complete custom theme documentation, see the [Uniwind Custom Themes Guide](https://docs.uniwind.dev/theming/custom-themes).

<Callout type="warning">
  **Important:** All themes must define the same variables. See the [Default Theme](/docs/native/getting-started/colors#default-theme) section for a complete list of all required variables.
</Callout>

```css
/* global.css */
@layer theme {
  :root {
    @variant ocean-light {
      /* Base Colors */
      --background: oklch(0.95 0.02 230);
      --foreground: oklch(0.25 0.04 230);

     /* Surface: Used for non-overlay components (cards, accordions, disclosure groups) */
      --surface: oklch(0.98 0.01 230);
      --surface-foreground: oklch(0.3 0.045 230);

      --surface-secondary: oklch(0.96 0.012 230);
      --surface-secondary-foreground: oklch(0.3 0.045 230);

      --surface-tertiary: oklch(0.94 0.015 230);
      --surface-tertiary-foreground: oklch(0.3 0.045 230);

      /* Overlay: Used for floating/overlay components (dialogs, popovers, modals, menus) */
      --overlay: oklch(0.998 0.003 230);
      --overlay-foreground: oklch(0.3 0.045 230);
      --backdrop: oklch(0% 0 0 / 20%);

      --muted: oklch(0.55 0.035 230);

      --default: oklch(0.94 0.018 230);
      --default-foreground: oklch(0.4 0.05 230);

      /* Accent */
      --accent: oklch(0.6 0.2 230);
      --accent-foreground: oklch(0.98 0.005 230);

      /* Form Field Defaults - Colors */
      --field-background: oklch(0.98 0.01 230);
      --field-foreground: oklch(0.25 0.04 230);
      --field-placeholder: var(--muted);
      --field-border: transparent;

      /* Status Colors */
      --success: oklch(0.72 0.14 165);
      --success-foreground: oklch(0.25 0.08 165);

      --warning: oklch(0.78 0.12 85);
      --warning-foreground: oklch(0.3 0.08 85);

      --danger: oklch(0.68 0.18 15);
      --danger-foreground: oklch(0.98 0.005 15);

      /* Component Colors */
      --segment: oklch(0.98 0.01 230);
      --segment-foreground: oklch(0.25 0.04 230);

      /* Misc Colors */
      --border: oklch(0 0 0 / 0%);
      --separator: oklch(0.91 0.015 230);
      --focus: var(--accent);
      --link: oklch(0.62 0.17 230);

      /* Shadows */
      --surface-shadow:
        0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
        0 0 1px 0 rgba(0, 0, 0, 0.06);
      --overlay-shadow:
        0 2px 8px 0 rgba(0, 0, 0, 0.02), 0 -6px 12px 0 rgba(0, 0, 0, 0.01),
        0 14px 28px 0 rgba(0, 0, 0, 0.03);
      --field-shadow:
        0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
        0 0 1px 0 rgba(0, 0, 0, 0.06);
    }

    @variant ocean-dark {
      /* Base Colors */
      --background: oklch(0.15 0.04 230);
      --foreground: oklch(0.94 0.01 230);

      /* Surface: Used for non-overlay components (cards, accordions, disclosure groups) */
      --surface: oklch(0.2 0.048 230);
      --surface-foreground: oklch(0.9 0.015 230);

      --surface-secondary: oklch(0.24 0.046 230);
      --surface-secondary-foreground: oklch(0.9 0.015 230);

      --surface-tertiary: oklch(0.27 0.044 230);
      --surface-tertiary-foreground: oklch(0.9 0.015 230);

      /* Overlay: Used for floating/overlay components (dialogs, popovers, modals, menus) */
      --overlay: oklch(0.23 0.045 230);
      --overlay-foreground: oklch(0.9 0.015 230);
      --backdrop: oklch(0% 0 0 / 20%);

      --muted: oklch(0.5 0.04 230);

      --default: oklch(0.25 0.05 230);
      --default-foreground: oklch(0.88 0.018 230);

      /* Accent */
      --accent: oklch(0.72 0.21 230);
      --accent-foreground: oklch(0.15 0.04 230);

      /* Form Field Defaults - Colors */
      --field-background: var(--default);
      --field-foreground: var(--foreground);
      --field-placeholder: var(--muted);
      --field-border: transparent;

      /* Status Colors */
      --success: oklch(0.68 0.16 165);
      --success-foreground: oklch(0.95 0.008 165);

      --warning: oklch(0.75 0.14 90);
      --warning-foreground: oklch(0.2 0.04 90);

      --danger: oklch(0.65 0.2 20);
      --danger-foreground: oklch(0.95 0.008 20);

      /* Component Colors */
      --segment: oklch(0.22 0.046 230);
      --segment-foreground: oklch(0.9 0.015 230);

      /* Misc Colors */
      --border: oklch(0 0 0 / 0%);
      --separator: oklch(0.28 0.045 230);
      --focus: var(--accent);
      --link: oklch(0.75 0.18 230);

      /* Shadows */
      --surface-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
      --overlay-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.3) inset;
      --field-shadow: 0 0 0 0 transparent inset; /* Transparent shadow to allow ring utilities to work */
    }
  }
}
```

**Important:** When adding custom themes, you must register them in your Metro config:

```js
// metro.config.js
const { withUniwindConfig } = require('uniwind/metro');
const {
  wrapWithReanimatedMetroConfig,
} = require('react-native-reanimated/metro-config');

const config = {
  // ... your existing config
};

module.exports = withUniwindConfig(wrapWithReanimatedMetroConfig(config), {
  cssEntryFile: './global.css',
  dtsFile: './src/uniwind.d.ts',
  extraThemes: ['ocean-light', 'ocean-dark'],
});
```

Apply themes in your app:

```tsx
import { Uniwind } from 'uniwind';
import { Button } from 'heroui-native';

function App() {
  return (
    <Button onPress={() => Uniwind.setTheme('ocean-light')}>
      <Button.Label>Ocean Theme</Button.Label>
    </Button>
  );
}
```

## Adding Custom Colors

Add your own semantic colors to the theme:

```css
@layer theme {
  @variant light {
    --info: oklch(0.6 0.15 210);
    --info-foreground: oklch(0.98 0 0);
  }

  @variant dark {
    --info: oklch(0.7 0.12 210);
    --info-foreground: oklch(0.15 0 0);
  }
}

/* Make the color available to Tailwind */
@theme inline {
  --color-info: var(--info);
  --color-info-foreground: var(--info-foreground);
}
```

Now use it in your components:

```tsx
import { View, Text } from 'react-native';

<View className="bg-info p-4 rounded-lg">
  <Text className="text-info-foreground">Info message</Text>
</View>;
```

## Custom Fonts

To use a custom font family in your app, you need to load the fonts and then override the font CSS variables.

### 1. Load Fonts in Your App

First, load your custom fonts (using Expo's `useFonts` hook for example):

```tsx
import { useFonts } from 'expo-font';
import { HeroUINativeProvider } from 'heroui-native';
import {
  YourFont_400Regular,
  YourFont_500Medium,
  YourFont_600SemiBold,
} from '@expo-google-fonts/your-font';

export default function App() {
  const [fontsLoaded] = useFonts({
    YourFont_400Regular,
    YourFont_500Medium,
    YourFont_600SemiBold,
  });

  if (!fontsLoaded) {
    return null; // Or return a loading screen
  }

  return <HeroUINativeProvider>{/* Your app content */}</HeroUINativeProvider>;
}
```

### 2. Configure Font CSS Variables

After loading the fonts, override the font CSS variables in your `global.css` file:

```css
@theme {
  --font-normal: 'YourFont-400Regular';
  --font-medium: 'YourFont-500Medium';
  --font-semibold: 'YourFont-600SemiBold';
}
```

**Note:** The font names in CSS variables should match the PostScript names of your loaded fonts. Check your font package documentation or use the font names exactly as they appear in your `useFonts` hook.

All HeroUI Native components automatically use these font variables, ensuring consistent typography throughout your app.

## Variables Reference

HeroUI defines three types of variables:

1. **Base Variables** — Non-changing values like `--white`, `--black`
2. **Theme Variables** — Colors that change between light/dark themes
3. **Calculated Variables** — Automatically generated hover (pressed) states and size variants

For a complete reference, see: [Colors Documentation](/docs/native/getting-started/colors), [Default Theme Variables](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/variables.css), [Shared Theme Utilities](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/theme.css)

**Calculated variables (Tailwind):**

We use Tailwind's `@theme` directive to automatically create calculated variables for hover (pressed) states and radius variants. These are defined in [theme.css](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/theme.css):
<CollapsibleCode lang="css" code={`  @theme inline static {
  --color-background: var(--background);
  --color-foreground: var(--foreground);

  --color-surface: var(--surface);
  --color-surface-foreground: var(--surface-foreground);
  --color-surface-hover: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);

  --color-surface-secondary: var(--surface-secondary);
  --color-surface-secondary-foreground: var(--surface-secondary-foreground);

  --color-surface-tertiary: var(--surface-tertiary);
  --color-surface-tertiary-foreground: var(--surface-tertiary-foreground);

  --color-overlay: var(--overlay);
  --color-overlay-foreground: var(--overlay-foreground);
  --color-backdrop: var(--backdrop);
  

  --color-muted: var(--muted);

  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);

  --color-segment: var(--segment);
  --color-segment-foreground: var(--segment-foreground);

  --color-border: var(--border);
  --color-separator: var(--separator);
  --color-focus: var(--focus);
  --color-link: var(--link);

  --color-default: var(--default);
  --color-default-foreground: var(--default-foreground);

  --color-success: var(--success);
  --color-success-foreground: var(--success-foreground);

  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);

  --color-danger: var(--danger);
  --color-danger-foreground: var(--danger-foreground);

  /* Form Field Tokens */
  --color-field: var(--field-background, var(--default));
  --color-field-foreground: var(--field-foreground, var(--foreground));
  --color-field-placeholder: var(--field-placeholder, var(--muted));
  --color-field-border: var(--field-border, var(--border));
  --radius-field: var(--field-radius, var(--radius-xl));
  --border-width-field-width: var(--field-border-width, var(--border-width));

  --shadow-surface: var(--surface-shadow);
  --shadow-overlay: var(--overlay-shadow);
  --shadow-field: var(--field-shadow);

  /* Calculated Variables */

  /* Colors */

  /* --- background shades --- */
  --color-background-secondary: color-mix(in oklab, var(--background) 96%, var(--foreground) 4%);
  --color-background-tertiary: color-mix(in oklab, var(--background) 92%, var(--foreground) 8%);
  --color-background-inverse: var(--foreground);

  /* ------------------------- */
  --color-default-hover: color-mix(in oklab, var(--default) 96%, var(--default-foreground) 4%);
  --color-accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);
  --color-success-hover: color-mix(in oklab, var(--success) 90%, var(--success-foreground) 10%);
  --color-warning-hover: color-mix(in oklab, var(--warning) 90%, var(--warning-foreground) 10%);
  --color-danger-hover: color-mix(in oklab, var(--danger) 90%, var(--danger-foreground) 10%);

  /* Form Field Colors */ 
  --color-field-hover: color-mix(in oklab, var(--field-background, var(--default)) 90%, var(--field-foreground, var(--foreground)) 2%);
  --color-field-focus: var(--field-background, var(--default));
  --color-field-border-hover: color-mix(in oklab, var(--field-border, var(--border)) 88%, var(--field-foreground, var(--foreground)) 10%);
  --color-field-border-focus: color-mix(in oklab, var(--field-border, var(--border)) 74%, var(--field-foreground, var(--foreground)) 22%);

  /* Soft Colors */
  --color-accent-soft: color-mix(in oklab, var(--accent) 15%, transparent);
  --color-accent-soft-foreground: var(--accent);
  --color-accent-soft-hover: color-mix(in oklab, var(--accent) 20%, transparent);

  --color-danger-soft: color-mix(in oklab, var(--danger) 15%, transparent);
  --color-danger-soft-foreground: var(--danger);
  --color-danger-soft-hover: color-mix(in oklab, var(--danger) 20%, transparent);

  --color-warning-soft: color-mix(in oklab, var(--warning) 15%, transparent);
  --color-warning-soft-foreground: var(--warning);
  --color-warning-soft-hover: color-mix(in oklab, var(--warning) 20%, transparent);

  --color-success-soft: color-mix(in oklab, var(--success) 15%, transparent);
  --color-success-soft-foreground: var(--success);
  --color-success-soft-hover: color-mix(in oklab, var(--success) 20%, transparent);

  /* Separator Colors - Levels */
  --color-separator-secondary: color-mix(in oklab, var(--surface) 85%, var(--surface-foreground) 15%);
  --color-separator-tertiary: color-mix(in oklab, var(--surface) 81%, var(--surface-foreground) 19%);

  /* Border Colors - Levels (progressive contrast: default → secondary → tertiary) */
  /* Light mode: lighter → darker | Dark mode: darker → lighter */
  --color-border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);
  --color-border-tertiary: color-mix(in oklab, var(--surface) 66%, var(--surface-foreground) 34%);

  /* Radius and default sizes - defaults can change by just changing the --radius */
  --radius-xs: calc(var(--radius) * 0.25); /* 0.125rem (2px) */
  --radius-sm: calc(var(--radius) * 0.5); /* 0.25rem (4px) */
  --radius-md: calc(var(--radius) * 0.75); /* 0.375rem (6px) */
  --radius-lg: calc(var(--radius) * 1); /* 0.5rem (8px) */
  --radius-xl: calc(var(--radius) * 1.5); /* 0.75rem (12px) */
  --radius-2xl: calc(var(--radius) * 2); /* 1rem (16px) */
  --radius-3xl: calc(var(--radius) * 3); /* 1.5rem (24px) */
  --radius-4xl: calc(var(--radius) * 4); /* 2rem (32px) */
}
`}
/>

> **Field border utilities:** Use `border-field-width` for the field border width and `border-field-border` for the field border color. Pair them together (`border-field-width border-field-border`) when you need both. The `border-field` class remains a border-color utility from `--color-field` only; do not use it for width. If you previously used `border-field` for width, migrate to `border-field-width`. Customize the width via the `--field-border-width` primitive.

Form controls now rely on the `--field-*` variables and their calculated hover/focus variants. Update them in your theme to restyle inputs, checkboxes, radios, and OTP slots without impacting surfaces like buttons or cards.

## Vibrant Palette

By default, HeroUI Native uses accessible soft foreground colors that mix the semantic color with the foreground for better contrast on soft backgrounds. If you prefer more saturated, vibrant soft foreground colors, import the optional `heroui-native/styles/vibrant` stylesheet alongside the base styles:

```css
/* global.css */
@import "heroui-native/styles";
@import "heroui-native/styles/vibrant"; /* [!code highlight] */
```

This switches all `*-soft-foreground` variables (accent, success, warning, danger) to use 92% of the semantic color with only 8% foreground mixed in — closer to the raw color but with a slight contrast boost. Components such as [Alert](/docs/native/components/alert), [Avatar](/docs/native/components/avatar), [Button](/docs/native/components/button), [Chip](/docs/native/components/chip), and [Toast](/docs/native/components/toast) automatically pick up the new soft foreground colors on their soft variants — no component prop changes are required.

| Mode | Accessible (default) | Vibrant |
|------|----------------------|---------|
| Soft foreground | `color-mix(color 70-80%, foreground 30-40%)` | `color-mix(color 92%, foreground 8%)` |

<Callout type="warning">
  The vibrant palette prioritizes visual saturation over contrast. It may not meet WCAG accessibility guidelines for some color combinations, especially with lighter accent colors.
</Callout>

<Callout type="info">
  The optional vibrant palette is available starting from [v1.0.4](/docs/native/releases/v1-0-4).
</Callout>

## Resources

- [Colors Documentation](/docs/native/getting-started/colors)
- [Styling Guide](/docs/native/getting-started/styling)
- [Tailwind CSS v4 Theming](https://tailwindcss.com/docs/theme)
- [OKLCH Color Tool](https://oklch.com)
