Checkbox
Definition
The <input type="checkbox">
defines a checkbox.
The checkbox is shown as a square box that is ticked (checked) when activated.
Checkboxes are used to let a user select one or more options of a limited number of choices.
Description
<input>
elements of type checkbox are rendered by default
as boxes that are checked (ticked) when activated, like you might see in an official
government paper form. The exact appearance depends upon the operating system
configuration under which the browser is running. Generally this is a square
but it may have rounded corners. A checkbox allows you to select single values
for submission in a form (or not).
Checkbox inputs can only have two states: checked or unchecked. They can have any value, but they either submit that value (checked) or don’t (unchecked) with a form submission. The default is unchecked.
Accessibility Considerations
Always add a <label>
element to your input field and associate
it programmatically with the checkbox. Without it screen reader users will not
know what they are selecting.
Simple Checkbox Example
<form>
<fieldset>
<legend class="h4 mb-3">Two-factor authentication</legend>
<div class="form-check">
<input
class="form-check-input"
id="twofa_enabled"
name="twofa_enabled"
type="checkbox"
value="true" /><label for="twofa_enabled"
>Enable two-factor authentication.</label
>
</div>
<input class="btn btn-primary btn-sm mt-3" type="submit" value="Submit" />
</fieldset>
</form>
Grouping Checkboxes
Accessibility Considerations
When a group of related form fields shares a group label, the group and corresponding label must be expressed semantically, so that people using a screen reader can understand the relationship of the form fields, their individual labels, and their group label.
Grouping controls is most important for related radio buttons and checkboxes. Associating a label with an individual radio button or checkbox is required, but alone is not enough. Knowing a radio button has a label of "yes" or "no" is not useful if the user does not know what question they are answering.
Like all form fields, it may be necessary to group checkboxes together with
a
<fieldset>
. According to Deque, there is no natural way
to create a description that is associated with a group. Adding
aria-describedby to either the <fieldset>
or
<legend>
element doesn't work. If the instructions are short
you can add it to the legend.
Grouped Checkboxes Example
<form>
<fieldset>
<legend class="h4">
Favorite fruits (Required) <span class="text-danger">*</span>
</legend>
<div class="form-check">
<input
class="form-check-input"
id="strawberry"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="strawberry"> Strawberry</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="watermelon"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="watermelon">Watermelon</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="apple"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="apple">Apple</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="orange"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="orange">Orange</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="other"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="other">Other</label>
</div>
</fieldset>
</form>
It is also acceptable to have multiple legends in one fieldset.
Grouped Checkboxes with Multiple Legends
<form>
<legend class="fs-6 mb-4">
All fields with an (<span class="text-danger">*</span>) are required fields.
</legend>
<fieldset>
<legend class="h4">
Favorite fruits <span class="text-danger">*</span>
</legend>
<div class="form-check">
<input
class="form-check-input"
id="strawberry"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="strawberry"> Strawberry</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="watermelon"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="watermelon">Watermelon</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="apple"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="apple">Apple</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="orange"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="orange">Orange</label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="other"
type="checkbox"
aria-required="true" />
<label class="form-check-label" for="other">Other</label>
</div>
</fieldset>
</form>
Indeterminate State
Visually, there are actually three states a checkbox can be in: checked, unchecked, or indeterminate.
You can’t make a checkbox indeterminate through HTML. There is no indeterminate attribute. It is a property of checkboxes though, which you can change via JavaScript.
The indeterminate state is visual only. The checkbox is still either checked or unchecked as a state. That means the visual indeterminate state masks the real value of the checkbox. Because of this, its important that the UI clearly expresses the indeterminate state!
Accessibility Considerations
In addition, aria-checked='mixed'
can be used to tell screen reader
users that the checkbox is neither checked or unchecked.
Don't do this
The current state of this checkbox is not clear to the user.
Do this!
The indeterminate state is clearly expressed through the selection of grouped choices.
<form action="">
<fieldset>
<legend class="h4">Favorite Things (Short and Tall)</legend>
<ul class="list-unstyled">
<li>
<input
class="form-check-input"
id="allTallThings"
name="tall"
type="checkbox" />
<label for="allTallThings">All Tall Things</label>
<ul class="ms-3 list-unstyled">
<li>
<input
class="form-check-input"
id=" buildings"
name=" buildings"
type="checkbox" />
<label for=" buildings">Buildings</label>
</li>
<li>
<input
class="form-check-input"
id="giraffe"
name="giraffe"
type="checkbox" />
<label for="giraffe">Giraffe</label>
</li>
<li>
<input
class="form-check-input"
id="giants"
name="giants"
type="checkbox" />
<label for="giants">Giants</label>
<ul class="ms-3 list-unstyled">
<li>
<input
class="form-check-input"
id="andre"
name="andre"
type="checkbox" />
<label for="andre">Andre</label>
</li>
<li>
<input
class="form-check-input"
id="bunyan"
name="bunyan"
type="checkbox" />
<label for="bunyan">Paul Bunyan</label>
</li>
</ul>
</li>
<li>
<input
class="form-check-input"
id="two-sandwiches"
name="two-sandwiches"
type="checkbox" />
<label for="two-sandwiches">Two stacked sandwiches</label>
</li>
</ul>
</li>
<li>
<input
class="form-check-input"
id="allShortThings"
name="short"
type="checkbox" />
<label for="allShortThings">All Short Things</label>
<ul class="ms-3 list-unstyled">
<li>
<input
class="form-check-input"
id="smurfs"
name="smurfs"
type="checkbox" />
<label for="smurfs">Smurfs</label>
</li>
<li>
<input
class="form-check-input"
id="mushrooms"
name="mushrooms"
type="checkbox" />
<label for="mushrooms">Mushrooms</label>
</li>
<li>
<input
class="form-check-input"
id="oneSandwich"
name="oneSandwich"
type="checkbox" />
<label for="oneSandwich">One Sandwich</label>
</li>
</ul>
</li>
</ul>
</fieldset>
</form>
Javascript for Indeterminate state
Please refer to Deque on how to properly set aria values on indeterminate checkbox groups.
https://dequeuniversity.com/library/aria/checkbox-tri
<script>
// helper function to create nodeArrays (not collections)
const nodeArray = (selector, parent = document) => [].slice.call(parent.querySelectorAll(selector));
// checkboxes of interest
const allThings = nodeArray('input');
// global listener
addEventListener('change', e => {
let check = e.target;
// exit if change event did not come from
// our list of allThings
if (allThings.indexOf(check) === -1) return;
// check/unchek children (includes check itself)
const children = nodeArray('input', check.parentNode);
children.forEach(child => child.checked = check.checked);
// traverse up from target check
while (check) {
// find parent and sibling checkboxes (quick'n'dirty)
const parent = (check.closest(['ul']).parentNode).querySelector('input');
const siblings = nodeArray('input', parent.closest('li').querySelector(['ul']));
// get checked state of siblings
// are every or some siblings checked (using Boolean as test function)
const checkStatus = siblings.map(check => check.checked);
const every = checkStatus.every(Boolean);
const some = checkStatus.some(Boolean);
// check parent if all siblings are checked
// set indeterminate if not all and not none are checked
parent.checked = every;
parent.indeterminate = !every && every !== some;
// prepare for nex loop
check = check != parent ? parent : 0;
}
})
/*
closest polyfill for ie
if (window.Element && !Element.prototype.closest) {
Element.prototype.closest =
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i,
el = this;
do {
i = matches.length;
while (--i >= 0 && matches.item(i) !== el) {};
} while ((i < 0) && (el = el.parentElement));
return el;
};
}
*/
</script>
Sass Variables
$form-label-margin-bottom: 0;
$form-label-color: $grey-500;
$form-check-label-color: $grey-500;
$form-check-input-border: $input-border-width solid $input-border-color;
$form-check-input-border-radius: 0.125em;
$form-check-input-checked-color: $input-color;
$form-check-input-checked-bg-color: $input-bg;
$form-check-input-checked-border-color: $input-color;
$form-check-input-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$white}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/></svg>");
$form-check-radio-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='2' fill='#{$form-check-input-checked-color}'/></svg>");
$form-check-input-indeterminate-color: $white;
$form-check-input-indeterminate-bg-color: $input-color;
$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color;
$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-indeterminate-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/></svg>");
See BS5 documentation for Sass Variables