Theming
Color mode
Adjust your application's color scheme based on user preference.
Usage
Hope UI comes with built-in support for managing color mode in your application. All components are color-mode aware.
By default, the color mode is stored in localStorage
and appends a class to the body
element.
Color mode script
The ColorModeScript
ensures that the color mode storage sync works properly, and needs to be added before the content inside the body tag.
For a standard SolidJS application, you need to add the ColorModeScript
to the index.tsx
(or .jsx) file.
tsx
// index.tsximport { ColorModeScript, HopeProvider } from "@hope-ui/core";import { render } from "solid-js/web";import App from "./App";render(() => (<><ColorModeScript /><HopeProvider><App /></HopeProvider></>),document.getElementById("root") as HTMLElement);
tsx
// index.tsximport { ColorModeScript, HopeProvider } from "@hope-ui/core";import { render } from "solid-js/web";import App from "./App";render(() => (<><ColorModeScript /><HopeProvider><App /></HopeProvider></>),document.getElementById("root") as HTMLElement);
For a SolidStart application, you need to add the ColorModeScript
to the root.tsx
file.
tsx
// root.tsximport {ColorModeScript,cookieStorageManagerSSR,HopeProvider,injectCriticalStyle,} from "@hope-ui/core";import { Suspense, useContext } from "solid-js";import {Body,ErrorBoundary,FileRoutes,Head,Html,Meta,Routes,Scripts,ServerContext,Title,} from "solid-start";export default function Root() {injectCriticalStyle();return (<Html lang="en"><Head><Title>SolidStart - Bare</Title><Meta charset="utf-8" /><Meta name="viewport" content="width=device-width, initial-scale=1" /></Head><Body><ColorModeScript /><HopeProvider><Suspense><ErrorBoundary><Routes><FileRoutes /></Routes></ErrorBoundary></Suspense></HopeProvider><Scripts /></Body></Html>);}
tsx
// root.tsximport {ColorModeScript,cookieStorageManagerSSR,HopeProvider,injectCriticalStyle,} from "@hope-ui/core";import { Suspense, useContext } from "solid-js";import {Body,ErrorBoundary,FileRoutes,Head,Html,Meta,Routes,Scripts,ServerContext,Title,} from "solid-start";export default function Root() {injectCriticalStyle();return (<Html lang="en"><Head><Title>SolidStart - Bare</Title><Meta charset="utf-8" /><Meta name="viewport" content="width=device-width, initial-scale=1" /></Head><Body><ColorModeScript /><HopeProvider><Suspense><ErrorBoundary><Routes><FileRoutes /></Routes></ErrorBoundary></Suspense></HopeProvider><Scripts /></Body></Html>);}
You can also use a cookie storage manager instead, like below.
tsx
// root.tsximport {ColorModeScript,cookieStorageManagerSSR,HopeProvider,injectCriticalStyle,} from "@hope-ui/core";import { Suspense, useContext } from "solid-js";import {Body,ErrorBoundary,FileRoutes,Head,Html,Meta,Routes,Scripts,ServerContext,Title,} from "solid-start";export default function Root() {const event = useContext(ServerContext);// or import the default `cookieStorageManager` from "@hope-ui/core"const storageManager = cookieStorageManagerSSR(isServer ? event.request.headers.get("cookie") ?? "" : document.cookie);injectCriticalStyle();return (<Html lang="en"><Head><Title>SolidStart - Bare</Title><Meta charset="utf-8" /><Meta name="viewport" content="width=device-width, initial-scale=1" /></Head><Body><ColorModeScript storageType="cookie" /><HopeProvider storageManager={storageManager}><Suspense><ErrorBoundary><Routes><FileRoutes /></Routes></ErrorBoundary></Suspense></HopeProvider><Scripts /></Body></Html>);}
tsx
// root.tsximport {ColorModeScript,cookieStorageManagerSSR,HopeProvider,injectCriticalStyle,} from "@hope-ui/core";import { Suspense, useContext } from "solid-js";import {Body,ErrorBoundary,FileRoutes,Head,Html,Meta,Routes,Scripts,ServerContext,Title,} from "solid-start";export default function Root() {const event = useContext(ServerContext);// or import the default `cookieStorageManager` from "@hope-ui/core"const storageManager = cookieStorageManagerSSR(isServer ? event.request.headers.get("cookie") ?? "" : document.cookie);injectCriticalStyle();return (<Html lang="en"><Head><Title>SolidStart - Bare</Title><Meta charset="utf-8" /><Meta name="viewport" content="width=device-width, initial-scale=1" /></Head><Body><ColorModeScript storageType="cookie" /><HopeProvider storageManager={storageManager}><Suspense><ErrorBoundary><Routes><FileRoutes /></Routes></ErrorBoundary></Suspense></HopeProvider><Scripts /></Body></Html>);}
To use the ColorModeScript
on a site with strict Content-Security-Policy
, you can use the
nonce
prop that will be passed to the script
tag.
Initial color mode
The default color mode used by Hope UI is system
, meaning it will adapt to user preference.
To set the initial color mode your application should start with, pass the initialColorMode
prop to ColorModeScript
and HopeProvider
with either light
, dark
or system
.
tsx
// index.tsximport { ColorModeScript, HopeProvider } from "@hope-ui/core";import { render } from "solid-js/web";import App from "./App";render(() => (<><ColorModeScript initialColorMode="dark" /><HopeProvider initialColorMode="dark"><App /></HopeProvider></>),document.getElementById("root") as HTMLElement);
tsx
// index.tsximport { ColorModeScript, HopeProvider } from "@hope-ui/core";import { render } from "solid-js/web";import App from "./App";render(() => (<><ColorModeScript initialColorMode="dark" /><HopeProvider initialColorMode="dark"><App /></HopeProvider></>),document.getElementById("root") as HTMLElement);
Changing color mode
To manage color mode in your application, Hope UI exposes the useColorMode
primitive. It gives you an accessor to get the current color mode and a function to toggle it.
tsx
import { Button, useColorMode } from "@hope-ui/core";function UseColorModeExample() {const { colorMode, toggleColorMode } = useColorMode();return (<Button onClick={toggleColorMode}>Toggle {colorMode() === "light" ? "dark" : "light"} mode</Button>);}
tsx
import { Button, useColorMode } from "@hope-ui/core";function UseColorModeExample() {const { colorMode, toggleColorMode } = useColorMode();return (<Button onClick={toggleColorMode}>Toggle {colorMode() === "light" ? "dark" : "light"} mode</Button>);}
Changing a value based on color mode
The useColorModeValue
primitive can be used to change any value or style based on the color mode.
It takes 2 arguments: the value in light and dark mode, and returns a derived signal with the correct value based on the color mode.
The below example demonstrates how to change a Box's background and color using useColorModeValue
.
tsx
import { Box, Button, useColorMode, useColorModeValue } from "@hope-ui/core";export function UseColorModeValueExample() {const { toggleColorMode } = useColorMode();const bg = useColorModeValue("tomato", "royalblue");const color = useColorModeValue("black", "white");return (<><Box mb={4} p={2} bg={bg()} color={color()}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
tsx
import { Box, Button, useColorMode, useColorModeValue } from "@hope-ui/core";export function UseColorModeValueExample() {const { toggleColorMode } = useColorMode();const bg = useColorModeValue("tomato", "royalblue");const color = useColorModeValue("black", "white");return (<><Box mb={4} p={2} bg={bg()} color={color()}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
Styling
Using the light/dark
object syntax
Style props accept an object as value in which you can set the light
and/or dark
value. The above example could also be achieved in this way:
tsx
import { Box, Button, useColorMode } from "@hope-ui/core";export function UseDarkStylePropExample() {const { toggleColorMode } = useColorMode();return (<><Boxmb={4}p={2}bg={{ light: "tomato", dark: "royalblue" }}color={{ light: "black", dark: "white" }}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
tsx
import { Box, Button, useColorMode } from "@hope-ui/core";export function UseDarkStylePropExample() {const { toggleColorMode } = useColorMode();return (<><Boxmb={4}p={2}bg={{ light: "tomato", dark: "royalblue" }}color={{ light: "black", dark: "white" }}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
The _dark
style prop
If you prefer to set all dark mode styles in the same place, Hope UI provides a _dark
style prop. Below is the same example achieved this way:
tsx
import { Box, Button, useColorMode } from "@hope-ui/core";export function UseDarkStylePropExample() {const { toggleColorMode } = useColorMode();return (<><Boxmb={4}p={2}bg="tomato"color="black"_dark={{bg: "royalblue",color: "white",}}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
tsx
import { Box, Button, useColorMode } from "@hope-ui/core";export function UseDarkStylePropExample() {const { toggleColorMode } = useColorMode();return (<><Boxmb={4}p={2}bg="tomato"color="black"_dark={{bg: "royalblue",color: "white",}}>This box's style will change based on the color mode.</Box><Button onClick={toggleColorMode}>Toggle color mode</Button></>);}
Caveat
Both the light/dark
object syntax and _dark
style prop doesn't work inside css pseudo-element selector like ::before
.
For example if you want to style the placeholder of an input
in dark mode the following code will not work:
tsx
<inputsx={{"&::placeholder": {color: {light: "neutral.500",dark: "neutral.600", // ❌ Doesn't work},},}}/>// Or<inputsx={{"&::placeholder": {color: "neutral.500",_dark: {color: "neutral.600", // ❌ Doesn't work too},},}}/>
tsx
<inputsx={{"&::placeholder": {color: {light: "neutral.500",dark: "neutral.600", // ❌ Doesn't work},},}}/>// Or<inputsx={{"&::placeholder": {color: "neutral.500",_dark: {color: "neutral.600", // ❌ Doesn't work too},},}}/>
Instead, you have to put the css pseudo-element selector inside the _dark
pseudo selector.
tsx
<inputsx={{"&::placeholder": {color: "neutral.500",},_dark: {"&::placeholder": {color: "neutral.600", // ✅ Work},},}}/>
tsx
<inputsx={{"&::placeholder": {color: "neutral.500",},_dark: {"&::placeholder": {color: "neutral.600", // ✅ Work},},}}/>