While surveying my Twitter timeline this morning, wondering what fresh horrors awaited me, this blog post about FOUT and dark themes in Gatsby/Next sites. Neither Gatsby nor Next are parts of any of my stacks at the moment, but Daniel's reasoning is pretty solid.
I've been thinking about dark themes lately—still need to make one for this here site—and Daniel's post sent me down that rabbit hole again.
One of my personal hurdles is keeping my CSS as minimal as possible 1, and including unnecessary dark theme-specific CSS (or light-themed, depending on your perspective; we do not theme-shame here) always struck me as bloat. Sure, bloat that makes for a better user experience, but bloat nevertheless.
My first thought to solve this was putting splitting the theme-specific CSS into a separate file, and slapping a
media attribute on it.
EXAMPLE CODE, GO!
If your OS is set to use light themes, you'll see black text on a white background, and if your OS is set to use dark themes, you'll see white text on a black background.
Sometimes I forget that the
media attribute accepts any valid media query, and it operates like
@media block rules in your CSS. But that's what it does. It's pretty neat.
Now, the assumption I made here is that placing
dark.css in a separate file could be a performance gain because, for example, the browser would see
(prefers-color-scheme: dark) and not request the file if the visitor's OS were set to use light themes.
That assumption was and is very, very wrong.
I opened the network tab in dev tools and saw that in Firefox, Chrome, Edge, and even IE,
dark.css was requested regardless of the OS settings. Just to make sure I wasn't missing some typo, I changed
I could've just accepted that as The Way and moved on, but reader, you are here for a blog post and that would not be very fun. So, LET'S GO TO THE SPEC! Specifically, HTML 5.2, section 4.2, with my own add emphasis.
That second paragraph contains the answer. The browser must apply the styles in that resource when the media query matches the environment, and the thing we have to remember is that the environment can change at any time, for any number of reasons.
In my example code,
dark.css is only applied if
(prefers-color-scheme: dark) evaluates to
true, but you can change your OS settings at any time, and the document styles should update accordingly (and in my opinion, with little to no effort on the part of you, the visitor).
In the case of, say, setting the value of
If we set
(min-width: 500px), the viewport may or may not be 500px wide when the page is loaded, or it may scale up or down after the fact, so the browser needs to be able to apply the styles when appropriate.
I'll keep this list of scenarios at three for brevity (there's a lot of possible media query situations) and because three is an aesthetically please number for me. We all win.
The point is that the browser downloads the resource even if
media doesn't match the environment because it could match at some point. In other words, using
media is not a performance win. Womp womp.
A little more about that
Let's look back at the note included in section 18.104.22.168 of the spec: "The external resource might have further restrictions defined within that limit its applicability. For example, a CSS style sheet might have some
@media blocks. This specification does not override such further restrictions or requirements."
So the process goes:
- The browser downloads the resource regardless of whether
- Those styles are applied according to the outcome of
- But any media queries found inside that resource are also evaluated and applied accordingly, regardless of the
Pretty straight forward if you ask me—but no one did, so of course this isn't how it works.
The way step 3 actually happens is that the subsequent media queries in the linked resource are effectively nested within the value defined in
media (and remember, its default value is
all so if it isn't explicitly defined, all of your linked media queries are then nested within an implied
@media all rule).
For example, if you set
(min-width: 500px) and included a
(min-width: 600px) media query, the styles defined in the latter would be applied because both conditions were met.
However, if you set
(width: 500px) and included a
(min-width: 600px) media query, the latter would never be applied because the viewport could not be both 500 pixels and 600 pixels wide simultaneously.
Ok, so what are the takeaways?
mediadoesn't prevent network requests
- Media queries in a linked stylesheet are nested within the condition in
media(with a default of
- It's ok to put your theme-specific CSS in with the rest of your CSS and work to keep it all streamlined
"lol," said Sisyphus
I don't the specifics of how a browser's printing UI works, but I am almost positive it is not a separate browser tab and would not trigger new network requests when the UI is activated.