feat(theme): dark mode by default, with a light/dark toggle #13

Merged
allan merged 1 commits from docs/dark-mode-toggle into main 2026-09-28 23:44:54 +00:00
Collaborator

Adds Material's dark scheme to the docs site and makes it the default, with a toggle in the header to switch back to light.

What changed

mkdocs.yml now declares two palette entries, slate first:

  • dark (slate) is first, and Material takes the first entry as the default, so the site opens dark.
  • a sun icon in the header switches to light; a moon switches back. Material remembers the choice in localStorage, so the toggle sticks across pages and visits.

docs/dc-brand.css gains two dark-scheme rules:

  • Hue. Material derives its dark greys from a --md-hue variable. Pointing it at the brand slate (206deg) instead of Material's neutral 225deg gives the whole scheme the same cast as the header. The light scheme does not use the variable (its greys are unsaturated), so it is untouched.
  • Links. The brand slate is unreadable on a dark background, so the dark scheme uses a light tint of it (#9fb5c6) for body links. Green stays the accent, which keeps link hover visible in both schemes - if links were green as well, hover would do nothing.

Dark is the default regardless of the operating system preference, because neither entry carries a media query. Material only follows prefers-color-scheme when one does.

Contrast, over the dark background

Measured from the rendered page (rgb(30, 36, 41)), with alpha composited:

Element Contrast
Body text 8.8:1
Body links 7.4:1
Inline code 7.2:1
Accent green (hover, focus) 5.6:1
Header text on the brand slate 10.2:1

All above the 4.5:1 that body text needs.

The light scheme is unchanged

A pixel diff of a local mkdocs build of this branch against one of main, both rendering light:

  • configuration page: 254 differing pixels, all inside the header toggle icon (x 1036-1057, y 14-37)
  • header strip at 2x: 824 differing pixels, all inside the toggle icon (x 2072-2114, y 28-74)
  • home page: the same 254 header pixels, plus 900 pixels inside the video placeholder where the loading spinner animates between captures

So the only thing the light scheme gains is the toggle.

Screenshots

From a local mkdocs build, 1920x1200 (and 480px for mobile).

Header, 2x - the toggle

Before (no toggle):

header before

Dark - sun icon:

header dark

Light - moon icon:

header light

Home - dark (the new default)

home dark

Home - light

home light

Configuration page - dark

tables dark

Configuration page - light

tables light

Mobile, 480px - dark

mobile dark

Mobile, 480px - light

mobile light

Notes for the reviewer

  • The toggle is present at 480px too, at 40x40.
  • The header stays the brand slate in both schemes, so the site keeps its identity in dark mode.
  • Admonition colours are still Material's semantic set, unchanged in both schemes.
  • No CI runs on this repo's PRs - publish.yml publishes on push to main - so nothing gates this but review.
Adds Material's dark scheme to the docs site and makes it the default, with a toggle in the header to switch back to light. ## What changed `mkdocs.yml` now declares two palette entries, slate first: - **dark (slate) is first, and Material takes the first entry as the default**, so the site opens dark. - a sun icon in the header switches to light; a moon switches back. Material remembers the choice in `localStorage`, so the toggle sticks across pages and visits. `docs/dc-brand.css` gains two dark-scheme rules: - **Hue.** Material derives its dark greys from a `--md-hue` variable. Pointing it at the brand slate (`206deg`) instead of Material's neutral `225deg` gives the whole scheme the same cast as the header. The light scheme does not use the variable (its greys are unsaturated), so it is untouched. - **Links.** The brand slate is unreadable on a dark background, so the dark scheme uses a light tint of it (`#9fb5c6`) for body links. Green stays the accent, which keeps link hover visible in both schemes - if links were green as well, hover would do nothing. Dark is the default regardless of the operating system preference, because neither entry carries a `media` query. Material only follows `prefers-color-scheme` when one does. ## Contrast, over the dark background Measured from the rendered page (`rgb(30, 36, 41)`), with alpha composited: | Element | Contrast | |---|---| | Body text | 8.8:1 | | Body links | 7.4:1 | | Inline code | 7.2:1 | | Accent green (hover, focus) | 5.6:1 | | Header text on the brand slate | 10.2:1 | All above the 4.5:1 that body text needs. ## The light scheme is unchanged A pixel diff of a local `mkdocs build` of this branch against one of `main`, both rendering light: - configuration page: 254 differing pixels, all inside the header toggle icon (x 1036-1057, y 14-37) - header strip at 2x: 824 differing pixels, all inside the toggle icon (x 2072-2114, y 28-74) - home page: the same 254 header pixels, plus 900 pixels inside the video placeholder where the loading spinner animates between captures So the only thing the light scheme gains is the toggle. ## Screenshots From a local `mkdocs build`, 1920x1200 (and 480px for mobile). ### Header, 2x - the toggle Before (no toggle): ![header before](/attachments/3bfb4b6c-8e4b-437a-9efb-b144cabd9b20) Dark - sun icon: ![header dark](/attachments/8c6690ad-c3c8-469b-9801-2824905a5459) Light - moon icon: ![header light](/attachments/cfb6409d-fa33-4032-8c0b-ee043dc4fb52) ### Home - dark (the new default) ![home dark](/attachments/c34c0014-f5e2-4354-9b1d-030106062303) ### Home - light ![home light](/attachments/7b140dd9-0d1f-4349-9e13-166f04207133) ### Configuration page - dark ![tables dark](/attachments/6e19f7f3-6a6c-429e-bdc8-54e3488e2f40) ### Configuration page - light ![tables light](/attachments/fcae4917-a2c1-4d84-8330-1f3bc93ee148) ### Mobile, 480px - dark ![mobile dark](/attachments/91a03ab9-ed62-4146-be80-81d9481af5e8) ### Mobile, 480px - light ![mobile light](/attachments/53697dc7-75a9-4e41-b4d7-dfefbc6b0a2a) ## Notes for the reviewer - The toggle is present at 480px too, at 40x40. - The header stays the brand slate in both schemes, so the site keeps its identity in dark mode. - Admonition colours are still Material's semantic set, unchanged in both schemes. - No CI runs on this repo's PRs - `publish.yml` publishes on push to `main` - so nothing gates this but review.
hermes added 1 commit 2026-09-28 23:22:09 +00:00
The docs site only offered Material's light scheme. This adds the dark
scheme and makes it the default.

- palette: two entries, slate first, each with a toggle. Material takes the
  first entry as the default, so the site opens dark and a sun icon in the
  header switches to light (and a moon switches back).
- dc-brand.css: the dark scheme's greys are derived from a hue variable, so
  it is pointed at the brand slate (206deg) rather than Material's neutral
  225deg, which gives the whole scheme the same cast as the header.
- dc-brand.css: the brand slate is unreadable on a dark background, so the
  dark scheme uses a light tint of it (#9fb5c6) for body links. Green stays
  the accent, which keeps link hover visible in both schemes.

Contrast, over the dark background rgb(30, 36, 41): body text 8.8:1, body
links 7.4:1, inline code 7.2:1, accent green 5.6:1, header text 10.2:1. The
light scheme is unchanged.
allan merged commit c800145ead into main 2026-09-28 23:44:54 +00:00
allan deleted branch docs/dark-mode-toggle 2026-09-28 23:44:54 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/docs.datacontroller.io#13