ColourSchemeToggle

The light and dark switch.

Overview

A site that offers both schemes: one toggle in the header or controls.

Switch

Do
import { ColourSchemeToggle } from "@hackcove/ui";

<ColourSchemeToggle />
One ColourSchemeToggle in the page header: it switches <html>, remembers the choice and shows the scheme you would switch to.
Don't
import { applyColourScheme, Button, ButtonGroup } from "@hackcove/ui";

<ButtonGroup label="Colour scheme">
  <Button variant="outline" size="sm" onClick={() => applyColourScheme("light")}>
    Light
  </Button>
  <Button variant="outline" size="sm" onClick={() => applyColourScheme("dark")}>
    Dark
  </Button>
</ButtonGroup>
Two hand-rolled buttons forget the choice, and nothing shows which scheme is on.

Schemes

Do
import { ColourSchemeToggle } from "@hackcove/ui";

<ColourSchemeToggle schemes={{ light: "paper", dark: "dark" }} />
A light theme with a class of its own (thrifty-paper's) is named in schemes, so the toggle swaps both classes.
Don't
import { ColourSchemeToggle } from "@hackcove/ui";

<ColourSchemeToggle />
Without schemes the toggle only adds and removes dark, so the light theme's own class is never put back.

Usage

  • Use one ColourSchemeToggle per site. It switches <html> between light and dark and remembers the choice in localStorage.
  • Put colourSchemeScript inline in <head>, so the stored or system scheme is on <html> before first paint and a light visitor never sees a dark flash. It knows only the dark class.
  • A light theme with a class of its own (thrifty-paper's paper) passes schemes={{ light: "paper", dark: "dark" }}; render the same class on <html> on the server with schemeClass.
  • An app that knows the scheme on the server (from a cookie) passes initial, sets store={false} and persists from onChange. It then leaves the head script out.
  • Do not build a switch from your own buttons; the functions below are what the toggle itself calls.

Accessibility

  • It is an IconButton named Switch to dark theme or Switch to light theme, so the name says the action, and the icon is aria-hidden.
  • The icon shown is the scheme you would switch to: the moon on a light page, the sun on a dark one. It follows the class on <html> through CSS, so it is right on the first frame.
  • applyColourScheme also sets color-scheme on <html>, so native controls and scrollbars follow.

API

ColourSchemeToggle

A sun/moon button that switches <html> between light and dark and remembers the choice. It shows the scheme you would switch to. Pair it with colourSchemeScript in <head>, unless you pass schemes or initial: the script knows only the dark class and the stored choice, so an app that sets the scheme itself leaves it out. An app whose light scheme has a class of its own passes schemes ({ light: "paper", dark: "dark" }); the toggle swaps those classes, and schemeClass gives the same one for the server to render on <html>. An app that knows the scheme on the server (from a cookie) passes it as initial, so the server markup, the hydrating client and the <html> class agree; persist a change from onChange. The icon follows the class on <html> through CSS, so even without initial a dark page shows the sun from the first frame; the button's name catches up once the client has hydrated. store={false} skips localStorage, for an app that keeps the choice in a server cookie and writes it from onChange. Every other button prop (id, data-*, aria-*) goes on the button.


initial

The scheme the server rendered. What the server and the hydrating client show; the real state follows right after. Default "light".

Type
"light" | "dark"

onChange
Type
(scheme: ColourScheme) => void

schemes

The class each scheme puts on <html>. Default { light: "", dark: "dark" }.

Type
ColourSchemeClasses
Default
{ light: "", dark: "dark" }

store

Remember the choice in localStorage. false leaves persisting to onChange. Default true.

Type
boolean
Default
true

Functions

  • applyColourScheme Puts a scheme on <html>: its class, the other scheme's class taken off, and color-scheme.
  • schemeClass The class a scheme puts on <html>, for the server to render so the first paint is right; empty for the default light scheme.
  • colourSchemeScript A string of JavaScript to inline in <head> before first paint: applies the stored scheme, else the system one.
  • readColourScheme The stored choice, else the system preference, with a fallback (default light) when neither is available, as on the server.
  • storeColourScheme Remembers a scheme in localStorage for the next visit; a storage error is swallowed.