Dark mode

I had a very productive time at Indie Web Camp Amsterdam. The format really lends itself to getting the most of a weekend—one day of discussions followed by one day of hands-on making and doing. You should definitely come along to Indie Web Camp Brighton on October 19th and 20th to experience it for yourself.

By the end of the “doing” day, I had something fun to demo—a dark mode for my website.

Y’know, when I first heard about Apple adding dark mode to their OS—and also to CSS—I thought, “Oh, great, Apple are making shit up again!” But then I realised that, like user style sheets, this is one more reminder to designers and developers that they don’t get the last word—users do.

Applying the dark mode styles is pretty straightforward in theory. You put the styles inside this media query:

@media (prefers-color-scheme: dark) {
...
}

Rather than over-riding every instance of a colour in my style sheet, I decided I’d do a little bit of refactoring first and switch to using CSS custom properties (or variables, if you will).

:root {
  --background-color: #fff;
  --text-color: #333;
  --link-color: #b52;
}
body {
  background-color: var(--background-color);
  color: var(--text-color);
}
a {
  color: var(--link-color);
}

Then I can over-ride the custom properties without having to touch the already-declared styles:

@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #111416
    --text-color: #ccc;
    --link-color: #f96;
  }
}

All in all, I have about a dozen custom properties for colours—variations for text, backgrounds, and interface elements like links and buttons.

By using custom properties and the prefers-color-scheme media query, I was 90% of the way there. But the devil is in the details.

I have SVGs of sparklines on my homepage. The SVG has a hard-coded colour value in the stroke attribute of the path element that draws the sparkline. Fortunately, this can be over-ridden in the style sheet:

svg.activity-sparkline path {
  stroke: var(--text-color);
}

The real challenge came with the images I use in the headers of my pages. They’re JPEGs with white corners on one side and white gradients on the other.

header images

I could make them PNGs to get transparency, but the file size would shoot up—they’re photographic images (with a little bit of scan-line treatment) so JPEGs (or WEBPs) are the better format. Then I realised I could use CSS to recreate the two effects:

  1. For the cut-out triangle in the top corner, there’s clip-path.
  2. For the gradient, there’s …gradients!
background-image: linear-gradient(
  to right,
  transparent 50%,
  var(—background-color) 100%
);

Oh, and I noticed that when I applied the clip-path for the corners, it had no effect in Safari. It turns out that after half a decade of support, it still only exists with -webkit prefix. That’s just ridiculous. At this point we should be burning vendor prefixes with fire. I can’t believe that Apple still ships standardised CSS properties that only work with a prefix.

In order to apply the CSS clip-path and gradient, I needed to save out the images again, this time without the effects baked in. I found the original Photoshop file I used to export the images. But I don’t have a copy of Photoshop any more. I haven’t had a copy of Photoshop since Adobe switched to their Mafia model of pricing. A quick bit of searching turned up Photopea, which is pretty much an entire recreation of Photoshop in the browser. I was able to open my old PSD file and re-export my images.

LEGO clone trooper Brighton bandstand Scaffolding Tokyo Florence

Let’s just take a moment here to pause and reflect on the fact that we can now use CSS to create all sorts of effects that previously required a graphic design tool like Photoshop. I could probably do those raster scan lines with CSS if I were smart enough.

dark mode

This is what I demo’d at the end of Indie Web Camp Amsterdam, and I was pleased with the results. But fate had an extra bit of good timing in store for me.

The very next day at the View Source conference, Melanie Richards gave a fantastic talk called The Tailored Web: Effectively Honoring Visual Preferences (seriously, conference organisers, you want this talk on your line-up). It was packed with great insights and advice on impementing dark mode, like this little gem for adjusting images:

@media (prefers-color-scheme: dark) {
  img {
    filter: brightness(.8) contrast(1.2);
  }
}

Melanie also pointed out that you can indicate the presence of dark mode styles to browsers, although the mechanism is yet to shake out. You can do it in CSS:

:root {
  color-scheme: light dark;
}

But you can also do it in HTML:

That allows browsers to swap out replaced content; interface elements like form fields and dropdowns.

Oh, and one other addition I added after the fact was swapping out map imagery by using the picture element to point to darker map tiles:

<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.mapbox.com/styles/v1/mapbox/dark-v10/static...">
<img src="https://api.mapbox.com/styles/v1/mapbox/outdoors-v10/static..." alt="map">
</picture>

light map dark map

So now I’ve got a dark mode for my website. Admittedly, it’s for just one of the eight style sheets. I’ve decided that, while I’ll update my default styles at every opportunity, I’m going to preservethe other skins as they are, like the historical museum pieces they are.

If you’re on the latest version of iOS, go ahead and toggle the light and dark options in your system preferences to flip between this site’s colour schemes.

Have you published a response to this? :

Responses

Aaron Parecki

👏 so cool! Some day I will get around to doing this on my website. Great idea using CSS variables too.

Chris M.

“Dark mode” for this site has been one of those things on my todo list for a while. The in-development theme I’m working on has been built from the ground up to support it through the use of media queries and CSS custom properties — but I hadn’t actually implemented it. After a nudge from reading this post by Jeremy I’ve finally implemented something to try out:

For now I’m borrowing Jeremy’s colours, but I’m planning to tweak these as the design evolves.

# Posted by Chris M. on Monday, October 7th, 2019 at 5:17pm

qubyte.codes

That’s right! After more than a year of talking about adding a dark mode I finally did it. The wider support for prefers-color-scheme is what pushed me over the edge. I’m also a slave to fashion.

Initially I wanted to create a sort of dark-mode-battenberg theme, but after a while using dark mode site I came to realise that my favourites are the ones which use as much black as possible. It’s becoming more common to see OLED screens in the wild, and these use less energy for black pixels since OLED pixels are self illuminating. I settled on using black for the background, and borrowing my light mode background colour for text and borders.

The additions to the CSS aren’t too dramatic, but I learnt a few things along the way

@media (prefers-color-scheme: dark) { :root { --background-color-main: #000; --background-color-alt: #000; --standout-color-main: hsl( var(--base-background-hue), var(--base-background-sat), var(--base-background-lum) ); --standout-color-alt: hsl( calc(var(--base-background-hue) - 30), var(--base-background-sat), var(--base-background-lum) ); } img:not([src*=".svg"]) { filter: brightness(.8) contrast(1.2); } pre { background: #000; } code { filter: invert(); }
} body > header { z-index: 1; /* works around stacking context issue introduced by filters */
}

The media query applies the content only when the OS is in dark mode and the browser supports the media query. The :root swaps the background colors to standout colours, and makes the background black.

Non-SVG images have their brightness and contrast adjusted with a filter. I borrowed a dark mode filter from Melanie Richards as suggested by Jeremy Keith.

Code listings are more difficult. I plan to do a bit more on these later, but for now, since the regular code is on a white bacground, I give dark mode code containers a black background and apply an invert filter to the code.

Finally, the filters had an unintended side effect. Filters place the elements they apply to above positioned elements in the stacking context, which led to images and code listings scrolling above the nav bar (which is part of a sticky positioned header). To address this issue I resorted to using z-index. While the rule of thumb seems to be to avoid use of z-index, I believe it’s appropriate in this case.

# Saturday, October 12th, 2019 at 1:30am

john holt ripley

“…one other addition I added after the fact was swapping out map imagery by using the picture element to point to darker map tiles:” <picture> <source media=”prefers-color-scheme: dark” srcset=”“> adactio.com/journal/15941

David Shanske

I’m often adding features and functionality to my website. A location tweak, a new link, etc. But it’s been a while since I did anything major to the layout.

When I initially heard about dark mode support, I decided to wait until there was more support, then I just didn’t get around to it. It became a thing for applications to have dark modes, then dark modes that would activate based on a global system prference.

So, now my site, if you set your system to prefer dark mode, will show you a dark version of my site. Otherwise, it will show you a light version. I took a lesson from Jeremy Keith, who did this two years ago, and used something called CSS Custom Properties…another thing I haven’t used.

My WordPress theme is based on the original Twenty Sixteen WordPress.org theme. I ported back select improvements made from _s, the starter theme it was based on, as well as subsequent WordPress.org themes, such as Twenty Seventeen through the present.

There are a lot of other little tweaks I had to make in both this, and the plugins I develop for WordPress to support this. Style improvements, filters to add for additional functionality, etc.

It is still a work in progress, and I have other ideas and plans, but it is live. See if you can find all the other little tweaks.

Cidney Hamilton

It’s nearly a month into 2024. I’ve been quietly updating my website– there’s now a dark theme. I’ve added back the contact form, and fixed a broken endpoint. I am debating about what else to say.

I’m spending a lot of time working on an adventure game. Among many other things, it’s an immigrant story. The main character is a medieval peasant moving to a city, and her circumstances are very different than mine, but I’m noticing how much of the bureaucratic mess I’ve experienced has felt like a contrived puzzle. (I’ve wanted to post more about how I’ve found a good workflow for making puzzle dependency charts in Emacs, but my nesting partner overruled me.)

My stepfather was an immigrant from a village with 200 people in Croatia. He and my mother were only together for a few years, but they stayed married long enough for him to become a US citizen. I grew up around other immigrants from various parts of former Yugoslavia, learning bits and pieces of the language (mostly the bad words). As an American living in Germany with an Israeli partner, the process I’m going through is completely different. I’m living with someone I love and feel safe with– and in Berlin, most people speak perfect English. But I’m very much an outsider; I’m very self-conscious about my poor pronunciation, my lack of native speakers to practice with.

Being a solo indie developer is lonely to begin with. Moving to another country on top of that has been… difficult. I am trying to convey this in my game. Natalia Martinsson’s talk from the first Narrascope still haunts me sometimes.

More here when I have a sense of how to share it.

6 Likes

# Liked by Lucid00 on Monday, October 7th, 2019 at 5:37am

# Liked by Daniel Ehniss on Monday, October 7th, 2019 at 5:37am

# Liked by Michael Spellacy (Spell) on Monday, October 7th, 2019 at 5:37am

# Liked by Dominik Schwind on Monday, October 7th, 2019 at 6:43am

# Liked by Chris Burnell on Monday, October 7th, 2019 at 11:42am

# Liked by Bram de Haan on Monday, October 7th, 2019 at 8:46pm

Related posts

Who knows?

Had you heard of these bits of CSS? Me too/neither!

Schooltijd

Going back to school in Amsterdam.

Control

Trying to understand a different mindset to mine.

Talking about style

The transcript of a talk.

Declarative design

Defining the inputs instead of trying to control the outputs.

Related links

How would you build Wordle with just HTML and CSS? | Scott Jehl, Web Designer/Developer

This is a great thought exercise in progressive enhancement …that Scott then turns into a real exercise!

Tagged with

Retrofitting fluid typography | Clagnut by Richard Rutter

Here’s a taste of what Rich will be delivering at Patterns Day on Thursday—can’t wait!

Tagged with

What is Utility-First CSS?: HeydonWorks

Heydon does a very good job of explaining why throwing away the power of selectors makes no sense.

Utility-first detractors complain a lot about how verbose this is and, consequently, how ugly. And it is indeed. But you’d forgive it that if it actually solved a problem, which it doesn’t. It is unequivocally an inferior way of making things which are alike look alike, as you should. It is and can only be useful for reproducing inconsistent design, wherein all those repeated values would instead differ.

He’s also right on the nose in explaining why something as awful at Tailwind could get so popular:

But CSS isn’t new, it’s only good. And in this backwards, bullshit-optimized economy of garbage and nonsense, good isn’t bad enough.

Tagged with

Bill Oddie taught me how to make web sites - Hicks.design

I remember Jon telling me this lovely story when we first met in person. I love the idea that we had already met in a style sheet.

I also love the idea of hosting your own little internet archive—that Bill Oddie site still looks pretty great to me!

It’s a lot like an embarrassing family photo, but I’m owning it!

Tagged with

Offloading JavaScript With Custom Properties: HeydonWorks

With classes, we can send CSS static values but with custom properties we can send dynamic ones, which is a major shift in the way we can style state. This is something that has been true for some time—and is extremely well supported—but sometimes it takes solving a small real-world problem to make you appreciate the value of it.

I think we still haven’t come to fully appreciate the superpower of custom properties: dynamic values that are shared between CSS and JavaScript.

Tagged with

Previously on this day

6 years ago I wrote An nth-letter selector in CSS

The latest installment in the long tradition of calling for this pseudo-element.

18 years ago I wrote Melbourne calling

I gave a talk for the Web Standards Group in Melbourne.

22 years ago I wrote Pea throwing

Today, by happy chance, I found myself in Lewes.

23 years ago I wrote The Ig Nobel Prize Winners

The envelope please…

23 years ago I wrote The Turing Test

It’s Saturday. That means it’s Guardian reading day.

23 years ago I wrote What Apple Should Do to Increase Marketshare

Here’s an interesting little piece that could put one of those cartoon lightbulbs above Steve Jobs’ head.

23 years ago I wrote Biomorph breeder

Talking about The Blind Watchmaker reminded me of one of the things that’s so fascinating in the book.