Skip to content

Checkbox and radio

Native controls, with the label wrapping the input so the whole thing is a target.

Preview

Code

The three render the same class names, the same ARIA and the same data attributes. That is checked when this site builds: a class in any of these samples that neither the markup contract nor the shipped stylesheet defines fails the build.

<label class="wsu-checkbox">
  <input type="checkbox" id="advisor" name="advisor">
  <span>I have spoken with my advisor</span>
</label>

<fieldset class="wsu-field" id="delivery" tabindex="-1">
  <legend class="wsu-field__label">Delivery</legend>
  <label class="wsu-radio"><input type="radio" name="delivery" value="in-person"><span>In person</span></label>
  <label class="wsu-radio"><input type="radio" name="delivery" value="online"><span>Online</span></label>
</fieldset>

Why it works this way

  • The label wraps the control, so the text is part of the click target rather than a separate 16px box next to it.
  • A radio set is a fieldset with a legend. Without it a screen reader user hears the options and not the question.
  • The fieldset carries an id and tabindex="-1" of its own, the same as the field it groups. A radio question is answered or not as a whole, so when an error summary links at it, the link has to land on the fieldset, and a fieldset only takes focus when something has given it a negative tabindex first.
  • The control keeps its native appearance under forced colors, which is why the styling is an accent-color and a focus ring rather than a hidden input and a drawn box.

Accessibility

  • Group of radios wrapped in fieldset with legend (1.3.1, 3.3.2).
  • The whole label is the target, which is what gets it past 44px (2.5.5).
  • fieldset id and tabindex="-1" so an error summary entry can move focus to the group rather than only scroll to it (2.4.3, 3.3.1).

Success criteria in brackets refer to WCAG 2.2. Every one of them is checked by axe-core in both themes at three viewport widths before this page can be published. See Accessibility for what the gates are.

Props

Blade

AttributeDefaultWhat it does
namerequiredAlso the default id and error-bag key.
labelnullRendered next to the box when no slot is given.
value / falseValue'1' / '0'falseValue renders the hidden input, so an unchecked box still submits.
checkedold inputFalls back to the old input value on a failed submit.
hintnullAfter the control, wired with aria-describedby. The one field whose hint follows rather than leads, because the label already wraps the toggle.
errorthe error bagPass a string to override.
requiredfalseAdds the required attribute and the "(required)" text.
ida slug of nameSet it to change the id without changing name.
bag'default'Named error bag.

Vue

PropType and defaultWhat it does
modelValueBoolean or Array, falseAn array collects several checkboxes into one model.
valueString, Number or Boolean, trueOnly meaningful when modelValue is an array.
RadioGroup optionsArray, [][{ value, label }] or plain strings.