Reading List
The most recent articles from a list of feeds I subscribe to.
Two levels of customising <selectmenu>
The proposed <selectmenu>
will have powerful styling options and full control over the different parts. In other words, there would be not one, but two levels of customisation. In this post, we'll look at what they are and when they are useful.
<selectmenu>
, you ask? It's a proposed new HTML element that is like <select>
, but fully customisable. Why? Because so many people are building their own selects, from scratch or with libraries, and that should be easier to do. With accessible defaults where possible and by leaving the parts that browsers are good at to the browser (like knowing where to position it, to place it on the top layer or to automatically dismiss it when appropriate (‘light dismiss’)).
For context, <selectmenu>
is a proposal by the Open UI Community Group. This group was founded to study common components and controls that people build on the web (in design systems, see the component name matrix), and suggest Web Platform features to make it easier to build such controls. Think new HTML elements, CSS features or APIs, as well as things that surface across HTML, CSS and JS.
<selectmenu>
doesn't ‘exist’ just yet, it is an idea that is being prototyped in Chromium, with other browser engines currently not opposed, but also not implementing it (as far as I'm aware). While you would't use it in projects yet, you can experiment with it in Chrome or Edge Canary with Experimental Web Platform Features flag on, and file issues if something doesn't work as expected or desired. For brevity, this post will refer to it as if it exists, to avoid saying ‘would’ in every sentence.
The anatomy of a <selectmenu>
The <selectmenu>
element, as currently prototyped, is a lot like the <select>
element that we've had for years. We can use it in a form, users can select an option out of many and their choice becomes part of the data that we process.
But where a <select>
comes with free built-in styles that we can't change, <selectmenu>
consists of some explicitly designed parts that can be changed (more on that a later). Often, the browser built-in styles of a <select>
are just fine, sometimes they are not.
In Sanity's Portable Text editor, the user can choose text types. Each option is styled like something of that type is commonly styled, and there is some shadows, rounded corners and arrows that a regular <select>
can't have.
The parts are:
select
, the root element, wrapping/containing all the partsbutton
, the toggle/button the user activates to open the optionslistbox
, the part that is toggled, it wraps the optionsoptgroup
, a part that we can optionally use to group related options togetheroption
, an actual choice, a value that a user can pickselected-value
, the element that displays the currently selected value
(from: Anatomy of the selectmenu)
When using regular <select>
s, it is possible to make the button part look however we want. In some browsers, we can also apply some specific styles to options, like background colours.
With <selectmenu>
, these parts are not just parts in the colloquial sense, they are <part>
s in the Web Component sense, meaning that they exist as elements in the shadow tree of the component. Consequently, all of these parts can be changed.
There are two levels to that: the first is to write CSS, where we can use all the power CSS has to offer, the second is to completely replace the DOM structure to what we want or need.
Option 1: CSS
In the <selectmenu>
prototype, the options are part of the (light) DOM like they are in a <select>
element, which means we can target them with CSS (and unlike with <select>
, our styles will actually get applied).
You could even give each option its own style! See Codepen (view in Canary with Experimental Web Platform Features flag on)
This is the most basic example of styling a selectmenu: you apply CSS to the options, like you would to any other HTML element.
The other parts of the selectmenu are targeted differently: we'll use a ::part()
selector referencing the respective part name. For example, to style the button:
selectmenu::part(button) {
// add any button styles
}
(Codepen of example with styled options)
To style other parts, you use that part's name instead, for instance ::part(listbox)
. Just last week, a resolution was passed to also expose the arrow as a part, to allow for easy styling or icon-replacing (part name to be decided).
In case you, like me, had not used the ::part
selector, it is part (no pun intended) of the CSS Shadow Parts module.
For background: Web Components can have a light and a shadow DOM. The light DOM is part of the DOM like anything else and can be styled, but the shadow DOM is internal to the element. This a bit like public and private functions. For styling purposes, the CSS Shadow Parts module states that the creator of a Web Component can choose to expose parts of their shadow DOM by names. Developers provide a list of parts (element names) to be exposed, and then the elements can be targeted through ::part(partname)
. With <selectmenu>
, we get a browser built in Web Component that has parts exposed (except for option
, which is in the light DOM).
This is huge… I mean, being able to throw CSS at the different parts of a selectmenu unlocks a lot of possibilities. Many of us will likely just use it to round some corners, add some shadows, maybe a little color or typography. And that's fine, we never could unless we built or loaded a library for custom selects.
I'm excited about being able to add a few finishing touches to selects, alone. But CSS has numerous powerful features, so that's only the surface. With all of CSS at our fingertips, custom selectmenu styles are probably going to be used in more interesting ways than we can think of now.
Option 2: full control by replacing parts
If we need more than ‘just’ styles, it is also possible to replace entire parts with whatever we like. For instance, we could write our own button entirely, and tell the browser to use that instead.
Replacing a part is done with slots. Let's say we created a button with markup of our choice:
<button>select option</button>
We can then tell the browser that, actually, we want this to be what the button part is, adding slot="button"
and behavior="button"
:
<button
slot="button"
behavior="button"
>select option</button>
(Codepen of example with replaced button part)
Now, the button will be what's used for the part called ‘button’ (as it is slotted into the right place with slot
) and get the browser's select button behavior and accessibility accomodations (set through behavior
). We could do the same for the other parts.
We could also do this in JavaScript, with Element.attachShadow()
, so that the markup we want to use can live in our JS, and we only need one HTML element at the point of usage rather than one plus a few more for slotting.
But wait… if we're replacing all the parts with our own things, isn't this just like rolling our own selects entirely? Well, yes, it is the advanced option, and usually CSS should suffice. But even if we replace some or many parts, the selectmenu would still get a bunch of things from the browser for free, like positioning, being on the top layer and light dismiss, features that would be hard to impossible to code in JavaScript.
Accessibility risk: avoid unexpected nests
There is an important accessibility risk to be aware of for folks planning to replace parts in their selectmenus. Browsers and assistive technologies have certain expectations about what HTML is and does. If we divert from those expectations, we risk causing real problems for end users, which could include controls becoming unusable.
One example is what would happen if we would nest an interactive element like a link or button inside an option
. Maybe one of our options has a tooltip? All of this should be avoided, at least for the time being (as far as I understand, this may change when secondary actions become a thing, see also w3c/aria#1440). The reason is: assistive technologies expect just text inside of options, not controls, so this would break badly and it could mean users wouldn't find those buttons or links, or wouldn't be able to interact with them. For this reason, it is strongly recommended to avoid interactive elements inside of options, i.e. do not add links or buttons in options.
Summing up
In this post, we looked at two ways to customise the parts of a selectmenu element. We can use CSS, which should cover a large amount of use cases and offer flexibility and room for creativity. Or we can replace entire parts with whatever we would like, the more advanced option that has accessibility risks if not thoroughly checked against specs and implementations. For a lot of the use cases that I see in design systems I have worked on, I feel CSS alone will cover all the needs. And it will do so very elegantly, often in literally a few lines of code.
Again, this is all in the future, the element is just being tested out. But, it may well be that this same pattern of having both CSS and parts-replacement for control customisation will come to other Open UI work, too. Generally, I am a fan of this layered approach. As a developer, I like the principle of being able to make things as simple or complex as I want.
If you want to see more demos, the Microsoft Edge team has built a series of cool <selectmenu> demos
(as they note, pushing the limits and some with accessibility issues). You can see and play with these, using Edge or Chrome Canary with Experimental Web Platform Features turned on. These demos include a lot of part replacements, too. If you have feedback, want to follow this work more closely or chime in, head to Open UI issues on GitHub or join us on the Open UI Discord community.
Originally posted as Two levels of customising
Two talks about documentation
In two recent conference talks, I learned about making better documentation. First, I heard Adrienne Tacke focus on the documentation's content, explaining how to make it more usable and effective. Then I saw Rich Harris go into how to build a documentation environment, showing a way to approach documentation of projects with a server-side components.
Both talks resonated with me. Good documentation is the backbone of every software project (and also crucial elsewhere, including for web standards like WCAG). For software companies there are countless business reasons to invest in documentation, and for anyone else, yes, improving documentation is still rewarding in many ways.
Types of documentation
To clarify what we're talking about here, Daniele Procida's Diátaxis framework for technical documentation, which I found out about in Rich's talk, identifies four types of documentation:
- tutorials, to teach people how to use the thing in the first place
- how-to guides, to teach an experienced user how to do specific things with your tool
- explanation, to give a high level overview of concepts in your tool
- reference documentation, to provide matter-of-fact information about functions, APIs etc, to be consulted not read; can be automatically generated
Adrienne's and Rich's talks focused mostly on the first two: tutorials and how-tos.
Be concise and valuable
In her talk, Documentation: the missing pieces, Adrienne went into some very common problems in documentation. She showed how documentation can be unclear and unhelpful, and then went on to show how to be concise and valuable instead. The whole talk is a must watch, but here are some highlights:
- be more explicit, by expanding acronyms (they can depend on context and not all acronyms are familiar to everyone) and by using the German naming convention (if you can be more explicit in a variable name, do it)
- be aware of the steps in what you are teaching and avoid skipping steps at all times; this is another moment of stepping (no pun intended) into the shoes of the reader of your documentation, they don't know your project as well as you do, so you want to be explicit and list all gotchas
- avoid surprises, always tell readers what they will need to make their way through the tutorial (prerequisities, dependencies, paid plans, time)
- focus on the thing you want folks to learn and help abstract the unrelated setup and configuration stuff away, by creating a starter app, Docker container or sandboxed environment, anything that isn't core to the thing you're currently teaching, but still needs to exist in order for the reader to get started
Interactive tutorials with a server-side component
In his recent JS Nation talk (start at 40s to avoid the crypto advertisement edited into the video 🥲), Rich explained tutorials are essential, as they actually show users what to do, but they are often overlooked. He showed Learn Knockout, a very early example of a tutorial he loved, and which the Svelte Tutorial was later inspired by, and others, like Lit tutorial, that follow the same model.
For the documentation of Svelte and SvelteKit, the frameworks he created, Rich used four principles:
- create a sandboxed environment, so that it is safe to make mistakes
- help muscle memory by disabling copy/paste (controversial!)
- provide a way to get straight to the solution if you're stuck (Rich calls this “panic button”)
- meet users where they are (in Svelte/SvelteKit's case: the browser)
Sandboxes for “full stack” projects need both a client and a server component. Some services provide a way to access a virtual server from a sandbox (like CodeSandbox Containers), but they have authentication barriers, to avoid abuse (like bitcoin mining) and can come with some latency, Rich explained. Then he found that on Stackblitz, the server is, rather impressively, ran inside the browser by compiling Node to WebAssembly and running that inside the browser's security sandbox (this is called WebContainers). And that wasn't enough: as he found a StackBlitz environment is made for coding, not learning, Rich teamed up with the StackBlitz team to have some kind of headless version of StackBlitz in order to create what's now in beta as learn.svelte.dev.
Summing up
While these talks were about different aspects of documentation, the speakers were equally determined to ensure that users of their documentation would actually learn the thing easily. It's not easy, neither when creating the content, nor in technically setting up the environment, but it can clearly make a difference.
Originally posted as Two talks about documentation on Hidde's blog.
Two talks about documentation
In two recent conference talks, I learned about making better documentation. First, I heard Adrienne Tacke focus on the documentation's content, explaining how to make it more usable and effective. Then I saw Rich Harris go into how to build a documentation environment, showing a way to approach documentation of projects with a server-side components.
Both talks resonated with me. Good documentation is the backbone of every software project (and also crucial elsewhere, including for web standards like WCAG). For software companies there are countless business reasons to invest in documentation, and for anyone else, yes, improving documentation is still rewarding in many ways.
Types of documentation
To clarify what we're talking about here, Daniele Procida's Diátaxis framework for technical documentation, which I found out about in Rich's talk, identifies four types of documentation:
- tutorials, to teach people how to use the thing in the first place
- how-to guidess, to teach an experienced user how to do specific things with your tool
- explanation, to give a high level overview of concepts in your tool
- reference documentation, to provide matter-of-fact information about functions, APIs etc, to be consulted not read; can be automatically generated
Adrienne's and Rich's talks focused mostly on the first two: tutorials and how-tos.
Be concise and valuable
In her talk, Documentation: the missing pieces, Adrienne went into some very common problems in documentation. She showed how documentation can be unclear and unhelpful, and then went on to show how to be concise and valuable instead. The whole talk is a must watch, but here are some highlights:
- be more explicit, by expanding acronyms (they can depend on context and not all acronyms are familiar to everyone) and by using the German naming convention (if you can be more explicit in a variable name, do it)
- be aware of the steps in what you are teaching and avoid skipping steps at all times; this is another moment of stepping (no pun intended) into the shoes of the reader of your documentation, they don't know your project as well as you do, so you want to be explicit and list all gotchas
- avoid surprises, always tell readers what they will need to make their way through the tutorial (prerequisities, dependencies, paid plans, time)
- focus on the thing you want folks to learn and help abstract the unrelated setup and configuration stuff away, by creating a starter app, Docker container or sandboxed environment, anything that isn't core to the thing you're currently teaching, but still needs to exist in order for the reader to get started
Interactive tutorials with a server-side component
In his recent JS Nation talk (start at 40s to avoid the crypto advertisement edited into the video 🥲), Rich explained tutorials are essential, as they actually show users what to do, but they are often overlooked. He showed Learn Knockout, a very early example of a tutorial he loved, and which the Svelte Tutorial was later inspired by, and others, like Lit tutorial, that follow the same model.
For the documentation of Svelte and SvelteKit, the frameworks he created, Rich used four principles:
- create a sandboxed environment, so that it is safe to make mistakes
- help muscle memory by disabling copy/paste (controversial!)
- provide a way to get straight to the solution if you're stuck (Rich calls this “panic button”)
- meet users where they are (in Svelte/SvelteKit's case: the browser)
Sandboxes for “full stack” projects need both a client and a server component. Some services provide a way to access a virtual server from a sandbox (like CodeSandbox Containers), but they have authentication barriers, to avoid abuse (like bitcoin mining) and can come with some latency, Rich explained. Then he found that on Stackblitz, the server is, rather impressively, ran inside the browser by compiling Node to WebAssembly and running that inside the browser's security sandbox (this is called WebContainers). And that wasn't enough: as he found a StackBlitz environment is made for coding, not learning, Rich teamed up with the StackBlitz team to have some kind of headless version of StackBlitz in order to create what's now in beta as learn.svelte.dev.
Summing up
While these talks were about different aspects of documentation, the speakers were equally determined to ensure that users of their documentation would actually learn the thing easily. It's not easy, neither when creating the content, nor in technically setting up the environment, but it can clearly make a difference.
Originally posted as Two talks about documentation on Hidde's blog.
“That's not accessible!” and other statements about accessibility
“We're 100% accessible”, some digital products claim. “That solution is inaccessible”, an accessibility specialist might say. These sorts of statements almost suggest that web accessiblity is a binary thing. Is it though?
In this post, I'll talk about why it's most helpful to see a website's accessibility as a continuum (or, you know, multiple continua). Even then, in some contexts, it makes sense to pretend it is binary.
If you find this interesting, but want actionable advice, see also Adrian Roselli's post Things to Do Before Asking “Is This Accessible?”
It is a spectrum
The accessibility of a website is a spectrum, in terms of different disabilities that exist, in terms of timing and in terms of objective claims.
The goal of web accessibility is that people with disabilities can use the web. In other words, it is about people and maximising the portion of people who can use our UI well. There are people who can't move their arms, who use their screen zoomed in, whose vision is blurry, who control their computer with their voice, and so forth. Accessibility is about people with a wide range of disabilities, that sometimes overlap, too. Our UI could be accessible to most or all of these people, or to some, or to none. Most products are accessible to at least some people with disabilities. Many have specific barriers for users from specific groups. Some do really well at continuously identifying barriers and removing them. Some don't.
Second, it is about timing: today all podcasts on our site may be transcribed, tomorrow we may upload an episode without a transcript. Or launch a new campaign that was done by this agency that didn't take all accessibility requirements into account. It happens. Accessibility can't be solved once and then shipped, it's a continuous process of tracking potential barriers and removing them. “That site is accessible” is a statement that changes over time on websites where content changes.
Third, accessibility conformance testing is subjective to some extent. This isn't a bug in accessibility standards, it's more like a most reasonable choice… If we tried hard, we could invent success criteria that can be evaluated with 100% certainty, but then we would need many of them, they might go out of date fast and they might end up a lot less technology agnostic. The subjectivity serves a purpose, but it's there, and again, a reason that a claim like “this is accessible” is hard to make.
So, basically, there is a degree of subjectivity in determining whether something is accessible, because it matters to which user(s), when it is checked and what is checked. For that reason, a statement like “this is accessible” or “this is not accessible” is best taken with a pinch of salt.
Why pretend it's not
A claim of “great accessibility” is subjective, a bit like “great user experience” and “great design”. Especially when you view “meeting WCAG” as a minimum and aim much higher by doing regular user testing and following best practices beyond WCAG (see my other post about using a superset of WCAG). But sometimes it makes sense to try and make formal and objective-like claims about the accessibility of a website or set of websites. To publish reports that say things like “60% of webshops in Germany are inaccessible” or “Only 10% of online banking is accessible”. Those are usually based on automated tests and/or accessibility conformance reports that refer to standards like WCAG.
One example of when it makes sense to pretend accessibility is objective, is the effectiveness of policy. National governments and organisations like the European Commission want to have a more accessible web, they have this as a policy goal. For that to be more than dreams or empty statements, they need to make it practical and tangible. Their method of measuring success is, roughly speaking, to gather accessibility statements and conformance reports. On the one hand, this reduces the experiences of people with disabilities to checking boxes in a standard, on the other hand, this provides insights at scale, while maintaining a reasonably good representation of individual experiences.
WCAG is used as a way to make statements about websites. Combined with a method like WCAG-EM, a detailed process for evaluating conformance published by the W3C, governments can get some level of certainty.
As an example, The Dutch government has a register of over 3500 accessibility conformance statements. They are each about a specific website, to which a rating between A (“fully meets WCAG”) and D (“does not meet WCAG”) is assigned. Rating E means “statement is missing”. Other efforts include AllAble's accessibility statements research, looking at accessibility statements from public sector bodies in the United Kingdom.
Of course, this approach is not perfect. Individual organisations might be using an auditing agency that is biased or not very good at evaluating WCAG. Some might self-evaluate (generally not a good idea). Or a website could have serious accessibility issues that happen to not be captured by WCAG (it happens). But even with some of those caveats in mind, even if the data is not 100% objective (is data ever?), collecting information from a large set of websites is the best a government can do. Regular formal WCAG/ATAG audits is a great thing for companies and organisations to do too, though ideally that strategy is supplemented by regular user tests and review of best practices.
Wrapping up
In summary: yes, measuring web accessibility is somewhat subjective and claims like “X is accessible” or “X is inaccessible” are tricky. If someone makes such statements, grab a pinch of salt! But, having said that, it can be helpful to talk about accessibility as “meets the criteria in this standard” and “does not meet the criteria in this standard”. Governments do this to measure the success of their policies and companies can do it to have some measurement of their own success. That's mostly useful, even though user tests and best practices based on them are even more meaningful.
Originally posted as “That's not accessible!” and other statements about accessibility on Hidde's blog.
How I built a dark mode toggle
This site has had dark and light modes for a while, but it never offered any choice. If your system preference was “dark”, that's what you would get (unless you used fancy browser flags). Yesterday, I implemented a dark mode toggle, so that you can override that at your leisure. In this post I will talk about some of the choices I made along the way. Basically, it's a long winded way of saying why it took me so long.
This is fun to fiddle around with, but really, it seems to me that all websites have this requirement, so why isn't this built into browsers? In other words, I agree with Bramus Van Damme, dark mode toggles should be a browser feature. His post has more reasons: however you support light and dark modes, you will likely end up with code duplication somewhere, and toggles that live in developer land require JavaScript.
The choices
HTML
There are a lot of HTML elements that you can use to give users a choice between one out of many options. Radio buttons and <select>
s come to mind. A checbox could be an option too… if there is only one option, checkboxes too allow just the one option.
I started out with radio buttons as I thought that was clever, and even ended up building the control all the way from coding a form a fieldset
, legend
and options
. I went ahead to visually hide the control itself and then use the :checked
pseudo class to only display the option that wasn't picked. In other words, when dark mode was on, the light mode option was visible and vice versa.
It's a whole form!
It must have been late, because only when I started testing this with keyboard, a sanity check I do with all controls, I realised it didn't feel right. With a keyboard, you would need arrow keys to pick the other option and that feels weird, especially for a control that is styled to not look like radio buttons. I guess the lesson is: never style elements too far from their original purpose.
I ended up going for just the one button
element. To switch styles, I would need JavaScript either way, so I would not benefit from having a checkbox in this case. In Underengineered Toggles Too, Adrian Roselli explains that the button makes sense if the control only works with JavaScript (check; we need to toggle a class or attribute to apply the CSS), if it only has true or false states, not mixed (check; we allow either light or dark) and if flipping the control immediately performs an action (check; the color scheme changes straightaway). The great thing about a button
element, just for the record, is that it comes with keyboard support built-in. No extra cost.
It's a button!
One button, like mine, does mean that users can't explicitly choose that they want my site to follow their browser or OS setting. In his post Your dark mode toggle is broken, Kilian Valkhof makes the case for including a “system” setting that would solve this very problem.
CSS
With CSS, I made the button look like not a button. I visually hid the text inside of it. I also added an SVG icon that I applied pointer-events: none
to, so that clicks on the SVG would be registered as clicks on the button.
JavaScript
In script, I wanted a couple of cases to be covered. Let's walk through the various parts of the implementation.
First, I stored the button and the query to find out if a dark color scheme is preffered, and created an empty variable for the current theme:
var button = document.querySelector('.theme-selector');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
var currentTheme;
I also added a function to set a theme, which adds an attribute to the html
element and stores the preference in localStorage
:
function setTheme(currentTheme) {
var pressed = currentTheme === 'dark' ? 'true' : 'false';
document.documentElement.setAttribute('data-theme-preference', currentTheme);
localStorage.setItem('theme-preference', currentTheme);
}
Then I figured out what the theme should be. First I checked if there is something in localStorage, if not if there is a preference for dark mode, and if not, I set it to default to light mode:
if (localStorage.getItem('theme-preference')) {
currentTheme = localStorage.getItem('theme-preference');
} else if (prefersDark.matches) {
currentTheme = 'dark';
} else {
// default
currentTheme = 'light';
}
We also want ways to change the theme. First off, when the user clicks the button:
button.addEventListener('click', function(event) {
currentTheme = document.documentElement.getAttribute('data-theme-preference') === "dark" ? "light" : "dark";
setTheme(currentTheme);
});
Secondly, when the user changes their theme preference:
prefersDark.addEventListener('change', function(event) {
currentTheme = event.matches ? 'dark' : 'light';
setTheme(currentTheme);
});
(This listens to changes in the browser's MediaQueryList, which could change when the setting is changed in the browser or the OS).
In Firefox, you can pick light or dark mode, if you don't want it to just follow the system or your choice of Firefox theme. There is no per website setting at the time of writing.
ARIA
In my book, it's ideal to only use ARIA when it is for something that:
- doesn't exist in HTML
- brings a known benefit to end users
In this case, we're using a standard button
element, which exists in HTML. But there is one detail about it that doesn't exist in HTML, which is that this is the type of button that toggles. We can add this by setting the aria-pressed
with a "true"
or "false"
value.
This brings a known benefit to end users, specifically to users of screenreaders, who will now hear this is a toggle button. For toggle buttons, it is important that the content doesn't change. As the button text, I have used “Toggle dark mode”.
If we would update the label of the button from “Turn on light” to “Turn on dark”, we kind of have a toggle (as MDN suggests), but that way, it wouldn't be recognised by the browser and set in the accessibility tree to be a toggle, and, because of that, not announced as a toggle by screenreaders.
Compromises
Code duplication
In his post, Bramus explains that when you want to build a dark mode toggle, you will end up with duplicated CSS. That is, if you both want to see color changes support the prefers-color-mode
media query, and the class or attribute that you're toggling with your toggle. I went for not supporting that media query directly in CSS. Instead, I rely on my script to set the right data attribute. This means that without JavaScript, users will only see the light mode, even if their system or browser setting is to use dark mode.
Honouring the system preference
As mentioned above, my toggle doesn't have a “do whatever the system does” setting. However, when you change the system setting, it will notice and use that system setting. That is, until you change your preference again, either via the toggle or the system setting.
Many themes
The toggle I ended up with can only swap between two states. Initially I had wanted it to work for any number of states, I mean, why not have lots of different themes between light and dark? It's tricky, because how do those two settings, that the media query currently supports checks for, map to my many themes? This may be something for future iterations, probably with some way to map two specific themes to be the browser's “light” and ”dark”.
Summing up
To build a dark mode toggle is an exercise full of compromises. There are many ways that, proverbially, lead to Rome, as you can read above. I was happy enough with the current result to ship it for now, but am more than open to feedback.
Originally posted as How I built a dark mode toggle on Hidde's blog.