Dark mode
How Project Broadsheet implements automatic dark mode via prefers-color-scheme, the manual theme toggle in the header, and the no-flash initialization script.
Project Broadsheet ships with full dark mode support. The site respects the reader's operating-system preference via prefers-color-scheme, and offers a manual toggle in the header for readers who want to override it. There's no flash of the wrong theme on page load, and every component is independently verified for contrast in both modes.
How the theme is chosen
- First visit. The theme-init script in
<head>readsprefers-color-schemefrom the browser and setsdata-theme="dark"ordata-theme="light"on the<html>element before any CSS loads. - Repeat visit. The same script checks
localStorageforpb-theme, which overrides the system preference. - Manual toggle. Clicking the sun/moon button in the header flips
data-themeand saves the new value tolocalStorage.
Because the theme is set before the first paint, readers never see a flash of the wrong theme.
Where dark mode lives in the CSS
Light mode values are the default, declared on :root:
:root {
--paper: #F4F1EB;
--ink: #1A1A1A;
--vermillion: #C0392B;
}
Dark mode values override them when data-theme="dark":
:root[data-theme="dark"] {
--paper: #12100E;
--ink: #E8E3D6;
--vermillion: #E05A4B;
}
Every component references these variables, not raw colors. That means changing light and dark values in one place updates the entire site.
Component-level overrides
Some components need more than a token swap. For example, the site footer uses a pinned dark surface in both themes, so outline-light buttons always have correct contrast. Any time a component needs to behave differently in dark mode, the CSS uses an explicit html[data-theme="dark"] selector:
html[data-theme="dark"] .card { background: #1A1815; }
Grep for data-theme="dark" in the CSS and you'll find every override.
Contrast verification
Every color pair used on the site has been checked against WCAG 2.2 Level AA in both modes. The contrast table is documented in the Style Guide.
The theme toggle button
The button lives in src/_includes/partials/header.njk. It's a plain <button> with an inline onclick="toggleTheme()" handler. The JS is in src/assets/js/main.js:
window.toggleTheme = function () {
var current = document.documentElement.getAttribute("data-theme");
var next = current === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", next);
localStorage.setItem("pb-theme", next);
};
That's it. Twelve lines of JS and a couple of CSS variables.
Reduced motion
Both the manual toggle and any component transitions honor prefers-reduced-motion. Readers with motion sensitivity don't see the cross-fade.
What to do next
- Customize design tokens to change the dark palette.
- Fonts and typography for type choices.
- Accessibility statement for the full contrast table.
Browse Support for community channels and paid support options, or book a call if you'd like me to set it up for you.