Dark Mode
Add support for dark mode but also any number of other color modes.
Getting started
Defining colors
To enable color modes in your application, the first thing to do is to make your theme color mode compatible.
In the theme.colors object, add a nested modes object that will contain keys for optional color modes. The name for the base color mode is default.
const theme = { colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', }, }, }, }
The colors defined at the root of the colors object will be accessible as default. All colors found in colors.modes will be referenced by their key. The above example will have two modes:
defaultdark
Add ColorModeProvider
After your theme and color modes are ready, you have to add a ColorModeProvider, just after your ThemeProvider:
import React from 'react' import { ThemeProvider, ColorModeProvider } from '@xstyled/styled-components' import App from './App' const theme = { colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', }, }, }, } function Root() { return ( <ThemeProvider theme={theme}> <ColorModeProvider> <App /> </ColorModeProvider> </ThemeProvider> ) }
Now your app is ready! It will automatically uses dark mode according to user system preferences.
Live Example
Basics
Change color mode
Use the useColorMode hook in your application to change the color mode. This value will be stored in localStorage and used whenever the page is loaded.
import React from 'react' import { useColorMode } from '@xstyled/styled-components' function Example(props) { const [colorMode, setColorMode] = useColorMode() return ( <header> <button onClick={(e) => { setColorMode(colorMode === 'default' ? 'dark' : 'default') }} > Toggle {colorMode === 'default' ? 'Dark' : 'Light'} </button> </header> ) }
Gatsby
To use it with Gatsby, add the following code to gatsby-ssr.js:
import React from 'react' import { getColorModeInitScriptElement } from '@xstyled/styled-components' export function onRenderBody({ setPreBodyComponents }) { setPreBodyComponents([getColorModeInitScriptElement()]) }
Next
To use it with Next, create a _document.js:
import React from 'react' import Document, { Html, Head, Main, NextScript } from 'next/document' import { getColorModeInitScriptElement } from '@xstyled/styled-components' export default class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx) return initialProps } render() { return ( <Html> <Head /> <body> {getColorModeInitScriptElement()} <Main /> <NextScript /> </body> </Html> ) } }
Server Side Rendering
For SSR, you have to include in the <head> or at the top of the body the initialization script.
You can use getColorModeInitScriptElement to get it as a React element or getColorModeInitScriptTag to get it as a plain string.
Advanced
There are a few advanced usages of color modes when you need more flexibility. For most use cases, you won't need to reference this section.
Use another default color mode
The colors you define at the root level, outside of theme.colors.modes is named default. This is the color mode used by default. Use defaultColorModeName to start with another color mode:
{ defaultColorModeName: 'dark', colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', } } } }
At the first visit, the user will use the dark color mode.
Renaming the default color mode
The colors you define at the root level, outside of theme.colors.modes is named default. If you'd like to customize it you can do so by setting initialColorModeName:
{ initialColorModeName: 'light', colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', } } } }
At the first visit, the user will use the light color mode that is implicitly the default one.
Disable prefers-color-scheme
By default, dark and light mode are automatically activated if @media (prefers-color-scheme: dark) matches. It means it automatically follows user system preference.
To turn off this behaviour, add useColorSchemeMediaQuery: false to your theme:
{ useColorSchemeMediaQuery: false, colors: { text: '#000', background: '#fff', modes: { dark: { text: '#fff', background: '#000', } } } }
Turn off custom properties
The default implementation of xstyled color modes uses CSS custom properties. If you're supporting browsers that don't support custom properties you can turn off this setting.
Not using CSS custom properties cause the colors to flash on initial page load if you use SSR or static generation (Next or Gatsby).
// example theme colors { useCustomProperties: false, colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', } } } }
Use another target
To make color mode works, a class is added to document.body. Sometimes you may want to specify another target, more specific. To do so, add target and targetSelector to ColorModeProvider:
import React from 'react' import { ThemeProvider, ColorModeProvider } from '@xstyled/styled-components' import App from './App' const target = document.getElementById('small-react-app') const theme = { colors: { text: '#000', background: '#fff', primary: '#07c', modes: { dark: { text: '#fff', background: '#000', primary: '#0cf', }, }, }, } function Root() { return ( <ThemeProvider theme={theme}> <ColorModeProvider target={target} targetSelector="#small-react-app"> <App /> </ColorModeProvider> </ThemeProvider> ) } render(<Root />, target)
Edit this page on GitHub
getColorModeInitScriptElement&getColorModeInitScriptTagalso accept atargetoption to configure the script. Example:getColorModeInitScriptTag({ target: 'document.getElementById("small-react-app")' })