Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
The Future of CSS: Easy Light-Dark Mode Color Switching with Light-Dark() (bram.us)
127 points by dcre on Oct 9, 2023 | hide | past | favorite | 70 comments


I've done a LOT of dark mode work, to the point where I'd consider myself an expert here[1].

I'm not a fan of this. This solves one tiny aspect of the larger problem in a non-scalable way that will inevitably lead to bloat and inflexibility. It's a really a naive approach that hasn't taken into account design at scale, nor the future of where design is heading.

Typically when doing theming, you have two axes - a visual mode axis (i.e. light/dark/high contrast/colorblind modes/etc) and a theme axis (i.e. docs/sheets/slides, each with a different brand color). While this does solve an aspect of the visual mode axis, as soon as you add either a new theme or a visual accessibility mode, you'll be forced to refactor. I see that as codesmell.

I also don't think this helps drive a better future of theming support. If we think about the future of theming, what we see today is a convergence of design patterns. Nearly everyone is doing theming at scale in at least roughly the same way (a semantic token layer that points to different primitive colors depending on the theme), and the differences between implementations continues to diminish over time. The convergence of patterns is a good thing - it means more code can be shared.

If you wanted to actually solve theming, what you should work for is not a constrained helper function like light-dark(), but instead a shared token schema. Today nearly every company has their own token schema and different ways of naming things in the semantic token layer. If we had a shard language here, not only would it be trivial to add light/dark theming (just redefine a few variables that are already provided for you), code could be shared between sites and inherit the theming/branding.

[1] Most recently leading Figma's dark mode stream, and currently leading their variables feature work to enable others to easily do light/dark/etc. I've consulted with 50+ enterprise tier companies on their theming. Also contributed to the W3C design token proposal for theme/mode support: https://github.com/design-tokens/community-group/issues/210. Previously worked on Jira's dark mode + a few other projects.


(Hi, author of the post/article here)

> This solves one tiny aspect of the larger problem in a non-scalable way that will inevitably lead to bloat and inflexibility. It's a really a naive approach that hasn't taken into account design at scale, nor the future of where design is heading.

Note that `light-dark()` isn’t the end goal here. As discussed within the CSS WG, the end goal is to have a generic function `schemed-value()` to give you what you want.

I realize this wasn’t included in the post, so I’ve added a new section to the post with this information: https://www.bram.us/2023/10/09/the-future-of-css-easy-light-...

I hope this addresses your concern. If not, feel free to let me (or the CSS WG for that matter) know.


FWIW, I added a comment here around a simple but more flexible proposal, that allows for easy light/dark theming via this approach, but would at least scale (and would allow for color-scheme's custom identifier support to be useful): https://github.com/w3c/csswg-drafts/issues/7561#issuecomment...


Remember that the projects you work on are not typical of most CSS usage in the world.

For small projects that never need to "scale", this looks handy.


Very true! Nonetheless this approach would also break compatibility with `color-scheme`'s custom identifiers, which is just bad in general for CSS even for these small projects. The alternative proposal I put together here would allow this to scale and still provide a helper function for small projects to use: https://github.com/w3c/csswg-drafts/issues/7561#issuecomment...


I do like your alternative proposal.

For the viewers at home:

:root {

  color-scheme: light dark high-contrast;
}

body {

  background: scheme(light #eee, dark #333, high-contrast #fff);

}


I appreciate that you not only have a nuanced critique but an alternative proposal as well.


Even if you never need to scale, Having light/dark & high contrast is just having basic features and complying to the law. If your site has 50 users it should have these, and this new light/dark switch is still inappropriate for even this basic use case.


What law? The U.S.'s Section 508 and other laws and regulations based on WCAG, don't require that sites honor `prefers-color-scheme` or to work correctly when `forced-colors` is active.

Nevertheless, it's a good idea to do so (though if a site honors `prefers-color-scheme` it should also have a setting so the user can choose to have that site not match their system's color scheme setting).


> If you wanted to actually solve theming, what you should work for is not a constrained helper function like light-dark(), but instead a shared token schema. Today nearly every company has their own token schema and different ways of naming things in the semantic token layer. If we had a shard language here, not only would it be trivial to add light/dark theming (just redefine a few variables that are already provided for you), code could be shared between sites and inherit the theming/branding.

Isn't that the idea behind https://open-props.style/ (and https://theme-ui.com/ in JS land)?

I think it's a great idea, but hampered by the lack of adoption incentives for the very people that need to adopt it for it to become successful (design system/component library authors). It introduces constraints, but the promised interoperability is not really beneficial to the people who need to work within those constraints.


There's millions of downloads every week for mui and bootstrap. Anyone using them is using something like theme-ui.


That link goes deep. I agree about the tokenizing.

I hate that we've picked off all the easy work on web design and now we're into problems we never even imagined 10 years ago, e.g. multiple visual modes, differing color spaces etc. This stuff is getting really, really hard to do well and do it right -- it is very easy to make things worse for a lot of users, rather than better.


I agree this looks like it will be of questionable use at scale. That said I don’t see a shared token schema ever achieving much. Those sorts of efforts usually flounder because there’s little incentive for adoption until a critical mass is achieved


> shared token schema

Could you explain or give an example of what this is?


theme-ui (https://theme-ui.com/getting-started), mui's `Theme` (https://mui.com/material-ui/customization/default-theme/), bootstrap's variables

Basically, when you think in purpose instead of colors.


Jira has a dark mode??


Does now. It's pretty nice.


Oof. Unless I’m missing something, this is a convenience that doesn’t enable anything new and uses syntax/terms that don’t naturally generalize for future applications. It just embeds one recent trend in very specific, very rigid terms.

This is how standards get bloated.


I agree.

You can already do this using the space toggle technique:

:root { --dark: ; }

@media (prefers-color-scheme: dark) { --light: ; --dark:initial; }

.example { color: var(--light, #000) var(--dark, #fff); }


That looks awful, but presumably if you are using sass instead of css you could define a light-dark macro already which does exactly that.

I wonder if custom functions might ever make their way to css.


Ah yes, the CSS Space Toggle. The way this works internally is a seemingly-unintended but very useful aspect of the CSS specification.


Correction, the media query should have been:

@media (prefers-color-scheme: dark) { :root { --light: ; --dark:initial; } }

Missed the selector.


(Hi, author of the post/article here)

Yes, `light-dark()` is very specific in what it can do … but it’s not the end goal. The end goal is to have a generic function – something like `schemed-value()` – that can respond to a plentitude of `color-scheme` values and return more than just `<color>` values.

Reality is, though, that browsers don’t have support for custom `color-scheme` values (it’s explicitly forbidden in the spec, for now) [^1] and that CSS parsers need to know ahead of time what they are about to parse [^2].

By narrowing things down in feature scope, you can use this convenience method now, instead of needing to wait a few months/years until the rest is figured out.

Once `schemed-value()` becomes a thing, `light-dark()` can become syntactic sugar for it:

``` light-dark(<color>, <color>); = schemed-value(light <color>, dark <color>); ```

[^1]: https://drafts.csswg.org/css-color-adjust/#color-scheme-prop... [^2]: https://www.w3.org/TR/css-syntax-3/#parse-grammar


God forbid someone adds practical features for common use cases to our beautiful turing tarpit.


You can have both. But good tool design looks at the horizon, not just at your feet.

You could deliver the same convenience in a way that’s set up to be repurposable (fullcolor /rgcolorblind) or extensible (light / grey / dark / …n) with as little difference as choosing a more mindful name.


I agree with the previous comment that the current approach lacks flexibility in achieving high contrast. It might be beneficial to consider a more general and adaptable method that can accommodate different styles or even introduce primitives for different styles in the future


Remember when UIs were customisable to the point that you could choose any colour for all the elements?

Now it's "you can have any colour you want, as long as it's light or dark", and all of a sudden "dark mode" is heralded as something revolutionary and new.

CSS3 even deprecated the "system colors" feature that was meant to allow the same degree of UI customisation we had before for websites too.

It's nothing but a really sad regression in empowerment, the continuing trend of treating users like cattle.


Heh, I was thinking about this recently, but from a slightly different angle: most browsers don’t feel like user agents any more. https://untested.sonnet.io/The+modern+Web+has+lost+the+User+...

Personally, I don’t mind the light-dark function although I don’t see much utility in it, since I use variables for this sort of thing.


Set your browser to always use reader mode, on desktop and mobile. It solves all the problems of modern web browsing. Web sites do not deserve to use any of their own CSS or layout by default.


> CSS3 even deprecated the "system colors" feature

Specific system color keywords were deprecated but the idea of system color keywords was not. They're essential for when `forced-colors` is active.

https://developer.mozilla.org/en-US/docs/Web/CSS/system-colo...


How is this better than using CSS variables for light/dark mode? With CSS variables you have better isolation for consistency with your entire color scheme, and you can extend it to supporting multiple themes (e.g. light, dark, blue).


My entire automatic light/dark support is, very early in the HTML:

    <meta name=color-scheme content="light dark">
then, in in-line CSS:

    @media (prefers-color-scheme:dark){body{background-color:#000;color:#eee}img{filter:brightness(.9)}}
I'm not seeing this new thing as a huge win so far.


Interesting that you adjust the image brightness.

I have everything set to dark mode, but I wouldn't want the images altered. Unless these are just navigational images on your site and you don't have any photos?


Images with a full dynamic range look too bright in the dark mode. I set them to 0.9 as well on my resources, otherwise the eyes are bleeding deep in the night.


There are multiple reasons for a user to want "dark mode":

* I just want everything to be dark on my screen because I like it.

* I am trying to use this device in a dark place.

* I want a dark, low-contrast background that doesn't compete with image colors.

The solutions to these three problems are all some kind of "dark mode" but each one needs a different solution. Sometimes one "dark mode" might work in conjunction with the user manually changing their brightness settings depending on the lighting in the environment, but not many people seem to design for that.


How does this deal with the ternary of light/dark/high-contrast that a lot of sites are now using?

I wish they would just enable features that are already in the browser that they've not put live, like grid-template-rows: masonry. All three major engines have the damned thing in there but it's not in release branch.


Masonry isn’t ready to be shipped as there are still quite a few open spec issues [^1] that need to be resolved first.

[^1]: https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+is%3...


There's also the light/dark/system (auto) ternary approach, which this seems to encourage the designer to ignore in favor of a binary toggle. It seems to beg the developer to treat the mode as a negation of whatever it currently is, which leads to bad results and time-dependency of setting.


Exactly. I'm not sure this is totally thought-out.

p.s. love your work


if browsers have support for a feature such as masonry, but it is not enabled in css spec does that mean it cannot be used? I recall seeing masonry in a tailwind talk, very neat!


Well, most people implement it using JavaScript if you want everything to be in the correct order. I use a pure CSS variant as I'm trying to go near-zero JS on my projects these days, but it means things are out-of-order. Depends on your use case.

I also check for CSS support in the browser using the "supports" feature and utilize the native support if it is enabled (e.g. Firefox behind a flag, and Safari behind a switch).


I’m consistently amazed at how much energy has been put into the light/dark UI pattern for web and mobile apps. Way way more than was ever put into dealing with colorblindness or any other form of color adjustment imo.

It’s nice to have the choice but I also wonder what else might have been done with all that UX/UI design and SWE time.


Right? This whole affair seems very disproportionate. My bank recently announced a redesign of their online web app, and one of the major points they said they focused on was ... dark mode? Really?


That seems to be linked to the spreading belief that this is a health issue instead of an aesthetic preference. Those who now believe this option somehow hurts their healthy eyes might already outnumber the actually colorblind.


Nah, that's not what it is. It's just fashion. Dark mode is the new flat icon.


It's because devices have been pushing these system preferences.

At one point I believe MacBook Pros were coming with default dark mode(or at least asking you which you prefer during startup).

My phone auto switches to dark preference based on time/environment.

Now, you don't want to be the ONLY product your user uses that blasts their eyeballs out because it doesn't respect system preferences.. Do you?


I guess it's better than nothing, but this does not feel like a very CSS way of solving this problem. I think I'd rather write a separate block of styles for dark mode, the way you do for other media queries. Happy to hear why that's wrong though.


It is not wrong, just annoying and error prone to write everything twice in two different places, but like you point out that is the CSS way.


It's "only" colors ? So far from 100% of your css (especially if you use something like tailwind where css class for shape and color are clearly separated)

Also I m not a css expert but can't you also simplify it by using css variables so that you can further reduce the scope of change by having only the css variables that change value ? Your --primary-color-500 etc.


If I look at the times I’ve implemented dark mode — I see that the effect will be:

1. Less lines of code (seems like a good thing)

2. I will still need to use prefers-dark-mode for some of my changes.

Thus — the concept count is ratcheted up by 1 more thing, and no concept is taken away.

The “concept count” is ridiculously high already.

They’re spending money from an overdrawn account.

If you want to permanently increase the number of things that a bunch of people must learn, it has to have more value or power than this.


> I will still need to use prefers-dark-mode for some of my changes

Can you detail what those would be? I’m curious to know :)


Sorry I missed this question earlier.

I may turn down the brightness on images, generally, in dark mode. (Using a filter).

And have some classes that mean “don’t alter the brightness of this image, in dark mode”

And there may be other images which I will invert the color of. For example any image that is basically black text on a white background — it’s better to invert it completely rather than just lower the brightness.


I can't believe stuff like this is still difficult.

In the late 00s I wrote a frontend for a very popular open-source web project that many here would have encountered. Unlike many web projects these days, it is considered de rigueur for each instance to implement its own stylesheet. Most instances will also allow each user to select their own stylesheet.

This is trivial because the frontend was written HTML first. The HTML contains absolutely no hint at how the style should be. The navigation is just the navigation, the content is the content etc. The nav could be at the top, bottom, side, whatever you can come up. And people did all of them.

And, of course, some of those stylesheets were dark mode.

Print stylesheets were also common, which would hide all the web nav stuff when printing the document.

How did all the layers of crap added since then seem to forget this and make this quite simple concept difficult? You can't tell me it's difficult to build two stylesheets, one dark, one light, and have them both as options. And what about other accessibility options? Low contrast? High contrast? It's not just about dark and light...


Dark mode is a weird phenomenon, I think. It kind of came out of nowhere and suddenly everything in existence started adding a dark mode and prominently listing it as a top features. That is not a complaint or anything like that, just an observation that dark mode seems to have spread in an unusual way compared to other technologies. But might of course also be just my perception.


The alternative is sick eyes


I actually remember from my software ergonomics course at university that black on white is better than white on black. I guess it stuck because it surprised my, I always considered the MS-DOS console easier to read. I do not remember any details, but I guess it must have been about working in a well lit office or something like that. Dark mode is probably only advantageous because we nowadays use screens in a lot of situations with all but ideal lighting.


A genuinely useful behaviour of a function like this would be to return the colour with most contrast over the background of its container element, rather than a predefined colour scheme.

I know that’s not how CSS works.


That is also in the works as color-contrast(): https://developer.mozilla.org/en-US/docs/Web/CSS/color_value...


Even though that’s a kind of strange and unworkable idea — it’s interesting to think about.

Unworkable in the sense that, for example, some elements are not physically positioned inside their container elements, and sometimes even in light dark mode you might use elements to create subtle layering etc, where you’d want the least contrast.

It wouldn’t be too difficult to write a gimmick webpage though, that implements the idea, to see what kind of fun would ensue.

I’ve got some sample code here that uses the little known but very powerful “tree Walker” to change elements 1 by 1.

https://til.secretgeek.net/css/replace_text_with_property_va...

And you can write javascript code that alters css variable values like this:

var root = document.documentElement; root.style.setProperty('--main-hue', 0);

I think the particular can of worms you’re suggesting could be very strange and interesting!


If people envisioned that web would become an application platform, maybe we wouldn't have CSS and HTML but instead some sort of lower level bytecode (ala WASM) and the actual higher level document and styling tools would be separate and compile to this bytecode. It makes me shutter every time I imagine how much you have to cover from CSS and HTML alone for a function web browser.

This method is pretty much ternary operator and it requires a browser support to be used, insanity.


This is so wrong on so many levels. What about Light-Grey-Dark()?


(Hi, author of the post here) `grey` is no accepted value for `color-scheme`, so that would make no sense.

Note that `light-dark()` is not the end station here, it’s a step towards a future function that (1) would be able to respond to any value for `color-scheme` and (2) can return any type of value.


"Here is an up-to-date list of browser support for the CSS :has() selector:"

I think this text is wrong. https://caniuse.com/?search=light-dark does not have an entry for this yet.


( Hi, author of the post here) Thanks for catching this. I’ve updated the post to fix this copy-paste error.

CanIUse has no entry for it, as this feature is brand new. The PR add the data (https://github.com/mdn/browser-compat-data/pull/20935) is still pending.


I strongly prefer this more flexible/future proof proposal:

https://github.com/w3c/csswg-drafts/issues/7561#issuecomment...


What would be neater is a `media()` CSS function, similar to `var()`, `env()` or `calc()` that allowed you to conditionally switch values based on arbitrary media queries, for example:

color: media(min-width: 1001px, red, blue)


Often I want "a bit darker" just to take the edge off blinding white light. This HN page for example, I use the Stylus browser extension to reduce the background white of page and this text box to a medium grey. Same for Wikipedia. I tried making a Stylus entry that worked on any website, but couldn't do it.


Would this make toggling between schemes easier? Suppose there is a button on the page that lets user pick light or dark. What would the js look like?


Toggling between schemes is not part of this feature.

Easily toggling light/dark is part of the Web Preferences API feature, which is being prototyped in Chrome: https://chromestatus.com/feature/4631882616012800


> The Future of CSS: Easy Light-Dark Mode Color Switching with Light-Dark()

So this is the first function which changes light gray with dark gray. And they call it "color". /s




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: