Creating a dialog with native HTML

Here it is now, the last advent calendar blog post for 2024. Which also means, that many of you are celebrating Christmas today or tomorrow. So why do you read blog posts on such a day? 😁

The last topic for this edition will be about another HTML element you might not have known before. But you all know and hate cookie popups, right? Well, unless you are into the development of such tools. 😅

The dialog HTML element

When we “want” to create a popup, we would have used some kind of “modal library” in the past. But now, we have a native HTML element for this: the <dialog> element. It has some similarities with details element, I have blogged about four days ago. And if you visited the resources I’ve linked there, you might already have seen the element. This is how you would use it:

<dialog>
	Some Text inside the dialog.
</dialog>

If you put this code into your page, you would see … absolutely nothing. The element is not visible by default. You can add the open attribute to it, just as with the details element and then you would see this:

Chrome with open dev tools showing the properties of the open dialog element, that is centered on the page.

As you can see here, we get more than just a blank element with some text. The dialog has a border, some padding and is centered on the page, using an absolute position and auto margin. This would even mean, that it is displayed above any content that comes after it.

Opening and closing the dialog

A dialog that open by default is not really all that useful. Fortunately, we have some JavaScript functions to make it interactive. So let’s add a button that would open the dialog and a button inside of it, to close it:

Chrome showing an open dialog element. The dev tools are open and at the bottom of the HTML markup tree, a "#top-layer" is shown after the closing html tag. The dialog has a paragraph and an undordered list. The page behind the dialog has an "Open dialog" button and inside the dialog, there is a "Read it" button at the end.

This screenshot shown multiple things at once. First, we have the webpage with an “Open dialog” button. When clicked, it will open the dialog, which has some more HTML content this time. You may also notice, that the background of the website is gray. It is white, but when the dialog is open, there will be an overlay to the page. This can also be seen in the left with the ::backdrop pseudo-element. One last thing to recognize is the #top-layer after the HTML tree on the left. This is a special layer that covers the entire page and helps with accessibility.

When you close on the “Read it” button, the dialog will close. For the two buttons, we need some JavaScript to add this functionality:

const dialog = document.querySelector('dialog');
const openButton = document.querySelector('button#open-dialog');
const closeButton = document.querySelector('button#close-dialog');
openButton.addEventListener('click', () => {
	dialog.showModal();
});
closeButton.addEventListener('click', () => {
	dialog.close();
})

When you use the showModal() function, it does not really matter where the element is placed in the HTML tree. It will always open in the center of the page. If the content does not fit, a scrollbar will appear.

Alternative way to open the dialog

There is a second function to open a dialog. It is simply called show() and would open the dialog “in place” and not as a modal. In this case, it makes a difference where the dialog is placed. It will still show it with “position: absolute” over the content, but not vertically centered anymore. It will appear just where its HTML is placed in the DOM. There will also be no backdrop overlay and no #top-layer, so the behavior has some major differences.

Closing the modal without JavaScript

To open the dialog, you either need some JavaScript with one of the two functions to “show” it. Unless you add the open attribute to it, to have it open by default – I really wonder why the functions are not openModal() and open(), then the element is also using the open attribute.

To close the dialog, however, you might not need JavaScript. You can also have it closing itself, when you have a form inside of it:

<dialog open>
	<form method="dialog">
		Some text in the dialog.
		<button>OK</button>
	</form>
</dialog>

Do you see the special trick with the form? It’s using the value dialog for the method attribute. This will close the dialog, whenever the form is submitted with a button inside of it. So you could even just have an input and it will close on ENTER.

If the dialog was opened with showModal(), this modal can also be closed with the ESC key. So there are different ways to close it without any JavaScript.

Styling the element

The dialog itself can be styled like any other “container”. As shown earlier, it comes with some default border and padding. More interesting is probably the styling of the backdrop. Here is an example styling both:

dialog {
	border-radius: 5px;
	border: 3px solid red;
	top: 30px;

	&::backdrop {
		background: black;
		opacity: 0.7;
	}
}

We are changing the border of the dialog and also change the background of the backdrop. Ah, and by the way, what you see here is no Sass syntax. This is native nested CSS. If you haven’t seen this before, read the previous advent calendar blog post “Nested CSS without preprocessors“. Finally, this is how it would look like:

An open dialog with a changed "background" with lower transparency and a red border with a border-radius. The dev tools are open to show the HTML code and CSS of the dialog element.

Instead of the rgba() value for the background we now use a background-color in combination with an opcatity.

Getting even more out of the element

On the documentation page, you will fine many more examples on how to use forms inside the element, style it, interact with it, etc. But all of that would be too much for this blog post. The CSS-Trick blog post that covers the details element, also has some examples for the dialog element.

Conclusion

I hope I have saved the best topic for today. At least I found this element really fascinating and used it in one project. I might blog about this use-case. But that’s something for next year. This was my final advent calendar blog post and also the last one for this year, keeping my two weeks routine on blogging.

I hope you’ve learned some new things in the advent calendar blog posts, or in the others I have written this year. It was fun, but also a lot more work than I thought it would be. Have some great few days and see you all in 2025! 🎇

An easier dark mode

Four day ago, I wrote about “The prefers-color-scheme media query” and how to use it to create a dark mode for your browser. Today, I’m telling you: you might not even need it. Hm, but how do we get a dark mode then, you might ask? With a single CSS property instead of a media query.

The color-scheme property

All you need to use is one single property to tell the browser which color schemes you have. And this is how you can use it:

:root {
  color-scheme: light dark;
}

That’s it! You don’t need any more than that. Come back tomorrow for another blog post. 😁 Nah, there is more to it. But let’s see what this already does. I’ve created a page with some HTML and no CSS, other than the line above. This is how it looks like in my Chrome browser using a light mode:

Screenshot of some elements in light mode in Chrome: h1, p, fieldset, input, textarea, select and button.

And now we use the switch that I’ve shown you in the blog post “Simulate preferences in the browser” to switch to the dark mode. This is what you would get then:

Screenshot of some elements in dark mode in Chrome: h1, p, fieldset, input, textarea, select and button. The inputs and the buttons now have a background color.

While in the light mode, all elements had a white background, the form elements and the button now have a gray background. The placeholder text of the input however has a too low contrast now.

All of this we “got for free”. Just with a single line of CSS. But this design might not please you, so how can you change it, without using the media query? There are some special “functions” you can use.

The light-dark() function

Let’s take our source code from “The prefers-color-scheme media query” blog post to see how the color-scheme can be used to create a simple dark mode. This was our old code:

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;
	}
}

Now, we take this code, and place every color into the new light-dark() function, that already has a good baseline support. We first set the light color and then the dark color. It is used like this:

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

Isn’t that a lot easier to read? When we view the page with a light color scheme preference, this is what we would see:

Chrome with open dev tools focused on a button. In the "Styles", the "light-dark()" function visualizes the two colors.

When you inspect the button, you can see the light-dark() function in the “Styles” and see/change the colors there. Now let’s view the same page with the dark color scheme preference:

Chrome with open dev tools focused on a button. In the "Styles", the "light-dark()" function visualizes the two colors. It simulates the dark mode.

That’s the basic usage of the color-scheme property and the light-dark() function. But the property can have different values.

Switching to a dark mode by default

If you want to use the dark mode as a default for your website and not use a light mode at all, just set the color-scheme like this:

:root {
	color-scheme: dark;
}

If you want to have a light mode by default, do the opposite, and use light as the only value. As soon as you have both values light and dark, the order is not important, you will have the behavior shown above.

Preventing the dark mode

As shown in the “Simulate preferences in the browser” blog post, Chrome has an option for an “Automatic dark mode”. This will use a dark mode, even if the color-scheme is set to light. But you can prevent a dark mode, by adding the only keyword as well:

:root {
	color-scheme: only light;
}

Now the browser will not use a dark mode. And sure enough, you can also use “only dark” to get the opposite.

Setting the property for some elements only

Some website designs have that use “different color schemes” for different element. Like a website that has a dark header, but a light footer. You might want to keep those colors, even if the modes can be switched. Then you can restrict the color-scheme for some elements, but allow both for other parts:

:root {
	color-scheme: light dark;
}
header {
	color-scheme: only dark;
}
.left {
	color-scheme: light;
}
.middle {
	color-scheme: light dark;
}
.right {
	color-scheme: dark;
}
footer {
	color-scheme: only light;
}

In this code snippet, we set both color schemes for the root element, but then change it for specific parts of the page. The header and footer are limited to just one color scheme. In the main section, we have three parts. The left and right only take one scheme, the one in the middle also takes two (we could leave property for the middle one out, as it would inherit the scheme from the root element, but for completeness, I left it here).

I’ve placed our button into each of these sections. Let’s see how this looks in Chrome with the different color scheme simulations:

With “prefers-color-scheme: light”

Chrome with "prefers-color-scheme: light" showing a dark button in the header and on the right, and light buttons on the left, in the middle and in the footer.

We can see here that our “color-scheme: dark” on the “.right” container has turned the button inside to the dark color-scheme.

With “prefers-color-scheme: dark”

Chrome with "prefers-color-scheme: dark" showing a dark button in the header, in the middle and on the right, and light buttons on the left and in the footer.

Switching to dark color scheme, the button in the middle has changed, but the other ones stayed the same. We also have a black background of the main element now, but the background colors of the header and footer stayed the same.

With “Automatic dark mode”

Chrome with "Automatic dark mode" showing a dark button in the header, in the middle and on the right, and a light button in the footer. The button on the left has a dark mode with a different border-color (darker).

Our header and footer still stay the same. But now also the left button has turned into a dark mode, since this part is not using the “only” keyword. The border color however is darker than in our defined CSS design.

Styling your dark mode even further

Since the light-dark() function can only be used for colors, you may still have to use the prefers-color-scheme media query to change other styles in your dark mode.

If you want to be even more creative, and learn deeply about dark mode option, I can recommend the “Dark Mode in CSS Guide” CSS-Trick blog posts, as well as the two pages on the color-scheme property and on the light-dark() function.

Conclusion

Adding a dark mode to your page and testing it has never been easier than with the methods described in the past blog posts. So maybe in those dark winter days, you can also adapt with the design of your website and try to create some nice dark color scheme.

Get CSS styles using JavaScript

You should usually use each of the basic web technologies for their main purposes: HTML for the markup, CSS for the styles and JavaScript for additional interactivity. But sometimes, those things cross into each other. There might be cases, in which you need to know the current rendered styles of an element. That’s what the topic today is about.

Get the computed styles of an element

The function name for that couldn’t be easier to remember: getComputedStyle(). It’s a function of the window object and gets passed the element we want the styles for:

const body = document.querySelector('body');
const bodyStyles = window.getComputedStyle(body);
const bodyFontSize = bodyStyles.getPropertyValue('font-size');
console.log(bodyFontSize); // 16px

To get a single CSS property, you use the getPropertyValue() function on the CSSStyleDeclaration object (in our case bodyStyles), and tell it what property you want to get.

If you run this code in a completely empty HTML, you will probably get 16px as the body font size, which is the default in most browsers, unless the size was changed in the operating system or browser.

But let’s see, where this can get more interesting. You don’t always have a fixed font width on an element, but might have one that is relative to other elements:

body {
	font-size: 22px;
}
h1 {
	font-size: 1.6em;
}
h1 small {
	font-size: 75%;
}

Now we have a website with a <h1> headline and some <small> element inside of it. What would our resulting font size be? Let’s grab our calculators:

  • <body> font-size: 22px
  • <h1> font-size: 22px * 1.6 = 35.2px
  • <small> inside <h1> font-size: 35.2px * 0.75 = 26.4px

So we should get a font-size of 26,4px for the small text. Let’s see, if that is the case, by also checking it with the dev tools:

Screenshot of Chrome with the opened dev tools. The page has an h1 element with a small inside. This element is selected and the "Computed" styles panel shows "font-size: 26.4px".

Here you can see a full example, with the modified selector. We have selected the <small> element, and Chrome shows us the 26.4px font-size in the “Computed” section. As I wrote after the console.log() call, this is also what we would get from JavaScript. Quite nice, isn’t it?

Getting styles from a pseudo-element

If an element has a pseudo-element, either specifically set or available by default, you can query styles for them. Let’s set some styles for the new ::marker pseudo-element of a list item:

ul li::marker {
	color: red;
}

We can’t just select a pseudo-element using the document.querySelector() method, but you can get the computed styles like this:

const element = document.querySelector('ul li');
const color = window.getComputedStyle(element, '::marker').color;
console.log(color); // rgb(255, 0, 0)

If you try to get styles for “an older pseudo-element” like the ::before pseudo-element, you can also use :before, but you can’t use :marker, as this is a newer pseudo-element, for which the variant with just one colon does not exist.

In this code snippet, I’m not using the getPropertyValue(), as it’s not strictly necessary. You can get most properties directly by their names. I’d recommend taking a look at the CSSStyleDeclaration object documentation of log the object to the console, to inspect the available properties.

What you can also see in this example is that we don’t get the value red, we have used in our CSS. Instead, we get rgb(255, 0, 0) as the computed style. You can find some more information on this in the notes on the documentation page.

Conclusion

You might have some JavaScript code that needs to know the styles of an element. Let it be the size, color, position or other properties that would have an effect on your code. In these cases, the getComputedStyle() is really helpful. WordPress Core is also using it in different place, like in the color-picker to warn about a too low color contrast. That’s a very good example on how to use the function.

Record and replay website interactions for tests

This is the final blog post on the debugging topic for this advent calendar. I reserved something special for the last one. Maybe you have something you have to test on the frontend (or backend) of your page, over and over again, after you have changed something. Let it be filling out a form, clicking through some elements or navigating pages. It can be really tiresome to do that over and over again manually. But if you are using Chrome for your tests, there is a nice built-in tool I’ve learned about from a colleague.

The Chrome Recorder feature

To get to this feature, you have two ways. You can open the panel in a similar way as we have opened the “Rendering” panel. Open the dev tools and click on the three vertical dots, and choose “More tools” and then “Recorder”:

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

The second way is to use the “Run Command” modal, which you open with “CTRL + SHIFT + P” (or “Command + Shift + P” on a Mac), and then search for “Recorder”. Once the panel is open, click on the blue “Create a new recording” button:

Screenshot of the opened Chrome Recorder panel with a blue "Create new recording" button.

On the next page, Chrome will automatically create a “Recording Name” for your recording with the current date and time, which you can overwrite with a more meaningful title:

Chrome Recorder panel with a changed "Recording Name" before the recording is started.

If you are ready, click on the red button at the button or hit “CTRL + E”. Then do something with the page. In this example, I clicked on the search in the top right, types “advent calendar” into the search field and hit ENTER. After the page reloaded, I clicked the button at the bottom again to stop the recording. You should see something like this then:

Chrome Recorder after the recording.

If you scroll up, you will find a “Show code” button in the top left. If you click it, you can see the technical file for the recording in different formats:

Chrome Recorder with opened code view.

The most interesting button is the blue “Replay” button. Left of if, you can also select a playback speed. When you click the button, Chrome will show a message “Chrome is being controlled by automated test software”. It will then replay all steps and check each of them. When everything was successful, the vertical line will be green for some seconds:

Chrome Recorder just after replay.

In the top left of the panel, you can see a dropdown. Here you will find any recording you every did in that Chrome profile. With the buttons next to it, you can import, export and delete them. By exporting them into a JSON file and sending it to some other person, this can be a great way to share the repeatable test steps with others.

You can also expand each step and change the settings of the step. You can find a short video explaining the Recorder a bit more on the Chrome for Developers page.

Conclusion

If you want to repeat some test on a page, but don’t want to manually repeat every click and keystroke manually, the Chrome Recorder is a great tool you can use. It also has an export option, I will probably cover in another blog post. But since this does not fit into our four topics for the 2024 advent calendar, you have to wait until 2025.

I couldn’t find a similar feature in Firefox, but if you know of an extension that offers this in other browsers, please leave a comment.

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.