theme-color (#281)¶
Generated by test/visual/theme-color-evidence.mjs.
Why there is no screenshot in this bundle¶
theme-color paints mobile browser chrome — the iOS Safari address and status bar, the Chrome/Android task-switcher card. That is OS UI outside the page, so Playwright cannot capture it, and any before/after image of it would be a drawing of the claim rather than the claim itself.
What can be measured is the pair of things that actually decide whether the chrome is right: which tags a build emits, and whether their values equal the surface the page really paints.
Measured — /, /docs/, /404.html, all identical¶
| before | after | |
|---|---|---|
| tags emitted | 1 | 2 |
| shape | content="#007bff", no media |
one per prefers-color-scheme |
| light value | — | #ffffff |
| dark value | — | #212529 |
equals computed --bs-body-bg (light) |
n/a — no light tag | true |
equals computed --bs-body-bg (dark) |
n/a — no dark tag | true |
#007bff is theme_color.main, the brand accent — the colour of links and primary buttons. A dark-first theme was advertising a bright blue address bar.
The "equals computed" rows are the durable part. The values are not compared against constants written in the test; they are compared against getComputedStyle(document.body).backgroundColor under each data-bs-theme. If someone later recolours the surface and forgets the meta, the test fails.
The case this issue was really filed about is not in the table¶
The reporter's observation was zero theme-color tags on lifehacker.dev — a remote_theme consumer. That site does not appear above because it is not this build, and it could not: a served-page test always runs with this repo's config, where the tag was present but wrong.
The reason a consumer got nothing is structural. remote_theme consumers do not inherit the theme's _config.yml (remote-theme-checklist.md), and the old tag sat behind {% if _fav_theme_color %} with its only fallback being site.theme_color.main — a key such a consumer has not declared. Nil in, nothing out.
Reproducing that needs the include rendered against an empty site, which is what test_theme_color_fallback_without_config in test/test_core.sh does:
main, empty config -> 0 tags []
branch, empty config -> 2 tags
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#212529">
Full config matrix, rendered through Liquid¶
The shape depends on how the mode is decided, because media="(prefers-color-scheme: …)" keys off the OS and nothing else. A pinned site gets one tag on purpose — a media pair would hand light chrome to an OS-light visitor looking at a page the site renders dark.
| config | tags |
|---|---|
| empty (remote_theme consumer) | 2 — #ffffff / #212529 |
this repo (auto, unlocked) |
2 — #ffffff / #212529 |
color_mode_default: dark |
1 — #212529 |
color_mode_default: light |
1 — #ffffff |
color_mode_lock: true + auto |
1 — #212529 (locked auto resolves dark, matching color-mode-init.html) |
color_mode_lock: true + light |
1 — #ffffff |
favicon.theme_color: "#0d1117" |
1 — #0d1117 (back-compat: a consumer who pinned a colour keeps it) |
theme_color_light / theme_color_dark set |
2 — the given values |
Tests, both directions¶
test/visual/core/head-contract.spec.js — 4 new @critical tests, run against two served builds:
fixed build 4 passed
pre-fix build 4 failed
"an auto-mode site emits one tag per scheme" (1 tag, not 2)
"the brand accent must not be advertised as chrome" (#007bff)
"no theme-color is declared for prefers-color-scheme: light"
"no theme-color is declared for prefers-color-scheme: dark"
The last two originally failed by 30-second timeout, because reading an attribute off a locator matching nothing waits rather than reports. They now count first — "nothing is emitted" is exactly the pre-fix state this test has to be able to describe.
Known limitation, recorded rather than solved¶
media follows the OS scheme. On an unlocked site a visitor can override the mode from the Appearance panel (localStorage), and no static meta can follow that — an OS-light visitor who picks dark gets light chrome over a dark page. Keeping chrome in sync with the panel needs a runtime hook in the mode-toggle path. That is out of scope here, and the pinned-mode branches above already remove the config-level version of the same mismatch.