Light and dark
Both modes come from seven custom properties. Write a component once and it works in both.
How it works
components/_mode.sass sets custom properties on body and overrides them on body.dark. Components read the properties, so there is no second set of rules for the dark mode.
.card
color: var(--text)
border: 1px solid var(--line)
background-color: var(--page)Properties
| Property | Light | Dark |
|---|---|---|
--page | $white | $night |
--surface | $light | $night-surface |
--text | $dark | $night-text |
--muted | $medium | $night-muted |
--line | $border | $night-line |
--line-soft | $border-soft | $night-line |
--accent | $accent | $night-accent |
Helpers that follow the mode
bg-page, bg-surface, c-text, c-muted, bc-line and bc-line-soft read the properties. Palette helpers such as bg-white or c-dark keep their exact colour in both modes. c-accent is the one exception: in dark mode it switches to $night-accent, because the light accent is too dark to read there.
The switch
components/Mode.jsx is the button in the header. It keeps the choice in localStorage under avior-mode and falls back to the system setting. An inline script in public/index.html applies the class before the page is painted, so a dark reader never sees a white flash.