The @theme-ui/color-modes package provides utilities for managing color modes in Theme UI applications. It handles color mode state, localStorage persistence, and system preference detection.
Installation
npm install @theme-ui/color-modes @emotion/react
This package is included in the main theme-ui package and generally should not be used on its own. It’s automatically imported when you use theme-ui.
When to Use
This package is used internally by Theme UI. You would only install it directly if you’re:
- Building a custom Theme UI setup
- Using
@theme-ui/core without the full theme-ui package
- Need fine-grained control over color mode behavior
For most use cases, use the theme-ui package which includes this package.
Exports
Components
ColorModeProvider
A React component that provides color mode functionality to your application.
import { ThemeProvider } from '@theme-ui/core'
import { ColorModeProvider } from '@theme-ui/color-modes'
function App({ children }) {
return (
<ThemeProvider theme={theme}>
<ColorModeProvider>
{children}
</ColorModeProvider>
</ThemeProvider>
)
}
Features:
- Manages color mode state
- Persists selection to localStorage
- Syncs with system color scheme preferences
- Applies color mode colors to theme
- Generates CSS custom properties for color modes
Props:
children: React.ReactNode - Child components
InitializeColorMode
A script component that prevents flash of unstyled content (FOUC) when loading the page.
import { InitializeColorMode } from '@theme-ui/color-modes'
function Document() {
return (
<html>
<head>
<InitializeColorMode />
</head>
<body>
<App />
</body>
</html>
)
}
This component injects a small script that:
- Reads the color mode from localStorage
- Applies it to the document before React hydrates
- Prevents flashing between color modes on page load
Hooks
useColorMode
A React hook to access and control the current color mode.
import { useColorMode } from '@theme-ui/color-modes'
function ThemeToggle() {
const [colorMode, setColorMode] = useColorMode()
return (
<button
onClick={() => {
setColorMode(colorMode === 'light' ? 'dark' : 'light')
}}
>
Toggle {colorMode === 'light' ? 'Dark' : 'Light'} Mode
</button>
)
}
Returns:
[
colorMode: string,
setColorMode: Dispatch<SetStateAction<string>>
]
TypeScript:
// You can specify a narrower type for color mode names
const [colorMode, setColorMode] = useColorMode<'light' | 'dark'>()
Error:
Throws an error if used outside of a ColorModeProvider.
Context Extensions
The package extends the Theme UI context with color mode properties:
interface ThemeUIContextValue {
colorMode?: string
setColorMode?: (colorMode: SetStateAction<string | undefined>) => void
}
Access these through useThemeUI() from @theme-ui/core:
import { useThemeUI } from '@theme-ui/core'
function MyComponent() {
const { colorMode, setColorMode } = useThemeUI()
// ...
}
Configuration
Configure color modes in your theme:
const theme = {
// Define your base colors (light mode by default)
colors: {
text: '#000',
background: '#fff',
primary: '#07c',
// Define alternate color modes
modes: {
dark: {
text: '#fff',
background: '#000',
primary: '#0cf',
},
// Add more modes as needed
sepia: {
text: '#433422',
background: '#f5deb3',
primary: '#b8860b',
},
},
},
// Optional: Configure color mode behavior
config: {
// The name of the initial/default color mode
initialColorModeName: 'light',
// Use CSS custom properties for colors
useCustomProperties: true,
// Store color mode preference in localStorage
useLocalStorage: true,
// Follow system color scheme preference
// 'system' - always follow system preference
// true - use system preference as initial value
// false - don't use system preference
useColorSchemeMediaQuery: true,
},
}
Features
localStorage Persistence
By default, color mode selection is persisted to localStorage:
// Automatically stored when color mode changes
setColorMode('dark')
// Disable with:
const theme = {
config: {
useLocalStorage: false,
},
}
System Preference Detection
Automatically detect and follow system color scheme:
const theme = {
config: {
// Use system preference as initial value
useColorSchemeMediaQuery: true,
// Or always follow system preference (dynamic)
useColorSchemeMediaQuery: 'system',
},
}
CSS Custom Properties
Colors are converted to CSS custom properties for efficient switching:
:root {
--theme-ui-colors-text: #000;
--theme-ui-colors-background: #fff;
--theme-ui-colors-primary: #07c;
}
[data-theme="dark"] {
--theme-ui-colors-text: #fff;
--theme-ui-colors-background: #000;
--theme-ui-colors-primary: #0cf;
}
Disable with:
const theme = {
config: {
useCustomProperties: false,
},
}
Multiple Color Modes
Support more than just light and dark:
const theme = {
colors: {
text: '#000',
background: '#fff',
modes: {
dark: { /* ... */ },
sepia: { /* ... */ },
highContrast: { /* ... */ },
},
},
}
// Switch between them:
setColorMode('sepia')
setColorMode('highContrast')
Nested Color Modes
Nested ColorModeProvider components are supported for section-specific color modes.
Implementation Details
Storage Key
Color mode is stored in localStorage under the key:
Media Queries
The package uses these media queries for system preference:
'(prefers-color-scheme: dark)'
'(prefers-color-scheme: light)'
FOUC Prevention
The InitializeColorMode component injects this script:
(function() {
try {
var mode = localStorage.getItem('theme-ui-color-mode');
if (!mode) return
document.documentElement.classList.add('theme-ui-' + mode);
} catch (e) {}
})();
This runs before React hydration to apply the correct color mode immediately.
TypeScript Types
import type {
ColorMode,
ColorModesScale,
} from '@theme-ui/color-modes'
// Or from @theme-ui/css:
import type { ColorMode, ColorModesScale } from '@theme-ui/css'
Notes
- Requires
@theme-ui/core and @theme-ui/css as dependencies
- Requires
@emotion/react and React 18+ as peer dependencies
- No side effects - safe for tree-shaking
- Supports server-side rendering with FOUC prevention
- Works with nested theme providers
Examples
Basic Toggle
import { useColorMode } from '@theme-ui/color-modes'
function ColorModeToggle() {
const [colorMode, setColorMode] = useColorMode()
return (
<button onClick={() => setColorMode(colorMode === 'default' ? 'dark' : 'default')}>
{colorMode === 'default' ? 'Dark' : 'Light'}
</button>
)
}
Multiple Mode Selector
import { useColorMode } from '@theme-ui/color-modes'
function ColorModeSelector() {
const [colorMode, setColorMode] = useColorMode()
return (
<select value={colorMode} onChange={e => setColorMode(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="sepia">Sepia</option>
</select>
)
}
With Icon
import { useColorMode, IconButton } from 'theme-ui'
function ColorModeButton() {
const [colorMode, setColorMode] = useColorMode()
return (
<IconButton
onClick={() => setColorMode(colorMode === 'light' ? 'dark' : 'light')}
aria-label="Toggle color mode"
>
{colorMode === 'light' ? '🌙' : '☀️'}
</IconButton>
)
}
Related