I shipped a button whose label was invisible. Same colour as its own background — 1:1 contrast, no readable text at all. It was only broken in dark mode, and my CI was green the entire time.

It was green for two reasons. I expected the first one. The second one is why I am writing this.

Reason one: the headless browser boots in light mode

axe evaluates the colour scheme that is actually rendered. It has no way to reason about a palette that was never painted. My pipeline loaded the page once, in whatever state Chromium starts in, and that state is prefers-color-scheme: light.

That part is well known, and the fix is one line: