Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Wed, 03 Jul 2024 16:09:49 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 225069128 Why is this thing in Dark Mode? https://frontendmasters.com/blog/why-is-this-thing-in-dark-mode/ https://frontendmasters.com/blog/why-is-this-thing-in-dark-mode/#respond Wed, 03 Jul 2024 16:09:48 +0000 https://frontendmasters.com/blog/?p=2907 I was looking at an email in a web app the other day, and it was showing it to me in “Dark Mode”. The email itself (of my own creation) purposely doesn’t bother to set a background or color for the most part, as then the defaults kick in which help naturally support both dark and light modes. So I was pleased! It worked!

Email in “Dark Mode” in Front
Email in “Light Mode” in Front

But then I was like… why am I looking at this email in Dark Mode? While I was working on the email, it was in Light Mode. I made it using MJML and the VS Code Extension which gave me a preview of the email, it was was in Light Mode there, so it looked surprising to me in Dark Mode that first time.

First I checked my System Settings on macOS to see what was going on there:

Light Mode there, so it wasn’t my system that was forcing Dark Mode.

Then I checked my browser. I happened to be using Arc, which has Themes.

That selected option on the left is “Automatic Appearance” which is what some things call “System” meaning match what the OS is doing.

Since that was set to “Automatic Appearance” it was following the OS’ Light Mode so that wasn’t doing it (probably). There is also Command Bar shortcuts in Arc. Try typing “Switch” in there to see actions to change it:

Other browsers do it differently, but can do it. For example you can “Customize Chrome” from a button on the Start Page in which you can force a theme or set to “Device”.

But this wasn’t explaining it for me either, as I was Light Mode through both of those layers.

What it turned out to be was a website-level setting. I was using the email app Front. Front has it’s own settings for themes, and in there indeed it was forcing a Dark Mode.

Changing that would change the colors of my email, which is exactly what I was trying to figure out.

So in terms of power, It’s like:

  1. Website setting
  2. Browser setting
  3. OS/Device setting

And those top two typically have an option to allow the setting to fall through to the next level.

That’s a lot of stuff to check when you’re trying to figure out what is controlling a color theme! I’m tempted to say too many, but when it comes to user control over websites, I tend to be in the camp of giving as much control to the user as possible. That leaves me extra conflicted about adding browser level color mode switches on a per-side basis, as it will likely lead to a 4-level system of diagnosing what mode is active.

]]>
https://frontendmasters.com/blog/why-is-this-thing-in-dark-mode/feed/ 0 2907
What’s Going On in Dark Theme / Light Theme Land https://frontendmasters.com/blog/dark-and-light/ https://frontendmasters.com/blog/dark-and-light/#comments Thu, 18 Apr 2024 23:43:30 +0000 https://frontendmasters.com/blog/?p=1716 There has been a fresh round of enthusiasm and writing around light mode / dark mode support for the web lately. I think it’s driven partially by the new light-dark() function in CSS (CSS Color Module Level 5 spec) that makes it easier to declare values that change depending on the mode. Here’s the basic usage:

html {
  color-scheme: light dark;

  background: light-dark(white, black);
  color: light-dark(black, white);
}

In real life, you’d probably be using custom properties with your colors. So you’d set up your colors for light mode, then when the special dark mode media query matches, you’d re-declare all those variables, and then you’d use them. Paweł Grzybek has a nice basic explanation. Here’s a comparison.

You used to have to do this:

:root {
  color-scheme: light dark;

  --dark-color: #292524;
  --light-color: #f5f5f4;

  --text-color: var(--dark-color);
  --bg-color: var(--light-color);
}

@media (prefers-color-scheme: dark) {
  :root {
    --text-color: var(--light-color);
    --bg-color: var(--dark-color);
  }
}

body {
  color: var(--text-color);
  background-color: var(--bg-color);
}

And now you can do this:

:root {
  color-scheme: light dark;

  --light: #292524;
  --dark: #f5f5f4;
}

body {
  color: light-dark(var(--light), var(--dark));
  background-color: light-dark(var(--dark), var(--light));
}

Essentially, it prevents you from having to use the @media query and re-declare variables. I like it. I think it makes code like this more readable and succinct — pending variable naming and usage of course.

Here’s what it’s good for: designing a site that responds to the operating system level setting for light mode / dark mode. And you have to be good with the level of browser support (no Safari just yet, as I write, but it’s in preview). If you are wondering what the fallback is, it’s doing things the old way (see above), and if you’re going to write that you might as well leave it at that.

If you’re going to want to offer more themes than just light and dark, well, you’re out of luck here, you’ll need to implement something else, likely based on changing classes on the <html> element and updating variables when the class matches.

html.nickleoden-theme {
  --bg-color: purple;
  --text-color: green;
}

I could imagine a more composable toggle method in the future, but this is what we have for now.


When I first saw light-dark(), I liked the basic idea, but I figured as soon as you offer your own user toggle for mode, you’d be out of luck. After all, you can’t change what the prefers-color-scheme media query returns, and thus which of the two values light-dark() will pick. But it turns out you can! The trick lies in that color-scheme property. You’ll see recommendations that you use color-scheme: light dark;. If you only set one or the other, you’re forcing light-dark() to pick the relevant side.

So then your user toggle could force one or the other values on the document if it’s set. (If it’s not set, leave it alone!). Here’s that working:

I like that this is based on a CSS property, as it means that you can use the cascade if you need to, setting the color-scheme on a per-element basis.

That’s just cool I think.


Anne Sturdivant blogged some recent learnings about all this color theme stuff and I learned a few things. For one, color-scheme also has normal which is “no color schemes defined, default to the browser or OS setting”, which I would assume means works the same as light dark, but in my testing did not switch over to dark when I switched my OS 🤷. Then there is only as a keyword to, uhm, I guess even more forcibly set the theme? It’s not clear to me, and also not really supported yet anyway.

Annie also clearly points out that when you change the color-theme away from the default light mode, all sorts of stuff changes. It’s certainly not just the results of light-dark(). If you set dark, the UI scrollbars go dark, and all the different form controls go dark. In fact, that might be the number one use-case for color-scheme really, even if we do have accent-color now.


There is also this whole idea of System Colors that tends to come up in writing about this, so I’m going to do the same. These are “named” colors, like rebeccapurple, but they have a fancy baked in ability in that they can change when the color-scheme changes. So if you want to flip out basically white and black, but not bother with variables and fancy functions and whatnot, you’ve got:

body {
  background-color: Canvas;
  color: CanvasText;
  color-scheme: light dark;
}

Note the Canvas and CanvasText, those are the System Colors. Allow me to snipe the images of the available colors and what they look like in the different modes from a great article by Mads Stoumann.

Mads made a cool demo of a theme toggler that uses newfangled style queries that is worth checking out. Remember, though, that these client-side-only theme switchers are really just demos. It’s likely on a real-world site, if you’re offering a switcher, you should try to persist that information. A cookie, sessionStorage, localStorage, to a database… something. Then when you do that, there is the risk that the page renders before you can access and set that information, leading to FART, my most successful coining of an acronym ever. It’s a tricky thing, as delaying rendering just for something like this doesn’t feel right.


Another thing percolating in the industry is, as Bramus puts it: What if you had real control over Light Mode / Dark Mode on a per-site basis? I always say that web standards bodies, and even browsers themselves to some degree, are at their best when they see developers toiling and doing the same sort of things, and then introduce better and standardized methods. Properly implementing color themes and the toggling methods with persistence is real work! It doesn’t look like color themes are just a fad, so I think this is a clear opportunity for help.

I think a bit of browser UI would be perfectly welcome:

A perfect implementation would be that the browser itself remembers what choice you’ve made (light, dark, or default to system preference), and that is applied during rendering, avoiding FART. Sounds like it is going to require a new API and then potentially browsers actually using it themselves, which is kinda funny to think about. I normally think about web APIs as being for developers, not browsers.

]]>
https://frontendmasters.com/blog/dark-and-light/feed/ 2 1716