Accessible css / js switch
by ian_smithz
HTML
<link rel="stylesheet" href="https://www.screwfix.com/assets/css/sfx.min.css">
<br> <br>
<label class="check-switch" for="switch_1">
<input id="switch_1" type="checkbox">
<span aria-hidden="true"></span>
</label>
<!-- Some screen reader combinations reported to be buggy with role="switch" - you can just use checkbox version -->
<!--
<label class="check-switch" for="switch_2">
<input id="switch_2" data-check-switch="" role="switch" type="checkbox">
<span aria-hidden="true"></span>
</label>
-->
CSS
/* containing label */
.check-switch {
padding: .5em;
margin: 0 3em;
position: relative;
}
.check-switch:hover {
box-shadow: 0 0 2px 1px #2196f3;
}
/* using the before/after pseudo elements of the span to create the "switch" */
.check-switch span:before,
.check-switch span:after {
border: 1px solid #565656;
content: "";
position: absolute;
top: 50%;
transform: translateY(-50%);
}
/* styling specific to the knob of the switch */
.check-switch span:after {
background: #fff;
border-radius: 100%;
height: 1.5em;
right: 1.5em;
transition: right .1825s ease-in-out;
width: 1.5em;
}
/* styling specific to the knob "container" */
.check-switch span:before {
background: #eee;
border-radius: 1.75em;
height: 1.75em;
right: .25em;
transition: background .2s ease-in-out;
width: 2.75em;
}
/* hide the actual checkbox from view, but not from keyboards or ATs.
Instead of standard visually hidden styling, instead set opacity to
almost 0 (not zero for ChomeVox legacy bug), pointer-events none, and
then set to full height/width of container element so that VO focus
ring outlines the component, instead of a tiny box within the component
*/
.check-switch input {
height: 100%;
left: 0;
opacity: .0001;
position: absolute;
top: 0;
width: 100%;
}
.check-switch input:not([role="button"]) {
pointer-events: none;
}
.check-switch input:focus + span:before {
outline: #a9a9a9 dotted 2px;
outline-offset: 1px;
}
/* change the position of the knob to indicate it has been checked*/
.check-switch input:checked + span:after {
right: .25em;
}
/* update the color of the "container" to further visually indicate state */
.check-switch input:checked + span:before {
background: #2196f3;
}
/* 'color in' the switch knob in high contrast mode by giving it
a large border */
@media screen and (-ms-high-contrast: active) {
.check-switch span:after {
background-color: windowText;
}
}
JavaScript
(function ( w, doc, undefined ) {
'use strict';
var A11YswitchCheck;
A11YswitchCheck = function ( ) {
/**
* Author: Scott O'Hara
* Version: 0.1.0
* License: https://github.com/scottaohara/a11y_styled_form_controls/blob/master/LICENSE
*/
var el;
/**
* Initialize the instance, run all setup functions
* and attach the necessary events.
*/
this.init = function ( elm ) {
el = elm;
setRole ( el );
attachEvents ( el );
};
/**
* Check default state of element:
* A toggle button is not particularly useful without JavaScript,
* so ideally such a button would be set to hidden or disabled, if JS wasn't
* around to make it function.
*/
var setRole = function ( el ) {
if ( el.getAttribute('type') === 'checkbox' ) {
el.setAttribute('role', 'switch');
}
else {
console.error(el.id + ' is not a checkbox...')
}
};
/**
* Attach keyEvents to toggle buttons
*/
var keyEvents = function ( e ) {
var keyCode = e.keyCode || e.which;
switch ( keyCode ) {
case 13:
e.preventDefault();
e.target.click();
break;
}
};
/**
* Events for toggle buttons
*/
var attachEvents = function ( el ) {
el.addEventListener('keypress', keyEvents, false);
};
return this;
}; // A11YswitchCheck()
w.A11YswitchCheck = A11YswitchCheck;
})( window, document );