Skip to content

Light and dark mode

A generated page has no light/dark control of its own: it takes its scheme from its surroundings. Embedded in a docs site with md-iframe, it follows the site's toggle, live, with nothing to configure. Flip this site's toggle in the header and watch the pinout below change with it:

PinConnect pinouts and 3DStep pages both work this way, by the same rules, and the 3DStep light and dark example shows a 3D page doing the same.

How the page decides

On load, the page picks its scheme from the first of these that applies:

  1. A ?theme= parameter. Opening board.pinout.html?theme=dark (or ?theme=light) pins that scheme and stops the page from following its surroundings. Handy for linking straight to one, or for an iframe you always want dark.
  2. The embedding page, when the page is embedded from the same origin. It reads MkDocs Material's and Zensical's data-md-color-scheme (where slate means dark), a generic data-theme="dark"|"light", or a dark class on <html> or <body>, and keeps watching, so flipping your docs site's toggle re-colors the embedded page live. This needs same-origin access, so it does not apply across origins.
  3. The reader's OS preference (prefers-color-scheme), when neither applies.

The normal docs-site setup, with the generated pages inside the site's own docs/ folder, is the same origin, so step 2 does the work.

Pinning a scheme on an embed

Add ?theme= to the path in the tag:

![Board pinout, always dark](pinouts/board/board.pinout.html?theme=dark){ type=iframe }

The light and dark example shows a pinout pinned each way beside one that follows the site.

Across origins

A page embedded from another origin cannot read its parent, so the parent tells it instead, by posting a message to the iframe at any time:

iframe.contentWindow.postMessage({ pinconnectTheme: "dark" }, "*");

Send "light" or "dark" to set the scheme. It takes effect immediately and overrides all of the above, including ?theme=. Any other value clears the override and hands control back to the reader's OS preference. 3DStep pages take the same message, key and all, so one script drives both tools.

For example, a site whose own toggle sets data-theme on <html> can keep a cross-origin pinout in step like this:

function sync() {
  var scheme = document.documentElement.dataset.theme === "dark" ? "dark" : "light";
  document.querySelectorAll("iframe.md-iframe").forEach(function (f) {
    f.contentWindow.postMessage({ pinconnectTheme: scheme }, "*");
  });
}
new MutationObserver(sync).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
document.querySelectorAll("iframe.md-iframe").forEach(function (f) { f.addEventListener("load", sync); });

The palettes themselves

Every bundled theme carries both a light and a dark palette, and a theme of your own can set either or both. See the PinConnect themes reference and the 3DStep themes reference.