Overview
Amplify UI supports color modes/schemes, like Dark Mode, through theme overrides. Amplify UI Theme Overrides let you define different theme styles in different contexts, such as color mode. You can also use plain CSS.
There are 2 ways to support light/dark mode in your application:
- Rely on the user's OS preference (System Preferences > General > Appearance in Mac)
- Place a control like a toggle button in your application that allows the user to switch color modes. With this option you could default to the OS preference or show 3 options: light, dark, system, like this site does.
Either of these approaches work with Amplify UI whether you are using the Theme Overrides or writing plain CSS.
ThemeProvider
colorMode
The ThemeProvider
accepts a colorMode
prop which can be light
, dark
, or system
.
If you have multiple ThemeProvider
s in your application, make sure to store colorMode
in the application's state or context and pass it to each ThemeProvider
or else some parts of your app won't have the right color mode applied. Also, because the theme uses CSS variables which are inherited, your application can have some weird behavior with nested themes and color modes.
Multiple ThemeProvider
s should be avoided if possible because it is more efficient to use a selector override instead. This site uses nested ThemeProvider
s for demos.
Default Dark Mode
Amplify UI comes with a default dark mode that you can use.
Note: the Amplify UI theme and any overrides like dark mode are scoped to the ThemeProvider. Changing the color mode in the example will only affect the example code.
Current color mode: system
import * as React from 'react';
import {
defaultDarkModeOverride,
ThemeProvider,
Card,
Text,
ToggleButton,
ToggleButtonGroup,
} from '@aws-amplify/ui-react';
export const DefaultDarkMode = () => {
const [colorMode, setColorMode] = React.useState('system');
const theme = {
name: 'my-theme',
overrides: [defaultDarkModeOverride],
};
return (
<ThemeProvider theme={theme} colorMode={colorMode}>
<Card>
<ToggleButtonGroup
value={colorMode}
isExclusive
onChange={(value) => setColorMode(value)}
>
<ToggleButton value="light">Light</ToggleButton>
<ToggleButton value="dark">Dark</ToggleButton>
<ToggleButton value="system">System</ToggleButton>
</ToggleButtonGroup>
<Text>Current color mode: {colorMode}</Text>
</Card>
</ThemeProvider>
);
};
Current color mode: system
import * as React from 'react';
import {
defaultDarkModeOverride,
ThemeProvider,
ColorMode,
Card,
Text,
ToggleButton,
ToggleButtonGroup,
} from '@aws-amplify/ui-react';
export const DefaultDarkMode = () => {
const [colorMode, setColorMode] = React.useState<ColorMode>('system');
const theme = {
name: 'my-theme',
overrides: [defaultDarkModeOverride],
};
return (
<ThemeProvider theme={theme} colorMode={colorMode}>
<Card>
<ToggleButtonGroup
value={colorMode}
isExclusive
onChange={(value: ColorMode) => setColorMode(value)}
>
<ToggleButton value="light">Light</ToggleButton>
<ToggleButton value="dark">Dark</ToggleButton>
<ToggleButton value="system">System</ToggleButton>
</ToggleButtonGroup>
<Text>Current color mode: {colorMode}</Text>
</Card>
</ThemeProvider>
);
};
System Dark Mode
If you don't want to provide a color mode control on your application, but still want to honor the user's operating system preference for color mode, you can set the colorMode
on the ThemeProvider to system
. Then use either the default dark mode override styling or provide your own.
Note: to see dark mode applied, change your OS preferences
Primary text
Secondary text
Tertiary text
import * as React from 'react';
import {
defaultDarkModeOverride,
ThemeProvider,
Card,
Text,
Button,
} from '@aws-amplify/ui-react';
export const SystemDarkModeExample = () => {
const theme = {
name: 'my-theme',
overrides: [defaultDarkModeOverride],
};
return (
// Note: color mode overrides are scoped to the ThemeProvider
// if you use multiple providers
<ThemeProvider theme={theme} colorMode="system">
<Card>
<Button>Hello</Button>
<Text variation="primary">Primary text</Text>
<Text variation="secondary">Secondary text</Text>
<Text variation="tertiary">Tertiary text</Text>
</Card>
</ThemeProvider>
);
};
Custom dark mode
Current color mode: system
import * as React from 'react';
import {
ThemeProvider,
Card,
Text,
ToggleButton,
ToggleButtonGroup,
} from '@aws-amplify/ui-react';
export const CustomDarkModeExample = () => {
const [colorMode, setColorMode] = React.useState('system');
const theme = {
name: 'my-theme',
overrides: [
{
colorMode: 'dark',
tokens: {
colors: {
font: {
primary: { value: '{colors.pink.100}' },
secondary: { value: '{colors.pink.90}' },
tertiary: { value: '{colors.pink.80}' },
},
background: {
primary: { value: '{colors.purple.10}' },
secondary: { value: '{colors.purple.20}' },
tertiary: { value: '{colors.purple.40}' },
},
border: {
primary: { value: '{colors.pink.60}' },
secondary: { value: '{colors.pink.40}' },
tertiary: { value: '{colors.pink.20}' },
},
},
},
},
],
};
return (
// Note: color mode overrides are scoped to the ThemeProvider
// if you use multiple providers
<ThemeProvider theme={theme} colorMode={colorMode}>
<Card>
<ToggleButtonGroup
value={colorMode}
isExclusive
onChange={(value) => setColorMode(value)}
>
<ToggleButton value="light">Light</ToggleButton>
<ToggleButton value="dark">Dark</ToggleButton>
<ToggleButton value="system">System</ToggleButton>
</ToggleButtonGroup>
<Text>Current color mode: {colorMode}</Text>
</Card>
</ThemeProvider>
);
};
Current color mode: system
import * as React from 'react';
import {
ThemeProvider,
ColorMode,
Card,
Text,
ToggleButton,
ToggleButtonGroup,
Theme,
} from '@aws-amplify/ui-react';
export const CustomDarkModeExample = () => {
const [colorMode, setColorMode] = React.useState<ColorMode>('system');
const theme: Theme = {
name: 'my-theme',
overrides: [
{
colorMode: 'dark',
tokens: {
colors: {
font: {
primary: { value: '{colors.pink.100}' },
secondary: { value: '{colors.pink.90}' },
tertiary: { value: '{colors.pink.80}' },
},
background: {
primary: { value: '{colors.purple.10}' },
secondary: { value: '{colors.purple.20}' },
tertiary: { value: '{colors.purple.40}' },
},
border: {
primary: { value: '{colors.pink.60}' },
secondary: { value: '{colors.pink.40}' },
tertiary: { value: '{colors.pink.20}' },
},
},
},
},
],
};
return (
// Note: color mode overrides are scoped to the ThemeProvider
// if you use multiple providers
<ThemeProvider theme={theme} colorMode={colorMode}>
<Card>
<ToggleButtonGroup
value={colorMode}
isExclusive
onChange={(value: ColorMode) => setColorMode(value)}
>
<ToggleButton value="light">Light</ToggleButton>
<ToggleButton value="dark">Dark</ToggleButton>
<ToggleButton value="system">System</ToggleButton>
</ToggleButtonGroup>
<Text>Current color mode: {colorMode}</Text>
</Card>
</ThemeProvider>
);
};
CSS
You can also write CSS variables directly to support color modes. If you are using the colorMode
prop in the ThemeProvider
, you can write CSS like this to support dark mode:
/* The prefers-color-scheme media query detects the system setting */
@media (prefers-color-scheme: dark) {
[data-amplify-color-mode='system'] {
--amplify-colors-background-primary: black;
}
}
[data-amplify-color-mode='dark'] {
--amplify-colors-background-primary: black;
}