网页
综合
更新时间 2025-01-06 00:00:00
Native HTML light and dark color scheme switching — Vadim Makeev
自动蜘蛛
pepelsbey.dev
正文约 6000 字
Skip to main content
It’s getting dark early in Berlin in the winter. It’s not even close to evening, but my OS and all apps have already switched to dark mode. Well, not all of them, unfortunately. And that’s the thing: dark mode has become a quality-of-life feature for many users, and I often try to avoid using apps or websites that haven’t implemented it, especially in the evening. They literally hurt my eyes!
When it comes to color scheme implementations, they range from rather useless ones that require a page reload to more sensible ones that query the prefers-color-scheme media feature and apply changes in CSS on the fly:
I’ll be using native CSS nesting in all demos throughout this article. It works in all modern browsers and makes code a bit more compact, especially when it comes to media queries. But if you’re not familiar with CSS nesting, you can use this handy Lightning CSS playground to figure out how it looks without nesting.
This approach is already a good start. But it only covers the simplest case and doesn’t allow users to choose a different color scheme for this specific website. Just like light color schemes hurt my eyes in the evening, many people are not comfortable with dark schemes or with particular ones that aren’t good for them. So, it’s all about user choice.
Currently, there’s no way to directly override a user’s OS preference if you want to offer a scheme selector on your page. Fortunately, in the CSS Media Queries Level 5 spec, there’s a PreferenceManager interface that will solve this problem. Meanwhile, the most popular solution these days is to use JavaScript to set an attribute like <html data-scheme="dark"> reflecting the forced scheme and use it in CSS:
This approach always seemed hacky to me. However, if you know HTML well enough, there are a few much more convenient native options available.
If you ever fall into the dark scheme rabbit hole, the first thing you’ll learn is the color-scheme CSS property. It’s essential for setting the scene for everything else. Most importantly, it turns on the browser’s default dark scheme support. Which, unfortunately, browsers can’t enable by default for backward compatibility reasons.
The light dark value means that we’re choosing to support both light and dark schemes in our code. The property will be inherited down the document tree, and the browser will enable some default styling for built-in primitives when needed. Thank you, browser!
When switching your schemes, it’s important to switch the value of this property, too: set color-scheme: light on the root along with light styles and the other way around for the dark ones. Remember this, it’ll come in handy later.
And somewhere along these lines, you’ll probably read that you can also set this mode right in HTML using the <meta> element. Why would you do that? Oh well, who knows? Maybe you don’t use CSS or whatever. So silly, right?
It turns out that it’s not just a flag for the browser but a tool you can use to force color schemes using JavaScript! But only if you use the fairly new light-dark() CSS function.
Remember the earlier example with media queries and prefers-color-scheme?
You can also express the same idea like this:
By the way, I use --color-back and --color-text variables in my demos. It makes it easier to set colors even in a small demo, let alone a bigger project. But to make things easier to read, I chose to set colors directly in code samples.
Here’s the three-position switch I often use. Along with “light” and “dark” options that force a certain scheme, there’s also the “auto” option that gives the control over the color scheme back to the OS, selected by default.
And to make it all work, a simple script that takes the button’s value and sets it to the <meta name="color-scheme">:
As you can see, once we set content="dark" the browser switches to the last value in the light-dark() function and the other way around with the light one. This HTML’s color-scheme turned out not so silly after all! You can check this method in action on this website. And if you happen to use Safari or Vivaldi browsers, you might notice something else changing while you switch the schemes.
To make it work properly, you’ll need to decide where to store your global color-scheme value, so the script can force it. In this example, I chose to use the HTML one, so I removed the color-scheme property from CSS. But you can also keep it in CSS and force it via JavaScript like so:
For a good UX, you’ll also need a way to store user preference somewhere in localStorage, so the users won’t have to switch it next time they visit or when opening a new tab. I’m sure you can figure it out on your own!
One of the downsides of this approach is the browser support: the light-dark() CSS function has been available in all modern browsers since May 2024, which makes it “newly available” on the Baseline scale. It will become “widely available” only around November 2026 or 30 months later. You can transpile it for older browsers using Lightning CSS or PostCSS plugin, but make sure you check the output and test it in an older browser. It might be a bit tricky at times.
As for the other major downside, the light-dark() function accepts only colors for now. Trust me, I tried to use it with other values, and it didn’t work. So, what’s the problem? You might need to change not just colors but images or font properties that work better with the dark scheme.
In this case, there’s another HTML solution for you!
You all know the most common way of linking CSS to a page: it’s a stylesheet linked from a file.
But did you know that you can also set the media attribute to conditionally load and apply CSS based on user preferences? Sure you can! But first, you’d need to split your files into light and dark styles:
It doesn’t look too convenient this way. A much better approach would be to split your styles into three parts! Yes, I’m serious. Hear me out!
Remember to set the appropriate color-scheme pro
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。