A dark theme that does not flash
The theme has to be applied before the first paint, which a deferred module cannot do.
IntermédiaireArticle4 steps
Theme flashing has a single cause: the theme is applied after the browser has already painted the page. Everything else follows from that.
1. Put the tokens on the root
The theme is carried by an attribute on <html>, and every colour resolves through a CSS
variable. Changing the attribute then re-themes the whole page at once, without any
component knowing the current theme.
2. Resolve the theme in the <head>
The script has to be inline and blocking. A type="module" script is deferred by
definition: it will run after paint, and the visitor will watch the page change.
3. Let an explicit choice win
With nothing stored, follow prefers-color-scheme. With a stored choice, the choice wins
— including when the system preference changes afterwards.
4. Test the right thing
A test reading the attribute after load passes even when the script is deferred. Read it at the first frame instead, which is the closest a test gets to what the eye saw.