Checkboxes, Radio Buttons and Grouping

Checkboxes for many answers, radios for one. The name attribute is what groups radios, and fieldset with legend is what makes either group make sense.

Concept

Two controls that look similar and behave completely differently.

  • Checkbox - an independent on or off switch. Several can be checked. Each one is its own question.
  • Radio button - one choice from a set. Checking one unchecks the others. The set is one question.

The rule of thumb: choose all that apply is checkboxes; choose one is radios.

Checkboxes

<fieldset>
  <legend>Which updates would you like?</legend>

  <label><input type="checkbox" name="topics" value="courses"> New courses</label>
  <label><input type="checkbox" name="topics" value="events"> Events</label>
  <label><input type="checkbox" name="topics" value="jobs" checked> Job postings</label>
</fieldset>

Sharing a name across checkboxes is a convention for sending several values under one key. In PHP the name would be written topics[] to arrive as an array; other server languages differ. Independent checkboxes should each have their own name.

The unchecked checkbox problem

An unchecked checkbox sends nothing at all. Not false, not an empty string - the key simply is not present. Server code that reads the key directly will fail.

<!-- ensures a value always arrives -->
<input type="hidden" name="newsletter" value="no">
<label><input type="checkbox" name="newsletter" value="yes"> Subscribe</label>

The hidden field comes first. If the box is checked, both are sent and the later value wins in most server frameworks. If not, only no arrives.

Radio buttons

<fieldset>
  <legend>Preferred contact method</legend>

  <label><input type="radio" name="contact" value="email" checked> Email</label>
  <label><input type="radio" name="contact" value="phone"> Phone</label>
  <label><input type="radio" name="contact" value="post"> Post</label>
</fieldset>

The shared name is what makes them a group. Give two radios different names and both can be selected, which is the single most common radio button bug.

Radios are also unusual in that they cannot be unselected once a choice is made. If no answer is a valid state, either provide an explicit prefer not to say option or do not preselect anything.

Keyboard behaviour

Radios in a group form one tab stop. Tab moves into the group, arrow keys move between the options, and Tab moves out. This is correct and expected - do not try to make each radio individually tabbable.

Checkboxes are each their own tab stop, toggled with Space.

Why fieldset and legend are required here

Without a legend, a screen reader announces Email, radio button, one of three. The reader knows the options but not the question.

The legend supplies it: Preferred contact method, group. Email, radio button, one of three. For a radio or checkbox group this is not a nicety, it is what makes the group comprehensible.

<fieldset>
  <legend>Do you need accommodation?</legend>
  <label><input type="radio" name="accom" value="yes" required> Yes</label>
  <label><input type="radio" name="accom" value="no"> No</label>
</fieldset>

Note that required on any one radio in a group makes the whole group required. It only needs to be written once, though writing it on each does no harm.

The indeterminate state

A checkbox has a third visual state, used for a parent that controls several children where only some are checked. It is set only from JavaScript - there is no attribute for it.

<label><input type="checkbox" id="select-all"> Select all</label>
document.getElementById("select-all").indeterminate = true;

It is a display state only. The control still submits as checked or unchecked.

Styling

Native checkboxes and radios are hard to style and easy to break. The safe modern approach keeps the real control and restyles it:

input[type="checkbox"],
input[type="radio"] {
  width: 1.15em;
  height: 1.15em;
  accent-color: #1e3a8a;   /* recolours the native control */
}

/* a comfortable target the whole width of the text */
label:has(> input[type="checkbox"]) {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  padding: 0.4rem 0;
  cursor: pointer;
}

accent-color is a one line way to match the brand without replacing the control. Hiding the real input and drawing a fake one with CSS works, but every time it is done the focus outline, the keyboard behaviour and the high contrast rendering have to be rebuilt by hand.

Important rules

  • Radios are grouped by a shared name, nothing else.
  • An unchecked checkbox or radio submits nothing.
  • checked is a boolean attribute; checked="false" still checks it.
  • Without value, a checked control submits the string on, which is rarely what you want.
  • A radio group is one tab stop; arrow keys move within it.
  • required on one radio applies to the whole group.

Common mistakes

  • Different name values on radios that should be a group, letting several be selected.
  • Using radios where checkboxes are needed, so the reader cannot pick two.
  • Omitting value and receiving on for everything.
  • Reading an unchecked checkbox key on the server and getting an error.
  • No fieldset and legend, leaving the group without its question.
  • Hiding the native control with display: none, which removes it from the tab order entirely.
  • Tiny tap targets on a phone.

Best practices

  • Checkboxes for independent choices, radios for one from a set.
  • Always wrap a group in a fieldset with a legend.
  • Always give a value.
  • Wrap the control in its label so the whole line is clickable.
  • For yes or no questions with a default, prefer a single checkbox to two radios.
  • Use accent-color before reaching for a custom control.
  • Keep radio lists under about seven options; beyond that use a select.

Practice

  1. Build a radio group with four options and confirm it is one tab stop with arrow key movement.
  2. Give two radios different names and observe both being selectable.
  3. Submit a form with an unchecked checkbox and inspect what the server receives, then add the hidden field pattern.
  4. Style a checkbox group with accent-color and a full width clickable label.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Software Engineer Notes Management System Administrator

Continue reading

All HTML notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.