creating a simple, accessible on/off switch
TRANSCRIPT
Checkbox and radio button switch:
https://russmaxdesign.github.io/switch-checkbox/
Github: https://github.com/russmaxdesign/switch-
checkbox
During this presentation, I’m going to ask you some questions - which you can answer in the chat window.
I'll be giving away three SitePoint Premium annual memberships as
prizes to the best/quickest answers.
That gives you unrestricted access to over $20,000 worth of SitePoint
books and courses!
https://www.sitepoint.com/premium/
Question 1: What is the easiest and most
effective way of identifying common accessibility problems in your site/
app?
A large number of users rely on key-strokes (TAB, ARROW, ENTER,
SPACE) or the equivalent of these keystrokes in order to navigate and
interact with sites/apps.
If you cannot navigate or interact with your site/app using keystrokes
only, then your site is potentially inaccessible to a large number of
users.
Question 2: Why is this one of the most evil CSS
rules you could ever write?
*:focus { outline: none; }
Because this rule make it hard, if not impossible, for keyboard-only users to see which element is in focus and therefore very hard to navigate
and interact with your site/app.
Web designers and developers have always struggled with how to customise radio buttons and
checkboxes.
The main issue is that radio buttons and checkboxes are notoriously hard to style - especially across multiple browsers and devices.
In some cases this involved using JavaScript to remove the original
radio or checkbox element making the end result inaccessible for a
wide range of assistive technologies.
It is possible to style these elements without having to use JavaScript. And more importantly, we can make
the end result accessible.
Let’s take a simple example of an on/off switch that can be applied to either radio or checkbox elements:
Well… many of these are not really features, they are just default
behaviours that should not be overridden.
Feature 1: We will use the appropriate
semantic elements - input and label elements. We will explicitly
associate these elements using matching “for" and "id" values.
Feature 2: The label content can be used to
describe the purpose of each switch for screen readers. This content is
hidden off-screen.
Feature 3: We will make the two different states (“on” and “off”) clearly
distinguishable using a tick icon for the “on” state. This will aid colour-
blind users and some types of cognitive-impaired users.
Feature 4: Because we are using native
elements, the default keyboard behaviour will still be available.
(Users can select a radio button or checkbox using the SPACE bar).
Feature 5: We will make the focus and hover states clearly visible. The focus state is especially important for
keyboard only users.
<div class="switch"> <input class="switch__control" type="radio" name="example01" id="example01">
<label class="switch__label" for="example01"> <span class="switch__content">Label content</span> </label>
</div>
input
label
<div class="switch"> <input class="switch__control" type="radio" name="example01" id="example01">
<label class="switch__label" for="example01"> <span class="switch__content">Label content</span> </label>
</div>
radio
<div class="switch"> <input class="switch__control" type="checkbox" name="example01" id="example01">
<label class="switch__label" for="example01"> <span class="switch__content">Label content</span> </label>
</div>
checkbox
<div class="switch"> <input class="switch__control" type="radio" name="example01" id="example01">
<label class="switch__label" for="example01"> <span class="switch__content">Label content</span> </label>
</div>
id
for
We will use BEM-like class names as these allow us to see the
relationship between the parent element, descendant elements and
modifiers.
/* parent module */ .switch { }
/* parent modifiers */ .switch-‐-‐xl { } .switch-‐-‐lg { } .switch-‐-‐md { } .switch-‐-‐sm { } .switch-‐-‐xs { }
/* parent module */ .switch { }
/* descendants of parent module */ .switch__control { } .switch__label { } .switch__content { }
The radio button or checkbox control (“switch__control”) is then positioned on top of the parent. It will be given the same dimensions
as the parent.
The label (“switch__label”) is placed on top of the radio button and also given the same dimensions as the parent. We are hiding the control
under the label.
We will then style the background of the label to look like a switch - including adding rounded corners
and our background icon.
And finally, the label content (“switch__content”) is hidden off screen so that it is available for
screen readers, but does not clutter the visual appearance of the switch.
Checkbox and radio button elements can be manually changed
by users - from unchecked to checked etc.
<!-‐-‐ no additional attributes -‐-‐> <input type="checkbox">
<!-‐-‐ boolean checked attribue -‐-‐> <input type="checkbox" checked>
<!-‐-‐ boolean disabled attribute -‐-‐> <input type="checkbox" disabled>
However, for this solution, most of the styling is applied to the label
element, rather than the input.
We can get around this using adjacent sibling selectors, which
target any label element that is adjacent to (comes directly after) the
input.
/* unchecked input */ .switch__control + label { }
/* checked input */ .switch__control:checked + label { }
/* disabled input */ .switch__control[disabled] + label { }
We also want to style the :focus and :hover states of the switch,
which can also be done using adjacent-sibling selectors.
/* unchecked input */ .switch__control:hover + label { } .switch__control:focus + label { }
/* checked input */ .switch__control:checked:hover + label { } .switch__control:checked:focus + label { }
Question 3: Why would we want to be able to
control all of the dimensions of our switch using one master SASS
variable?
So, we have four different variables for the dimensions:
- switch width- switch height
- toggle width/height- gutter (space) around the toggle
$switch-‐width: 3em; $switch-‐height: ($switch-‐width / 2); /* 1.5em */ $toggle-‐width: ($switch-‐width / 3); /* 1em */ $toggle-‐gutter: ($switch-‐width / 12); /* .25em */
$switch-‐xl: 1.6em; $switch-‐lg: 1.4em; $switch-‐md: 1.2em; $switch-‐sm: 1em; $switch-‐xs: .8em;
$color-‐toggle: #fff;
$color-‐unchecked-‐static: #aaa; $color-‐unchecked-‐hover: #777;
$color-‐checked-‐static: #00a000; $color-‐checked-‐hover: #006e00;
$color-‐disabled: #ddd;
I’m generally not a fan of transitions or animations unless they are being used to help “tell the story” of a UI component - help users understand
what is happening.
For the checkbox, we could do a very simple transition to animate the switch from unchecked to
checked - to help users understand what has happened.
.switch__label:after { left: $toggle-‐gutter; transition: left .04s;
}
.switch__control:checked + label:after { left: $switch-‐height + $toggle-‐gutter; }
.switch__label { background: $color-‐unchecked-‐static; transition: background .2s;
}
.switch__control:hover + label,
.switch__control:focus + label { background: $color-‐unchecked-‐hover; }
Checkbox and radio button switch:
https://russmaxdesign.github.io/switch-checkbox/
Github: https://github.com/russmaxdesign/switch-
checkbox
A simple, accessible language switcher module:
https://russmaxdesign.github.io/language-switcher/
Github: https://github.com/russmaxdesign/language-
switcher
Upvote - downvote module: https://russmaxdesign.github.io/upvote-downvote/
Github: https://github.com/russmaxdesign/upvote-
downvote