0.0.3

Switcher

Based of Toggle Group Component

Switcher component enables dynamic updating of data attributes while automatically saving preferences to local storage. It's ideal for implementing theme switching functionality such as light/dark mode toggles or custom theme selectors across your application.


Anatomy

The Switcher component consists of the following data-part

root item

It can include multiple item parts.

data-value is required on all item parts

data-key is required on the component

data-default-value is not required on the component but recommended to have a default value value to load at first.

<div class="switcher switcher-js" data-default-value="a" data-key="key-anatomy">
                  <div data-part="root">
                  <button data-part="item" data-value="a">A</button>
                  <button data-part="item" data-value="b">B</button>
                  <button data-part="item" data-value="c">C</button>
                  </div>
                  </div>
Check the changes of the values of the attribute of the html tag

Data attributes

Each switcher can be set with different settings with the following data-attribute.

<div class="switcher switcher-js" data-key="key-attributes" data-default-value="c" data-orientation="vertical" data-dir="rtl" data-deselectable="true" data-loop-focus="false" data-rowing-focus="false" data-multiple="true">
                  <div data-part="root">
                  <button data-part="item" data-value="a">A</button>
                  <button data-part="item" data-value="b">B</button>
                  <button data-part="item" data-value="c">C</button>
                  </div>
                  </div>
Data attribute Type / Options Description
id string The unique id of the component. Default generated if none is provided
data-key string The attribute key used to apply the values to the document: data-${key}.
data-default-value string || string list The initial selected switcher value/values. Separated by comma
data-value string || string list The controled switcher value/values. Separated by comma
data-orientation horizontal | vertical The orientation of the switcher. Can be `horizontal` or `vertical` - `horizontal`: only left and right arrow key navigation will work. - `vertical`: only up and down arrow key navigation will work.
data-dir ltr | rtl The orientation of the switcher. Can be `ltr` or `rtl`
data-loop-focus boolean Whether the keyboard navigation will loop from last tab to first, and vice versa.
data-rowing-focus boolean Whether to use roving tab index to manage focus.
data-multiple boolean Whether to allow multiple toggles to be selected.
data-deselectable boolean Whether the switcher allows empty selection. Note: This is ignored if multiple is true
data-disabled boolean Whether the toggle is disabled.

Callbacks

Each Switcher component can receive callbacks that can be used to respond to user interaction with custom behavior.

You must add a custom id for the switcher and a event listener for your event name


                      document.getElementById("my-callback-switcher")
                      ?.addEventListener("my-callback-switcher-event", (event) => {
                        console.log("Received event:", (event as CustomEvent).detail);
                      });
                  
<div id="my-callback-switcher" class="switcher switcher-js" data-key="key-callback" data-default-value="a" data-on-value-change="my-callback-switcher-event">
                  <div data-part="root">
                  <button data-part="item" data-value="a">A</button>
                  <button data-part="item" data-value="b">B</button>
                  <button data-part="item" data-value="c">C</button>
                  </div>
                  </div>

Open your browser's console to see the events received when the toggle is clicked

Data attribute Type / Options Description
data-on-value-change string Event name to be send when the toogle is clicked

Set Value Control

Switcher value can be control with event dispatch

You must set a custom id for your switcher, as the default is randomly generated

switcher:set-value

the detail of the event must contain the value, a list of strings

                  
                     value = ["light"]
                      const targetEl = document.getElementById("my-switcher");
          if (targetEl && targetEl !== event.target) {
            targetEl.dispatchEvent(new CustomEvent("switcher:set-value", {
              detail: { value }
            }));
          }
                  
              

This a real example changing the mode on the site.

Due to another switcher with the same key on the same page, we must synchronise them using the id and callback and set value

<div id="mode-switcher-demo" class="switcher switcher-js" data-default-value="light" data-key="mode" data-on-value-change="update-mode-switcher">
                  <div data-part="root">
                  <button data-part="item" data-value="light" aria-label="Switch to light color mode">
                  <svg class="item__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"></path>
                  </svg>
                  </button>
                  <button data-part="item" data-value="dark" aria-label="Switch to dark color mode">
                  <svg class="item__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"></path>
                  </svg>
                  </button>
                  </div>
                  </div>
                  
                          document.getElementById("mode-switcher-demo")?.addEventListener("update-mode-switcher", (event) => {
                              const { value } = (event as CustomEvent<{ value: string[] }>).detail;
                              const targetEl = document.getElementById("mode-switcher");
                              if (targetEl && targetEl !== event.target) {
                                  targetEl.dispatchEvent(new CustomEvent("switcher:set-value", {
                                  detail: { value }
                                  }));
                              }
                              });
                              
                              document.getElementById("mode-switcher")?.addEventListener("update-mode-switcher-demo", (event) => {
                              const { value } = (event as CustomEvent<{ value: string[] }>).detail;    
                              const targetEl = document.getElementById("mode-switcher-demo");
                              if (targetEl && targetEl !== event.target) {
                                  targetEl.dispatchEvent(new CustomEvent("switcher:set-value", {
                                  detail: { value }
                                  }));
                              }
                              });
                  
              

Duo Switch

If you only have 2 values and you wish to hide the active one, you can add the switcher--duo class

<div class="switcher switcher-js switcher--duo switcher--circle" data-default-value="a" data-key="key-duo">
                  <div data-part="root">
                  <button data-part="item" data-value="a">A</button>
                  <button data-part="item" data-value="b">B</button>
                  </div>
                  </div>

RTL

RTL support for switcher

<div dir="rtl">
                  <div class="switcher switcher-js" data-key="key-rtl" data-default-value="a" data-dir="rtl">
                  <div data-part="root">
                  <button data-part="item" data-value="a">A
                  <svg class="item__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
                  </svg>
                  </button>
                  <button data-part="item" data-value="b">B
                  <svg class="item__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
                  </svg>
                  </button>
                  <button data-part="item" data-value="c">C
                  <svg class="item__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
                  </svg>
                  </button>
                  </div>
                  </div>
                  </div>

Custom (Tailwind)

If you are using Tailwind styling, you can customize each switcher and its parts with Tailwind utilities

<div class="switcher switcher-js" data-key="key-6" data-default-value="a">
                  <div data-part="root" class="bg-ui-alert shadow-xl rounded-xl">
                  <button data-part="item" data-value="a" class="bg-ui-brand data-[state=on]:bg-accent-brand data-[state=on]:text-accent-contrast-alert data-[state=on]:hover:bg-accent-brand-1 data-[state=on]:active:bg-accent-brand-2">A</button>
                  <button data-part="item" data-value="b" class="bg-ui-alert data-[state=on]:bg-accent-alert data-[state=on]:text-accent-contrast-brand data-[state=on]:hover:bg-accent-brand-1 data-[state=on]:active:bg-accent-brand-2">B</button>
                  <button data-part="item" data-value="c" class="bg-ui data-[state=on]:bg-accent data-[state=on]:text-accent-contrast-brand data-[state=on]:hover:bg-accent-1 data-[state=on]:active:bg-accent-2">C</button>
                  </div>
                  </div>

Templates

Switcher components is available in the followng templates

Switch Tabs