AniUI

Dark Mode

Set up dark mode in your React Native app with NativeWind or Uniwind.

How It Works#

AniUI uses CSS custom properties with a .dark class. NativeWind automatically applies the dark class based on the device's color scheme. All AniUI components respond to dark mode without any extra configuration.

Expo Setup#

1. Configure app.json#

Set userInterfaceStyle to "automatic" to follow the system setting:

app.json
{
  "expo": {
    "userInterfaceStyle": "automatic"
  }
}

2. Import global CSS#

Make sure your root layout imports the global stylesheet:

app/_layout.tsx
import "../global.css";

export default function RootLayout() {
  // ...
}

That's it. NativeWind detects the system color scheme and applies the .dark class automatically. All CSS variables switch between their light and dark values.

Manual Toggle#

To let users switch themes manually, use NativeWind's useColorScheme hook:

components/theme-toggle.tsx
import { Pressable, Text } from "react-native";
import { useColorScheme } from "nativewind";

export function ThemeToggle() {
  const { colorScheme, toggleColorScheme } = useColorScheme();

  return (
    <Pressable
      onPress={toggleColorScheme}
      className="rounded-md bg-secondary px-4 py-2"
    >
      <Text className="text-secondary-foreground">
        {colorScheme === "dark" ? "Light Mode" : "Dark Mode"}
      </Text>
    </Pressable>
  );
}

Uniwind#

Uniwind uses Uniwind.setTheme() for programmatic theme switching. Dark mode values are defined in global.css using a @media (prefers-color-scheme: dark) block.

1. global.css setup#

Make sure your CSS has @import "uniwind" and uses @layer theme with @variant light/dark:

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

@theme {
  --radius: 0.5rem;
}

@layer theme {
  :root {
    @variant light {
      --color-background: hsl(0 0% 100%);
      --color-foreground: hsl(240 10% 3.9%);
      --color-primary: hsl(240 5.9% 10%);
      /* ... light values */
    }

    @variant dark {
      --color-background: hsl(240 10% 3.9%);
      --color-foreground: hsl(0 0% 98%);
      --color-primary: hsl(0 0% 98%);
      /* ... dark values */
    }
  }
}

2. Toggle theme#

components/theme-toggle.tsx
import { Pressable, Text } from "react-native";
import { Uniwind, useUniwind } from "uniwind";

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

  return (
    <Pressable
      onPress={() => {
        const next = Uniwind.currentTheme === "light" ? "dark" : "light";
        Uniwind.setTheme(next);
      }}
      className="rounded-md bg-secondary px-4 py-2"
    >
      <Text className="text-secondary-foreground">
        {theme === "dark" ? "Light Mode" : "Dark Mode"}
      </Text>
    </Pressable>
  );
}

Uniwind.setTheme() automatically calls Appearance.setColorScheme() internally, so native components (Alert, Modal, etc.) also adapt. Use Uniwind.setTheme("system") to re-enable automatic system theme following.

Bare React Native#

For non-Expo projects, NativeWind uses React Native's built-in Appearance API. No extra setup is needed — just ensure your NativeWind config is correct and global.css is loaded.

Using Dark Mode in Components#

AniUI components use semantic color tokens that automatically adapt. If you need conditional styling, use NativeWind's dark: prefix:

<View className="bg-white dark:bg-gray-900">
  <Text className="text-black dark:text-white">
    Adapts to color scheme
  </Text>
</View>

{/* But prefer semantic tokens — they adapt automatically: */}
<View className="bg-background">
  <Text className="text-foreground">
    Always correct
  </Text>
</View>