Skip to main content

/ Treasa Ní Chonchúir

The <style-switcher> element

(One of the custom elements I use around here.)

Alternative stylesheets (created with <link rel="alternate stylesheet">) are a neat feature in HTML, but no modern browser exposes UI for switching between them.

That’s where the <style-switcher> custom element comes in.

Table of Contents

  1. Usage
  2. How it works
  3. A note about progressive enhancement

Usage

As with any custom element, the close tag cannot be omitted.

Don’t forget to close your custom elements!
<style-switcher></style-switcher>

The element takes one attribute, label, which you can use to supply text for labeling the switcher’s drop-down menu.

Supply a custom label with the label="" attribute
<style-switcher label="Choose a theme"></style-switcher>

But where does the custom element get the list of themes from? It just finds all the <link rel=stylesheet> elements in the page. If a <link> element has a title="" attribute, it uses that as a human-readable description of the stylesheet. If title="" is missing, it synthesizes a description from the contents of the href="" attribute.

Basic <style-switcher> usage
<link rel=stylesheet href=/css/default.css>
<link rel="alternate stylesheet" href=green-links.css
      title="Basic theme with green links">
<link rel="alternate stylesheet" href=red-links.css
      title="Basic theme with red links">
…
<style-switcher label="Choose a theme"></style-switcher>
The above markup in action

How it works

It’s pretty much the simplest thing. There’s a change event handler on the <select> element which disables all of the stylesheets except for the selected one. (There’s a bit of redundant code for disabling stylesheets, marked like this below. This is a workaround for a Blink and WebKit bug that’s been around for ages.)

function updateStylesheets(event) {
    const selected_href = event.target.value;

    for (const sheet of document.querySelectorAll("link[rel~=stylesheet]")) {
        if (sheet.getAttribute("href") == selected_href) {
            sheet.relList.remove("alternate");
            sheet.disabled = false;
            sheet.disabled = true;
            sheet.disabled = false;
        } else {
            sheet.relList.add("alternate");
            sheet.disabled = true;
        }
    }
}

A note about progressive enhancement

I’m an advocate for custom elements that enhance the markup they contain, that still more-or-less function when the custom element fails to initialize. (This pattern’s called HTML web components these days.) You’ve probably noticed that <style-switcher> doesn’t seem to fit that pattern.

I’d argue that it actually does. All of the functionality of <style-switcher> is optional enhancement—the widget is completely unnecessary to the function of the page. It’s a nice-to-have feature. So, in the situation where it fails to load, there’s simply nothing visible to the user. The user has no idea they’re missing out on anything, which is totally fine.