JSFiddle - React, Tailwind, and code Playground

by maritavindedal

HTML

<div role="group" aria-labelledby="id-group-label">
  <h3 id="id-group-label">Environmental Controls</h3>
  <button type="button" role="switch" aria-checked="false">
    <span class="label">Living Room Lights</span>
    <svg xmlns="http://www.w3.org/2000/svg" height="20" width="36">
      <rect class="container" x="1" y="1" width="34" height="18" rx="4"></rect>
      <rect class="off" x="4" y="4" width="12" height="12" rx="4"></rect>
      <rect class="on" x="20" y="4" width="12" height="12" rx="4"></rect>
    </svg>
    <span class="on" aria-hidden="true" aria-label="edit mode turned on">On</span>
    <span class="off" aria-hidden="true" aria-label="edit-mode turned off">Off</span>
  </button>

  <button type="button" role="switch" aria-checked="false">
    <span class="label">Outdoor Lights</span>
    <svg xmlns="http://www.w3.org/2000/svg" height="20" width="36">
      <rect class="container" x="1" y="1" width="34" height="18" rx="4"></rect>
      <rect class="off" x="4" y="4" width="12" height="12" rx="4"></rect>
      <rect class="on" x="20" y="4" width="12" height="12" rx="4"></rect>
    </svg>
    <span class="on" aria-label="edit mode turned on">On</span>
    <span class="off" aria-label="edit-mode turned off">Off</span>
  </button>
</div>

CSS

button[role="switch"] {
  display: block;
  margin: 2px;
  padding: 4px 4px 8px 8px;
  border: 0 solid #005a9c;
  border-radius: 5px;
  width: 17em;
  height: 3em;
  text-align: left;
  background-color: white;
  color: black;
}

button[role="switch"] .label {
  position: relative;
  top: -3px;
  display: inline-block;
  padding: 0;
  margin: 0;
  width: 10em;
  vertical-align: middle;
  color: black;
}

button[role="switch"] svg {
  display: inline-block;
  width: 36px;
  height: 20px;
  forced-color-adjust: auto;
  position: relative;
  top: 4px;
}

button[role="switch"] svg rect {
  fill-opacity: 0;
  stroke-width: 2;
  stroke: currentcolor;
}

button[role="switch"] svg rect.off {
  display: block;
  stroke: currentcolor;
  fill: currentcolor;
  fill-opacity: 1;
}

button[role="switch"][aria-checked="true"] svg rect.off {
  display: none;
}

button[role="switch"] svg rect.on {
  display: none;
}

button[role="switch"][aria-checked="true"] svg rect.on {
  color: green;
  display: block;
  stroke-color: currentcolor;
  fill: currentcolor;
  fill-opacity: 1;
}

button[role="switch"] span.off {
  display: inline-block;
}

button[role="switch"] span.on {
  display: none;
}

button[role="switch"][aria-checked="true"] span.off {
  display: none;
}

button[role="switch"][aria-checked="true"] span.on {
  display: inline-block;
}

button[role="switch"]:focus,
button[role="switch"]:hover {
  padding: 2px 2px 6px 6px;
  border-width: 2px;
  outline: none;
  background-color: #def;
  cursor: pointer;
}

JavaScript

/*
 *   This content is licensed according to the W3C Software License at
 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
 *
 *   File:  switch.js
 *
 *   Desc:  Switch widget that implements ARIA Authoring Practices
 */

'use strict';

class ButtonSwitch {
  constructor(domNode) {
    this.switchNode = domNode;
    this.switchNode.addEventListener('click', () => this.toggleStatus());

    // Set background color for the SVG container Rect
    var color = getComputedStyle(this.switchNode).getPropertyValue(
      'background-color'
    );
    var containerNode = this.switchNode.querySelector('rect.container');
    containerNode.setAttribute('fill', color);
  }

  // Switch state of a switch
  toggleStatus() {
    const currentState =
      this.switchNode.getAttribute('aria-checked') === 'true';
    const newState = String(!currentState);

    this.switchNode.setAttribute('aria-checked', newState);
  }
}

// Initialize switches
window.addEventListener('load', function () {
  // Initialize the Switch component on all matching DOM nodes
  Array.from(document.querySelectorAll('button[role^=switch]')).forEach(
    (element) => new ButtonSwitch(element)
  );
});