LEARNING OBJECTIVES โต
- Understand the architectural design and mutual exclusivity model of
<input type="radio">. - Master the keyboard accessibility mechanics of radio groups, including roving focus via arrow keys.
- Implement semantic grouping using
<fieldset>and<legend>for assistive technologies. - Manage initial default states and avoid the "un-uncheckable" trap in required radio groups.
๐ The Mental Model & Story (Intuitive Foundation)
Imagine driving a vintage 1970s automobile equipped with an analog push-button car radio on the dashboard.
+-------------------------------------------------------------+
| CAR STEREO DASHBOARD |
| |
| [ AM 540 ] [ AM 720 ] [ FM 89.9 ] [ FM 98.1 ] |
| ( ) ( ) (*) ( ) |
| ^ |
| Currently Depressed |
+-------------------------------------------------------------+
Inside that physical stereo was a mechanical latch bar. When you pressed the button for FM 89.9, the mechanical bar snapped that button down and simultaneously popped out whichever preset station had been pressed before. You could never listen to two radio stations at the exact same momentโthe physics of the mechanical latch made choices strictly mutually exclusive.
This mechanical car radio is the exact namesake for HTML's <input type="radio">.
When you present users with a question where exactly one answer must be chosen from a small list of options (e.g., shipping speed, billing frequency, gender marker, payment gateway), radio buttons are the gold standard UI pattern.
Technical Deep Dive & Specifications
Mutual Exclusivity Mechanics
In the WHATWG HTML standard, radio buttons are bound together by sharing the exact same name attribute within their form owner.
+-------------------------------------------------------------------------------+
| RADIO GROUP STATE TRANSITION |
+-------------------------------------------------------------------------------+
Initial State (name="shipping"):
Option A (Standard Ground): [ checked = true ] (*)
Option B (Priority Air): [ checked = false ] ( )
Option C (Overnight): [ checked = false ] ( )
User Clicks Option C:
Browser Engine:
1. Finds all <input type="radio"> sharing name="shipping" in current form.
2. Iterates over group: sets Option A.checked = false, Option B.checked = false.
3. Sets Option C.checked = true.
4. Dispatches 'change' event on Option C (and Option A loses selection).
Payload Serialized: "shipping=overnight" (Only 1 pair submitted!)
Keyboard Navigation & Roving Focus
Radio buttons implement a specialized keyboard navigation model known in accessibility specifications as the Roving Tabindex Pattern:
Keyboard Interaction Architecture:
User presses [ Tab ]:
-> Focus moves directly into the radio group.
-> If an option is checked, focus lands ON the checked option.
-> If NO option is checked, focus lands on the FIRST radio option.
User presses [ Arrow Down ] or [ Arrow Right ]:
-> Focus AND selection immediately shift to the NEXT radio button.
-> The newly focused radio is automatically checked.
User presses [ Arrow Up ] or [ Arrow Left ]:
-> Focus AND selection immediately shift to the PREVIOUS radio button.
-> Cycles from top to bottom (wraps around).
User presses [ Tab ] again:
-> Focus LEAVES the radio group entirely and moves to the NEXT form control.
| Key / Gesture | WAI-ARIA Standard Action |
|---|---|
| Tab | Enters the group (focusing the selected option) or exits the group. |
| Down Arrow / Right Arrow | Selects and focuses the next radio button in the group (cycles to first if on last). |
| Up Arrow / Left Arrow | Selects and focuses the previous radio button in the group (cycles to last if on first). |
| Space | Selects the currently focused radio button if none was selected. |
The Semantic Structure: <fieldset> and <legend>
Because individual radio buttons only have short labels (e.g., "Standard ($5.00)"), screen reader users tabbing directly into the field would not know the context of the question without a grouping container.
The W3C Accessibility Guidelines (WCAG 2.1 SC 1.3.1 & SC 3.3.2) require grouping related radio buttons using <fieldset> with an explicit <legend>:
<fieldset>
<!-- The legend acts as the accessible question title for screen readers -->
<legend>Choose Delivery Speed</legend>
<div>
<input type="radio" id="ship-std" name="shipping_method" value="ground" checked>
<label for="ship-std">Standard Ground (3โ5 Business Days)</label>
</div>
<div>
<input type="radio" id="ship-exp" name="shipping_method" value="express">
<label for="ship-exp">Express Air (2 Business Days)</label>
</div>
</fieldset>
When a screen reader focuses ship-exp, it reads: "Choose Delivery Speed, Express Air, radio button, checked, 2 of 2".
The "Un-uncheckable" Quirk & Initial Defaults
Unlike checkboxes (which can be toggled on and off repeatedly), a radio button cannot be unchecked by clicking it a second time. Once an option is chosen, the user can only change their choice to a different radio button in the same group.
Checkbox: Click [X] -> Click [ ] -> Click [X] (Toggleable)
Radio: Click (*) -> Click (*) -> (*) (Permanent once selected)
[!IMPORTANT] Always provide an explicit
checkedattribute on the most common, sensible default option in your radio group. If you do not provide a default and the user clicks any option by mistake, they cannot revert to an empty state without resetting the entire form.
๐ป Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 50โ52 (
<fieldset>&<legend>): Groups the mutually exclusive controls into a cohesive semantic unit and defines the accessible group title. - Lines 55โ68 (Option 1): Defines the default active selection with
checked. Both the input and label are linked usingfor="ship-standard"andid="ship-standard". - Lines 70โ96 (Options 2 & 3): Options sharing
name="shipping_tier". Clicking or using arrow keys to select either option will automatically deactivate Option 1. - Submission Payload: When submitted with Priority Air selected, the HTTP POST body contains strictly:
shipping_tier=priority.
Expected Browser Render Output
+-----------------------------------------------------------+
| Select Shipping Method |
| |
| +-------------------------------------------------------+ |
| | (*) Standard Delivery โ $4.99 | |
| | Estimated delivery in 4โ6 business days | |
| +-------------------------------------------------------+ |
| +-------------------------------------------------------+ |
| | ( ) Priority Air โ $12.99 | |
| | Guaranteed delivery in 2 business days | |
| +-------------------------------------------------------+ |
| +-------------------------------------------------------+ |
| | ( ) Overnight Priority โ $24.99 | |
| | Next business day delivery by 10:30 AM | |
| +-------------------------------------------------------+ |
| |
| [ Continue to Payment ] |
+-----------------------------------------------------------+๐๏ธ Hands-On Exercise
๐ฏ The Challenge: Build an Accessible Subscription Plan Selector
Instructions:
- Construct an accessible subscription plan selector inside a
<form>using semantic<fieldset>and<legend>tags. - The legend should read: "Choose Your Subscription Plan".
- Create three distinct radio options sharing
name="subscription_plan":- Starter Tier:
value="starter", price$9/mo, single user. - Professional Tier:
value="pro", price$29/mo, up to 10 users (pre-selected by default usingchecked). - Enterprise Tier:
value="enterprise", price$99/mo, unlimited users.
- Starter Tier:
- Add a
requiredattribute to the radio group to guarantee form validation. - Provide accessible
<label>markup so users can click anywhere on the plan description to trigger selection.
๐ Starter Code Sandbox
โ ๏ธ Common Pitfalls
- Using Checkboxes for Mutually Exclusive Questions: Using
<input type="checkbox">for questions like "Select Gender" or "Select Shipping Speed" is a serious UX flaw because users can check multiple boxes, creating contradictory data. - Forgetting
<fieldset>and<legend>: Placing bare radio buttons without a<fieldset>makes it impossible for screen reader users to understand what category the radio buttons belong to when cycling with arrow keys. - Different
nameAttributes on Same Group: If you writename="plan1",name="plan2", andname="plan3", the browser considers each input an independent group, breaking mutual exclusivity completely.
๐ก Pro Tips
- Modern CSS
:has()for Interactive Cards: Use.card:has(input[type="radio"]:checked)to style entire surrounding containers without writing JavaScript click listeners. - Arrow Key Navigation Testing: Always verify your radio buttons using pure keyboard navigation. Tabbing should enter the group once, arrow keys should cycle selections, and tabbing again should leave the group.
๐ Key Takeaways
<input type="radio">controls enforce mutually exclusive selection where only one option can be chosen at a time.- All radio buttons participating in the same selection group must share the exact same
nameattribute. - Keyboard navigation uses the Roving Focus Pattern: Tab enters/leaves the group, while Arrow Keys change selection.
- Radio groups should always be wrapped in a semantic
<fieldset>with an explanatory<legend>. - Unlike checkboxes, once a radio button is selected, it cannot be deselected by clicking it again; it can only be switched to another radio.
- --