Accessible accordions with the details HTML element

My second last blog post on the HTML topic in this advent calendar series wants to introduce an element some of you might have used, without even knowing it. But the functionality it offers you, many might have used in a website.

Toggle and accordion elements

When we only have one element that would reveal additional text on the click of a button or text, we often speak about “toggles”. If we have multiple, and only one is open at the same time, we usually refer to them as “accordions”.

In the past, we would have used a JavaScript library for such a functionality, some usual container elements and a button or link. But now, we have an element for this. If we want to have a single toggle, this would be our markup:

<details>
	<summary>Learn more about the details element</summary>
	This is text that would only be shown if the element opened.
	You can also use any other HTML elements here, you don't have
	use plain text only.
</details>

In this example, we don’t wrap our “hidden text” in any element. Most likely you would use a <p> or <div>, but really any content that comes after the <summary> will be hidden, as the element is “closed” by default. And this is how it would look like:

A triangle pointing to the right with the text "Learn more about the details element" next to it.

The right pointing arrow is there by default. The shape will look slightly different in different browser. When you click on the arrow or the text, the element will open, and the arrow now points down, indicating the element is open:

A triangle pointing down, with text from the summary element right of it and the rest of the content inside the details element below it.

As mentioned earlier, the element is closed by default, you can open it, by adding on empty open attribute to the element:

<details open>
<!--... -->
</details>

Now, clicking on the <summary> element will close the <details> element. I cannot really think of a use-case where one would use this for a single toggle. Why hide something on a click, that was already visible?

Creating an accordion using multiple “grouped” details elements

The <details> element has only to specific attributes. We’ve already learned about open, the second one is name. If you give multiple <details> elements this attribute with the same value, they will be grouped into an according, where always only one is open at the same time:

<h2>Group one</h2>
<details name="group-one">
	<summary>First element of group one</summary>
	<p>The content of the first element of group one.</p>
</details>
<details name="group-one">
	<summary>Second element of group one</summary>
	<p>The content of the second element of group one.</p>
</details>
<h2>Group two</h2>
<details name="group-two">
	<summary>First element of group two</summary>
	<p>The content of the first element of group two.</p>
</details>
<blockquote>
	<q>Some random quote in between</q>
	<p>Some famous person</p>
</blockquote>
<details name="group-two" open>
	<summary>Second element of group two</summary>
	<ul>
		<li>Item 1</li>
		<li>Item 2</li>
	</ul>
</details>

I’ve chosen a little more complex example here to show multiple things at once. First of all, we have two accordions using the name attributes values of group-one and group-two. This is how that HTML markup would render:

Two details accordion groups with a quote in between elements of the second group, which has an unordered list as the content of the second details element.

In group one, no element has the open attribute, so both of them are closed by default, whereas in group two, the second element has the open attribute and shows its content. In this case, the content is an unordered list, showing, that the element can contain any HTML and not only static text.

In between the first and second element of group two, we also have a blockquote. This should illustrate that you don’t need to have all <details> elements next to each other. You can have content between them.

If you open one of the other elements in the same group, the currently open element closes. If you close the open element as well, all elements are closed now. There is no (native) way to disallow closing all elements.

If you want to allow all elements to be open, just don’t use the name attribute on them. You could also use some JavaScript for an “open all” and “close all” button, that would toggle them. There is even a toggle event, you can register an event listener to. You can find out more about that in the documentation.

Styling the element

The documentation also has some basic styling examples. But is you want to see some more creative ways to style the element, take a look at the CSS-Tricks blog post covering the element.

Conclusion

When you really need to have a toggle/accordion element on your website, I can highly recommend using the native HTML elements. I personally don’t like toggle or accordions. I rather scroll through a longer page, than having to click on all these “toggles”, which also move the page up/down so you need to target the next element to click. That really frustrates me.

If you use WordPress, simply use the “Details” block, which will give you that exact element. You will have a line for the summary, and then a “Paragraph” block for the content, but you can use any block here, even multiple ones or blocks like “Group”, “Columns”, etc. to get really creative.

The prefers-color-scheme media query

Today I want to talk about something, many of you probably like a lot: dark modes! Or more specifically, how to switch the color scheme to dark mode, if visitors prefer it. I do use it for my IDE and for some online services, but for many, I’m still on the light side.

Detecting the dark mode and changing styles

Turning on dark mode is something a user can do in their operating system, in the browser or by clicking a button. Whichever way they choose, your CSS code should use the media query like this:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: #eee;
	border: 3px solid #333;
	border-radius: 10px;
	color: #333;
}
@media (prefers-color-scheme: dark) {
	button {
		background: #333;
		border-color: #333;
		color: #eee;
	}
}

This CSS will expect that you are using a light color scheme by default. If a visitor has set the preference to dark, they will get the alternative colors for the button. This is how a button would look like in light or dark colors:

A button with the text "light colors" on the left, with a light gray brackground-colors and a dark gray text and border-color. A second button on the right with the text "dark colors" and the text in light gray and the background and border in dark gray for a "filled" layout.

By using the same color for background and border, we made the dark version “filled”. You don’t always have to invert/swap all colors. That what some generic plugins do, though. As explained in “Simulate preferences in the browser“, you can easily test those dark mode styles without the need to change the settings in your operating system. Chrome also has an “Automatic dark mode” option, but this would just try to “guess” which colors to use, and we would have a slightly lighter gray for the background and a light gray border:

Two buttons, one labeled "light colors" and one "dark colors". Both have the same colors with a medium gray border, a dark gray background, and a light gray text color.

As you can see, even our defined colors for the dark mode of a button would not apply anymore. Instead, Chrome is displaying any button in this “automatic dark mode”. But I’m also not sure if many people really use this.

Using the light dark mode as the default

Many people design a website with a light mode as the default and then use the media query as shown above to change colors for a dark mode. But if you happen to design a page with a dark mode as the default, that would be a lot of CSS in this media query. What you can do instead: Change colors when a visitor explicitly prefers a light mode. This is how you do it:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: #333;
	border: 3px solid #333;
	border-radius: 10px;
	color: #eee;
}
@media (prefers-color-scheme: light) {
	button {
		background: #eee;
		border-color: #eee;
		color: #333;
	}
}

This code snippet is basically the first one again, with (almost) all colors switched, and with the major different that we now check for light in the prefers-color-scheme media query. The button now looks like this in the default dark mode and in light mode:

Two buttons, the left one with the text "dark colors", and a dark gray background and light gray. The right one with the text "light colors" and a light gray background and dark gray text.

So, depending on your own preference, you can decide which color scheme to use as your website’s default. Adding the other color scheme can make a huge difference in terms of readability and accessibility. Especially when you have a dark mode as a default, please consider offering a light scheme as well.

Conclusion

Adding a dark mode to your CSS was never easier as with this media query. If you want to give users an option to change the mode, a “toggle button” or some user setting can make sense. And for those, another property will come in handy. But this will be the topic for our last blog post in the 2024 advent calendar on CSS.

Tagged Template Literals in JavaScript

In the blog post 8 days ago with the title “Dynamic parameters in JavaScript“, I have shown you how functions with a variable number of parameters work. Today, I want to talk about the “tagged templates” feature in JavaScript, as a form of a function with a variable number of parameters.

Template Literals

Before we head into the topic, let’s talk about template literals first. In old versions of JavaScript, you would have done the following, you place a variable in between some static text:

var name = "Bernhard";
var country = "Finland";
var greet = "Hi, my name is " + name + " and I'm currently in " + country + ".";
console.log(greet);
//Hi, my name is Bernhard and I'm currently in Finland.

With modern JavaScript, you can use a “template literals” to place some variable (or even code) inside your strings that get interpreted:

const name = "Bernhard";
const country = "Finland";
const greet = `Hi, my name is ${name} and I'm currently in ${country}.`;
console.log(greet);
//Hi, my name is Bernhard and I'm currently in Finland.

Using the backticks instead of other quotes, we turn our simple string into a template literal. Isn’t that much easier to read? Ah, and yes. I am currently in Finland, and not in Berlin. On the Åland Islands, to be precise.

Tagged Template Literals

After this short introduction, let’s come to the “tagged” template literals. When you first see them, they might look a bit odd. This is how one example might look like:

const name = "Bernhard";
const country = "Finland";
const greet = makeItBold`Hi, my name is ${name} and I'm currently in ${country}.`;

function makeItBold(strings, ...values) {
	// strings => ['Hello, my name is ', " and I'm currently in ", '.']
	// values => ['Bernhard', 'Finland']

	let result = "";

	// Loop through the strings and values
	for (let i = 0; i < values.length; i++) {
		result += strings[i] + `<b>${values[i]}</b>`;
	}

	// Append the last string (after the last variable)
	result += strings[values.length];

	return result;
}

console.log(greet);
// Hi, my name is <b>Bernhard</b> and I'm currently in <b>Finland</b>.

We define a function (makeItBold) and then we just write the function name before the template literal. In the function, we get the “static strings array” as the first strings parameter and then a dynamic number of parameters for each “variable” (or more precisely the expressions) in the template literal as the second values parameter. We could also write this function like this:

function makeItBold(strings, name, country) {
	// ...
}

But that would not really be flexible, if we swap the variables around or have more/fewer variables in another string.

One thing we can do, to make the function a little shorter, is using some modern JavaScript code handling arrays (written in multiple lines for better readability):

function makeItBold(strings, ...values) {
	return strings.reduce(
		(result, str, i) => result + str + (values[i] ? `<b>${values[i]}</b>` : ""),
		""
	);
}

Those of you familiar with modern JavaScript can probably easily read this, I still have to look twice and read it carefully.

I had hard times finding a good “real life example”, but after coming up with the example above, I found one that might be really useful, and that is not using scalar values, but objects. What do you think about this one?

function schema(strings, ...values) {
	return strings.reduce((result, str, i) => {
		const value = values[i];
		if (value) {
			// Check for specific schema.org types and add relevant markup
			if (value.type === "Person") {
				return (
					result +
					str +
					`<span itemscope itemtype="https://schema.org/Person">
						<span itemprop="name">${value.name}</span>
					</span>`
				);
			} else if (value.type === "Place") {
				return (
					result +
					str +
					`<span itemscope itemtype="https://schema.org/Place">
						<span itemprop="name">${value.name}</span>
					</span>`
				);
			}
		}
		return result + str;
	}, "");
}

// Usage
const name = { type: "Person", name: "Bernhard" };
const country = { type: "Place", name: "Finland" };

const greet = schema`Hello, my name is ${name} and I'm currently in ${country}.`;

console.log(greet);
// Hello, my name is <span itemscope itemtype="https://schema.org/Person"><span itemprop="name">Bernhard</span></span> and I'm currently in <span itemscope itemtype="https://schema.org/Place"><span itemprop="name">Finland</span></span>.

Conclusion

As mentioned, I wasn’t easy finding a good example. One could argue, that you could also just a template literal in many cases and some inline HTML markup. But still this feature is available to you, and you should know how to read code using it.

If you have an example from one of your projects using a tagged template literal, please leave a comment.

Pausing JavaScript with the debugger keyword

This blog post is not going to cover the basic topic on how to debug JavaScript code in your browser. We could probably have a full advent calendar covering all the aspects. But I want to introduce a neat little keyword for your debugging toolbox.

Setting a breakpoint in the browser

When you want to debug some JavaScript code, you probably set some breakpoint in your code at some point. If you have a small JavaScript file that is nicely formatted, it might look like this in Chrome:

Chrome dev tools with a manual breakpoint set the browser stopped on that line.

You would open the dev tools, navigate to the JavaScript file in the “Sources” tab, set a break point and reload the page. Chrome will then stop at the line and you can debug your code.

The issue with compiled JavaScript code

Those of you developing code with React, Vue, etc. and using webpack or similar tools to compile your code, will not have such nicely formatted code that is also easy to read. Let’s throw in our code from above into the compiling workflow from the @wordpress/scripts package and compile it:

npm run build

Now we get a compiled “production” file, and our nicely formatted code is now only a single line in this compiled file:

Chrome dev tools showing a minified JavaScript file.

How do we set a breakpoint now? If you want to set a breakpoint in a “single line JavaScript” file, there is a little feature you can use. Just click on the “{}” icon (“Pretty print”) in the bottom left of the middle column, and you will get something like this, where you can set a breakpoint in the “virtual line”:

Chrome dev tools "pretty printing" the one-line JavaScript file and a manual break point set on a prettified line.

That kind of works. But if you want to debug the console.log() calls at the end of the file, they are still in one line and additionally in a ternary operator.

Using the debugger keyword for a “forced breakpoint”

After this short introduction, we now come to the keyword, I wanted to talk about today. While you develop your code, you can add the debugger keyword to any line of your code, which will force the browser to pause on that line. Let’s use it in the compiled version to debug the “if” portion of the ternary operator. We use a different command now to get the “development version” that would also start a watcher and compile the code as soon as we change something:

npm run start

In our JavaScript code, we also add the debugger keyword like this to the if/else at the end:

if (found) {
    debugger;
    console.log(`there is a 4 in array ${found}'`);
} else {
    console.log("There is no 4 in the array");
}

If we refresh the browser now, it will stop at this keyword in line 17. We don’t have to add a breakpoint here:

Chrome dev tool showing a JavaScript file stopped at the "debugger" keyword.

In the top right you can see “Debugger paused” and the script stopped, just as with a breakpoint set by us manually in this view. We can now continue with our debugging.

Using the debugger keyword in minified files

First of all: you probably shouldn’t do that. If you use the “build” script, you are usually creating files for production, and these should not have a debugger keyword in them. That’s why the minification webpack plugin is removing this keyword. If, for whatever reason, you do want to have the debugger keyword in the compiled files, you need to write a custom webpack.config.js webpack configuration file:

const defaultConfig = require('@wordpress/scripts/config/webpack.config');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
    ...defaultConfig,
    optimization: {
        ...defaultConfig.optimization,
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    compress: {
                        drop_debugger: false,
                    },
                },
            }),
        ],
    },
};

If we run npm run build now and refresh the browser, we will see the keyword and the browser stops there:

Chrome dev tool showing a JavaScript file stopped at the "debugger" keyword in a "prettified" minified code.

This would also automatically trigger the “Pretty print” icon in the bottom left and show you the code like above.

Conclusion

It may sometimes be hard to set a breakpoint manually, especially if your JavaScript files get compiled, combined and even minified, and you have no idea where a line you just wrote end up. You can also just set the debugger keyword temporarily, and once the script stopped, set a regular breakpoint, to remove the keyword right away, so you also don’t accidentally publish code with the keyword inside.

The HTML meter element and how it works

This blog post covers an element, that is very similar to the progress element I have covered in a previous blog post. It is used to display some kind of “level” like of a battery or “score” like in on online test. Those visualizations are usually defined by a min and max value.

A basic example of the meter element

Let’s start with a very basic example that has all the attributes it needs to function. This is what we have to implement:

<label for="score">Score:</label>
<meter id="score" value="0.5">50%</meter>

OK, the associated <label> is optional, but as you probably know me by now, I always try to give examples with good accessibility in mind.

The element has a min attribute, which defaults to 0 if not defined, whereas max is set to 1. So, if we don’t define them, we would use a value range between 0 and 1 and therefore our value of 0.5 would represent 50% on the meter. The result would look like this:

A label "Score" followed by the meter element that is half "filled" with a green background.

Similar to the <progress> element, we don’t see the “text node” that says “50%”. This is only shown if the element is not supported by the browser.

Using the additional attributes to make it more useful

With the <progress> element, we only had a max and value attribute. The <meter> element has a lot more, and they make the big difference between the two elements. Here is an example with all the attributes you can use:

<meter min="-20" max="100" low="10" high="40" optimum="25" value="10">10°C</meter>

We can explicitly set the min and max attributes. The min attributes is also not fixed to 0, it can have any value, even negative ones, like you could have a meter representing a temperature below zero. This is how that would look like for some different temperature values:

Seven meter elements ranging from -20°C to 100°C with the temperature 10°C, 25°C and 40°C with a green background, the other ones with a yellow background.

By setting an optimum value between the low and high attribute values (it could have any value between 10 and 40), this range will be indicated with a green background. Any range below min or above high will be shown in yellow.

A meter with three different colors

Let’s take another example with a score on a test. We would define the test as “passed”, when you get 50% or more, and it would be “good” if you have 80% or more. These elements would represent different scores:

<meter min="0" max="100" low="50" high="80" optimum="100" value="0">    0%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="10">  10%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="20">  20%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="30">  30%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="40">  40%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="50">  50%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="60">  60%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="70">  70%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="80">  80%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="90">  90%</meter>
<meter min="0" max="100" low="50" high="80" optimum="100" value="100">100%</meter>

You can see, that now our optimum is between high and max. When the value of optimum is larger than the value of max (we just set it to 100), we get three different background colors:

Eleven meter elements ranging from 0% to 100%. The background at 0% is not visible and values 10% to 40% have a green red background. Values 50% to 70% have a yellow and values 80% to 100% have a green background.

This represents our score definition perfectly, right? If you are unclear how to set attributes values, check the documentation and test some different values in some different browsers.

Styling the element

There are only few CSS properties you can use in WebKit browsers and for Safari you even need some JavaScript hack to make them work. You will find them linked in the “See more” section of the documentation.

In Chrome, you could combine them with an attribute selector to change the background color of the exact “optimum value” for the temperature example like this:

meter[value="25"]::-webkit-meter-optimum-value {
	background-color: blue;
}

But in Firefox that would have no effect. As soon as you change the background of the element, it will look very different (like if you use “appearance: none” on it). I’d suggest you just experiment with the element a bit.

Conclusion

The <meter> element is probably one more lesser known elements, but it can be a nice addition to your UI to visualize some numbers. For the <progress> element, we had the ProgressBar component for WordPress, but for <meter>, we don’t have such a component, yet.

Better readability with reduce transparency

Today, I want to write about a CSS feature, that is still experimental. But it can make a difference for users who have difficulties reading text on transparent elements.

On many websites, you will find some elements that place text on an image. This is very often a huge issue for the readability of that text.

Text on an image

Let’s use a little example. WordPress offers the “Cover” block. When you use it, the image becomes a background image. The block has a control for the “overlay opacity”. If it is set to a value 21 or lower, the text is black by default. If you set the value to 22 or higher, the text color switches to white.

Cliffs in the archipelago of Åland with stones coverd in lichen in the front and the sea (blurred) in the back.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

As you can see in the example above, the text is hard to read in the upper part, even if you usually don’t have issues reading text on images. For a dyslexic person like me, it can get extremely frustrating.

Using a preference to reduce the transparency

The new CSS property I want to write about is prefers-reduced-transparency and just as for the reduced motion preference, it would use the reduce value. Here is an example for the Cover block:

@media (prefers-reduced-transparency: reduce) {
    .has-background-dim {
        opacity: .7;
    }
}

In a previous blog post, I’ve explained how you can simulate such preferences. If you would simulate the render setting, the Cover block would look like this after the “opacity” change:

Cliffs in the archipelago of Åland with stones coverd in lichen in the front and the sea (blurred) in the back.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Isn’t that a lot better to read? Changing the opacity when someone prefers a reduced transparency is quite obvious. But as with any other media query, you can have any CSS properties inside of it. So instead of changing the opacity, you can also change the element.

How about moving the text below the image without any transparency and with black text on white? Or you align the text next to the image. Be creative and make your content readable for people with different preferences and needs.

Conclusion

There are so many CSS properties that are unknown or not yet widely available. This specific one can be used in Chromium browsers already, and Firefox users can set a flag to use it. If you are using Windows 10/11, macOS or iOS, you can also already active this setting, as explained in the documentation. Given that OS support, I would be surprised if this new media feature will be experimental for much longer.

Natural sorting in JavaScript

If you only ever deal with English strings in your code, you can stop reading now. Unless you are curious and want to learn something new, then please stay. 😉

In languages like German, we have characters that don’t exist in the English alphabet. Some look similar to other characters, but some are extra. And in German we only have 4 of them, other languages have many more.

How strings are usually sorted

When you have an array of strings, there are different ways in how they are sorted. Let’s look at some examples:

console.log(['c', 'b', 'a', 'B', 'A', 'C'].sort())
// (6) ['A', 'B', 'C', 'a', 'b', 'c']

If you ever wrote some code and sorted it with a simple sorting function, you might be aware that many of them use the order of the characters in the ASCII table. That’s why we see the capital letters first, followed by the lower-case letters.

That might be just what we expect and want for English. But let’s throw in some German Umlaut into the array:

console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort())
// (6) ['A', 'B', 'a', 'b', 'Ä', 'ä']

Even if you don’t speak German and don’t know about the order of letters, this doesn’t look correct, right? The capital “Ä” comes after all other lower-case letters. Since the Umlauts are not part of the ASCII table (like most letters not include in “Basic Latin”), JavaScript is using their UTF-16 code unit values so sort them. These also represent each character with a number. The letter “Ä” for example has the numeric value 228, while “z” has 122, and that’s why it is ordered like above.

Using the Intl.Collator object for natural sorting

The solution for this is really very easy. The sort() function for an array can take a callback function as a parameter. This function get passed two values of the array. It should return a negative number, if the first value is smaller than the second one, return a positive number, if the opposite is true, or return 0, if both have the same value.

But we don’t have to write such a callback function for every language ourselves. Instead, we can use the Intl.Collator object. For our German umlaut example, the code would look like this:

console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(new Intl.Collator('de').compare));
// (6) ['a', 'A', 'ä', 'Ä', 'b', 'B']

Not only do we now have our umlaut next to the similarly looking letters, we also get each lower-case letter followed by its capital letters. This is also what you would expect from a “dictionary sorting”.

Using the compare function directly

As mentioned earlier, the sort() function expects a callback that returns a negative, or positive number, or a 0. And this is exactly what the function does. We can explicitly call that function (and not only reference its name) and pass in two letters to compare:

new Intl.Collator('de').compare('ä', 'a')
// 1
new Intl.Collator('de').compare('a', 'ä')
// -1
new Intl.Collator('de').compare('a', 'a')
// 0

To demonstrate, that this function is not simply using a static list of numbers for each character, but really sorts by a specific language, here is a good example for that:

new Intl.Collator('de').compare('ä', 'z')
// -1
new Intl.Collator('sv').compare('ä', 'z')
// 1

The Swedish language, which I’m currently trying to learn, also has the “ä” letter. But it is sorted differently than a German umlaut, and you would find words starting with almost at the end of a Swedish dictionary (only followed by words starting with “ö”).

Using advanced options

There are some options you can also pass as an object to the sort() function, like in this example, when you want to have capital letters first:

console.log(
	['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(
		new Intl.Collator(
			'de', 
			{
				caseFirst: 'upper'
			}
		).compare
	)
);
// (6) ['A', 'a', 'Ä', 'ä', 'B', 'b']

You can find some more options on the documentation page or a full list in the ECMA specification. There are even some wild “languages” like “de-u-co-phonebk“, to order an array like it would appear in a German phone book – in case you know what I am talking about. 😁

Bonus: Sorting number string

I don’t want to write about all options, but one might be really helpful in your code. If you even sorted an array with numbers as string, you know that alphanumeric sorting does not work:

console.log(['1', '2', '11', '101', '3'].sort());
// (5) ['1', '101', '11', '2', '3']

But with the option numeric, you get what you want (the first “language” parameter might not be important here):

console.log(
	['1', '2', '11', '101', '3'].sort(
		new Intl.Collator(
			'en',
			{
				numeric: true
			}
		).compare
	)
);
// (5) ['1', '2', '3', '11', '101']

Just what we wanted to have. 😊

Conclusion

Sorting strings by their natural order in a specific language can really make a big difference on how usable your code is (or if it would even break things, if done incorrectly). With the Intl.Collator object, this is made rather easy in JavaScript. Other programming languages don’t provide such a powerful tool for you.

Simulate preferences in the browser

Before we get into some topics in the advent calendar, we need to learn about a browser feature. Therefore, I have changed the order of my planned topics on debugging a bit to have this one earlier.

In some of my next blog posts, I will talk about some CSS features, that can benefit certain people using your website. Almost 5 years ago, I had a blog post on how to “Recude motion on a website for better accessibility“. In this blog post, I’ve explained how you can change the settings in Windows 10 or Android 9, to simulate the reduced motion preference. But you might use a different operating system (like I use Manjaro Linux for work), where there is no quick setting, or you want to quickly toggle the setting. Fortunately, some browsers have such a toggle for you.

Setting preferences in Chrome

In Chrome (and other browsers based on the Chromium project like Edge) you can find most settings by clicking on the three vertical dots in the top right, then you choose “More tools” and open the “Rendering” tools:

Screenshot on how to open the "More tools > Rendering" settings.

This opens up another panel in the development tools. If you scroll down a bit, you will find these settings, you can use to simulate different preferences. In this example, I am setting prefers-reduced-motion to reduce:

Screenshot of the different rendering settings with the "prefers-reduced-motion" setting opened.

The only downside with this setting: as soon as you close the developer tool, the setting would reset to its default value. If you want to test a site with more vertical space available (since the two panels use a lot of height), I would suggest detaching the developer tools from the browser window.

We will need these settings in upcoming topics. But there are more options to simulate settings that will change the behavior and look of your site. If you want to quickly display your site with a dark color scheme, you don’t have to change your operating system to a dark color scheme or open up the rendering settings. Instead, you will find a nice toggle in the “Styles” section. Click on the “paint brush” icon and switch to the color scheme you want to test.

Screenshot of the opened toggle to switch the "prefers-color-scheme" setting.

Unfortuantey, just as with the rendering panel, the color scheme will switch back to the default value, as soon as you close the development tools.

Setting preferences in Edge

Even though Edge is based on Chromium and many development tool look exactly the same, you will not find the “More tools > Rendering” panel in Edge. Instead, you open up the dev tools first. On panels like “Elements” you will see the “Console” and “Issues” int the lower part. Here, you click on the “+” icon and choose the “Rendering Quick View”. It looks very similar to Chrome once opened.

Screenshot of the opened "Add tool to Quick View" panel in Edge.

You also have a quick toggle for the colors scheme in Edge with a “paint brush” icon, just as in Chrome.

Setting preferences in Firefox

In Firefox, the settings are not easily available. They are also located in different places. If you want to set the prefers-reduced-motion, you have to navigate to the URL about:config, then search for ui.prefersReducedMotion, add a boolean value and set it to false. For prefers-reduced-transparency however, you would search for layout.css.prefers-reduced-transparency.enabled and set this to true. That’s all very inconsistent and annoyingly not all in one place.

If we want to toggle the color scheme, we are more lucky. Firefox offers two buttons. With the “sun icon”, you can enforce the light color mode and with the “waxing crescent moon icon”, you can enforce the dark color mode.

Screenshot of the "Toggle dark color scheme" icon in Firefox.

Toggling one of them disables the other one. If you disable both, the default will be taken.

Bonus: faster toggle of preferences in Chromium browsers

I can’t tell you how to change the preferences in Safari, since I don’t use a Mac (or a simulates Safari often enough). But I have a nice little bonus tip for you. If you want to quickly change one of these preferences, you can use the “Run command” feature in Chromium browsers.

While the dev tools are open, hit “CTRL + SHIFT + P” (or “Command + Shift + P” on a Mac) and then search for a preference to toggle:

Screenshot of the "Run command" modal with a search for the term "prefer" showing "Emulate CSS prefers-color-scheme: dark" as the first matching command.

I haven’t found an equivalent feature in Firefox, but maybe one of you know it?

Conclusion

When developing a website that behaves or renders differently, depending on preferences set in the operating system, you should always test this. But you don’t have to change the settings of your operating system. Browser usually have options to emulate those.

If any of you knows better options for these settings in Firefox or an extension that would allow you to switch those more easily, please share your tips in the comments.

Display a progress bar on your site

If you implement some web application, where the indication of the current progress would improve the UX, a nice little progress bar can help. And there is an easy way to add one. When in the past, we would have used something like jQuery UI Progressbar, we can use a native HTML element.

The progress HTML element

If you think of how to create an element to visualize some progress on a website and which attributes it has, you might think about something similar to the range input we had in a previous blog post, but the element is much simpler:

<label for="progress-bar">Progress:</label>
<progress id="progress-bar" value="30" max="100">30 %</progress>

This is how the native <progress> HTML element looks like in Chrome with the code from above:

A label "Progress:" followed by the progress element with a 30% progress.

As you can see, the element is inline. And do you recognize something? Even though this is not a form element, it is combined with a label. This enhances its accessibility. Adding the value as a text node inside, this text will not be visible in the browser if the element is supported. So this is only needed, if you are dealing with really older browsers not supporting the element, to show this inline text instead.

Advanced usage of the element

Have you recognized, that we have not set a min attribute in the example? This attribute is not available, because the min value always defaults to 0.

But did you know, that you can also not use the value attribute and what would happen then? It might blow your mind a bit, but this is what would happen:

That’s right! You would get an animating progress bar. No JavaScript, no CSS animation, just a <progress> element without a value. And to make it even more amazing, you can use it with some attributes for an accessible progress indicator for a different region of the site. Here is an example from the documentation page:

<div aria-busy="true" aria-describedby="progress-bar">
  <!-- content is for this region is loading -->
</div>

<!-- ... -->

<progress id="progress-bar" aria-label="Content loading…"></progress>

If you are in a WordPress project, you can use the ProgressBar component. In WordPress Core, it seems to only be used for a “loading animation” progress bar like above. It does use the <progress> element, but again with some custom HTML markup replacing the browser default visualization, so it has the same styling in all browsers.

Conclusion

A progress bar can really improve the UX for a web application. If you need one, why not go with the native one. You can also style it a bit. More on that could be found in the linked documentation pages. If you add a <progress> element, please do make sure to also have a label with it. Not only does it help users with assistive technology, but indicating that the progress stands for helps any user.

Nested CSS without preprocessors

I don’t know when I’ve started using Sass but. My first blog post on that topic dates back to 2012. There are many features I love Sass for, and one of them is nesting.

What is nesting in Sass

A typical example for when nesting is really helpful is when dealing with CSS selectors of elements, that are usually children of other elements. Unordered lists used for navigation menus are a great example here:

nav {
	ul {
		list-style: none;

		ul {
			display: block;
		}
	}

	li {
		display: inline-block;
	}

	a {
		display: block;
		padding: 5px 10px;
	}
}

In this code, we have some child (or grandchild) nodes of a <nav> tag. By nesting them, and using a preprocessor like Sass, we get the following result in the compiled CSS:

nav ul {
  list-style: none;
}
nav ul ul {
  display: block;
}
nav li {
  display: inline-block;
}
nav a {
  display: block;
  padding: 5px 10px;
}

Many of you will probably agree, that the Sass code is easier to write and change, as you don’t have to write the full CSS selector. Rearranging the nesting becomes really easy, compared to replacing the middle parts of long CSS selectors. The selectors I’ve chosen above are rather short. This is on purpose, as you should not nest too much, just because you can, or even worse, because it replicated your HTML. We probably don’t need a CSS selector like “nav ul li a“, since if we just want to style every link in the navigation, just having “nav a” is all we need.

How to use nesting without a preprocessor?

If you try to use that nicely nested code in a .css file, it might just work. No more need to use a preprocessor. But you might have to support a browser, that does not implement this new specification, and which doesn’t understand this syntax. For those browsers, all you have to do is adding an & before each nested selector. The code above changes to this:

nav {
	& ul {
		list-style: none;

		& ul {
			display: block;
		}
	}

	& li {
		display: inline-block;
	}

	& a {
		display: block;
		padding: 5px 10px;
	}
}

Fortunately, all major browsers have implemented this new specification, where you don’t have to add the & before the nested selectors. But they also support the old syntax. So if you want to be safe, you could still use this old syntax for some time. I can also highly recommend reading a bit more on CSS nesting in the documentation.

Conclusion

That’s it! Just use nesting as in Sass, Less and other preprocessors and modern browsers will understand your code. If you still use a preprocessor for other things like variables, they will still convert this syntax into the normal “long CSS selectors”.

With CSS custom properties (variables) and nesting, there are two of three features now native to CSS, I use a lot in Sass. The last one is “templating” using multiple files for different part/components. The rest like mixins are more “nice to have” for me, but not really essential.