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.
tsximport { Button, useColorMode } from "@hope-ui/core";function UseColorModeExample() {const { colorMode, toggleColorMode } = useColorMode();return (<Button onClick={toggleColorMode}>Toggle {colorMode() === "light" ? "dark" : "light"} mode</Button>);}
tsximport { 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.
tsximport { 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></>);}
tsximport { 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:
tsximport { 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></>);}
tsximport { 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:
tsximport { 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></>);}
tsximport { 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},},}}/>