Checkbox
The little box you tick to agree, pick several items or select a whole list at once. Learn its states (including "mixed"), when it beats a switch or a radio button, and the consent and keyboard bugs testers find in it.
“I agree to the Terms.” “Remember me.” “Leave at the door.” “Select all.” You tick boxes like these dozens of times a week, usually without thinking. That’s exactly why checkbox bugs are so sneaky: the box you can’t tap, the “Select all” that lies about how many emails you’re deleting, the marketing consent that was ticked for you.
A checkbox is also where UI testing meets legal testing. Let’s learn both sides.
What is a checkbox?
A checkbox is a small square the user ticks to say “yes, include this”. Each checkbox is independent: ticking one doesn’t untick the others. And, unlike a toggle switchToggle switchAn on/off control that applies a setting immediately, like Wi-Fi or Dark mode.Open in glossary →, ticking a checkbox usually does nothing on its own. The choice is applied when the user presses a button like Continue, Save or Pay.
You’ll see checkboxes used for:
- A single yes/no in a form: I agree to the Terms, Remember me, Billing address is the same as delivery.
- Picking several items from a list: cuisines in a food app filter, newsletter topics, pizza toppings.
- Bulk actions: ticking emails, photos or files, then pressing Archive or Delete.
- Consent: Send me offers by email, Share my data with partners.
Anatomy
Email me about
You can change this any time in Settings.
- 1
Group label (optional)
Says what the set of checkboxes is about. On the web it is a <legend> inside a <fieldset>.
- 2
Box (container)
Empty when unchecked, filled when checked. A dash means "mixed": some, but not all, items below are checked.
- 3
Checkmark
Shows the checked state with a shape, not only a color.
- 4
Label
Says what you agree to or pick. Tapping it must toggle the box too.
- 5
Touch target
The tappable area. The box is only about 18 px, but the target must be at least 44×44 pt (iOS) or 48×48 dp (Android).
- 6
Helper or error text
Extra info, or the error when a required box was left unchecked ("You need to accept the Terms").
The states
A checkbox has more states than “ticked or not”. Each one needs testing:
| State | Looks like | Meaning |
|---|---|---|
| Unchecked | Empty box | Not selected |
| Checked | Filled box with a tick | Selected |
| Mixed (indeterminate) | Filled box with a dash | Parent of a list where some children are checked |
| Disabled | Faded, keeps its tick or not | Can’t be changed right now |
| Error | Red box + error text | A required box was left unchecked |
| Focused | Ring or highlight around it | Keyboard focus is here (web, external keyboards) |
Checkbox vs toggle switch vs radio button
The fastest way to spot a wrong component is to ask two questions: can the user pick more than one? and does it apply immediately?
| Component | How many? | When it applies | Example |
|---|---|---|---|
| Checkbox | Zero, one or many | On Submit / Save | Toppings, “I agree to the Terms” |
| Toggle switch | One setting, on/off | Immediately | Dark theme, Wi-Fi |
| Radio buttons | Exactly one of a few | Usually on Submit | Standard / Express delivery |
| Segmented control | Exactly one of 2–5 | Immediately | Day / Week / Month |
See also the radio button, toggle switch and segmented control pages.
Designs & variants
The idea is the same everywhere, but the platforms disagree on what it looks like. iOS doesn’t even have one.
Every checkbox here works. Click the label text too, and try Tab + Space.
Material 3 checkbox
AndroidAn 18 dp box with rounded corners inside a 48 dp touch target. A round highlight (state layer) appears on hover, press and focus.
Native web checkbox
WebA plain <input type="checkbox"> with a <label>. It looks slightly different in every browser, but keyboard, label clicks and screen readers work for free.
Custom web checkbox
WebStyled to match the brand. Built on a hidden real input, so it still works with the keyboard. Many custom checkboxes are just a <div>, and that's where the bugs live.
Cuisines
iOS: checkmarks in a list
iOSiOS has no checkbox control. Multi-select lists show a blue checkmark on the right of each selected row, and the whole row is tappable.
- Unchecked
- Checked
- Mixed
- Focused
- Disabled
- Disabled on
- Error
- Error on
All the states
Test each one, in light and dark mode. Disabled boxes keep showing their value. "Mixed" (indeterminate) only appears on a parent checkbox whose children are partly checked.
“Select all” and the mixed state
A parent checkbox that controls a list is one of the most bug-prone patterns in any app. The rule is simple, and it must hold after every click:
What the parent should show
2/5 selected · aria-checked="mixed"
Try: uncheck one email, check them all one by one, click the parent while it shows a dash, archive everything. The count, the parent and the Archive button must always agree.
What happens when you click a parent that shows a dash? Most apps select all, some clear all. Both are fine if the behavior is consistent and matches the spec. What’s never fine: the parent saying “all” when it isn’t, or a count that disagrees with the ticks.
When to use it
A checkbox is the right choice when:
- The user can pick any number of options, including none: Wi-Fi, Parking, Pool.
- There’s one optional yes/no in a form: Remember me, Send me a copy.
- The user must actively agree to something before continuing: I accept the Terms.
- The choice is applied later, when the user presses a button.
Good and bad uses
Checkboxes in a form with Save
Email me about
Nothing changes until Save preferences. That’s what checkboxes communicate.
Switches in a form with Save
Email me about
Switches promise an instant effect. Here users can’t tell if the change already happened or needs Save.
The whole row is the hit area
Tap target: the whole row (box + label).
Tapping the label ticks the box. Easy on a phone, and required for accessibility.
Only the tiny box reacts
Tap target: a 14 px box. The text does nothing.
A 14 px target on a touch screen. Users tap the text, nothing happens, and they think the form is broken.
Marketing consent left unticked
The user has to opt in actively. Terms and marketing are separate boxes.
Marketing consent pre-ticked
The user “agreed” to offers without doing anything. In many countries this doesn’t count as consent.
Long label with a hanging indent
The box lines up with the first line and the text wraps under itself.
Text wraps under the box
I confirm that I am over 18 and that the passenger names match the passports exactly
Hard to scan, and with large text it’s unclear which box belongs to which sentence.
Also avoid checkboxes when the user must pick exactly one option (use radio buttons) and for a single setting that applies immediately (use a switch).
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Label not clickable | Tapping the text does nothing, only the tiny box reacts | Medium |
| 2 | “Select all” out of sync | Parent stays ticked after you untick one item, never shows mixed, or the count is wrong | High (if it drives a bulk delete) |
| 3 | Pre-ticked consent | Marketing or data-sharing boxes are ticked by default | High (legal risk) |
| 4 | Not keyboard operable | A custom checkbox can be focused but Space doesn’t toggle it, or it can’t be focused at all | High (for affected users) |
| 5 | Required box not enforced | The form submits without “I agree”, or only the browser checks it and the server accepts the request anyway | High |
| 6 | Error not explained | A required box turns red with no text, or the error isn’t announced to screen readers | Medium |
| 7 | State lost | Ticks disappear after going Back, rotating, or a validation error elsewhere on the form | Medium |
| 8 | Disabled but clickable | A greyed-out box can still be toggled by tapping or with the keyboard | Medium |
| 9 | Long labels break | Text wraps under the box, gets truncated, or overlaps at large font sizes | Low–Medium |
| 10 | Silent screen reader | VoiceOver / TalkBack doesn’t say “checkbox”, “checked” or the label | High (for affected users) |
Find the bug
This travel site’s sign-up flow has six checkbox bugs. Explore it like a tester:
- Click the text next to each box, not just the box.
- Try to continue without ticking anything. Read the screen carefully.
- On step 2, use Select all, then untick one topic. Watch the count.
- Use only the keyboard: Tab to each checkbox and press Space.
- Go to step 2 and press Back. Is everything as you left it?
🐞 Bug hunt: Travel site sign-up
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
Use the mouse and the keyboard (Tab, Space).
- 1
Hint: Click the words "I agree to the Terms…" instead of the box.
- 2
Hint: Look at the second checkbox before touching anything, then check the summary at the end.
- 3
Hint: On step 2, press Tab until a topic is focused, then press Space.
- 4
Hint: Tick Select all, then untick "Car rental". Look at the parent and the count.
- 5
Hint: Enter a valid email, leave the Terms unticked and press Continue.
- 6
Hint: Tick Terms, untick marketing, continue, then press Back.
How to test it
Checklist
Checkbox test checklist
0 of 13 checked
Example test cases
TC-CB-001Account cannot be created without accepting the Termsnegativehigh
Steps
- Open the sign-up page
- Enter a valid email
- Leave "I agree to the Terms" unticked
- Press Continue
Expected result
The user stays on the page. An error text "Accept the Terms to continue" appears next to the checkbox and is announced by the screen reader. No account is created.
TC-CB-002Server rejects sign-up without Terms acceptancenegativehigh
Preconditions
- Proxy tool or DevTools available
Steps
- Fill in the form and tick the Terms
- Intercept the sign-up request
- Remove or set the termsAccepted field to false
- Send the request
Test data
termsAccepted: false
Expected result
The API responds with a 4xx error and no account is created.
TC-CB-003Marketing consent is off by default and respectedpositivehigh
Steps
- Open the sign-up page
- Check the marketing checkbox without touching it
- Complete sign-up without ticking it
- Open the account's email preferences
Expected result
The marketing box is unticked on load, and the account shows marketing emails as off.
TC-CB-004Parent checkbox shows mixed statepositivemedium
Preconditions
- Inbox has 5 emails
Steps
- Tick "Select all"
- Untick one email
Expected result
The parent shows a dash (mixed), the counter reads "4 selected", and Archive acts on exactly 4 emails.
TC-CB-005Ticking every item individually checks the parentedgelow
Preconditions
- Inbox has 5 emails, none ticked
Steps
- Tick all 5 emails one by one
Expected result
The parent becomes fully checked and the counter reads "5 selected".
TC-CB-006Label tap toggles the checkboxpositivemedium
Steps
- Tap the text "Remember me" (not the box)
- Tap it again
Expected result
The box is ticked after the first tap and unticked after the second.
TC-CB-007Checkbox can be operated with the keyboardpositivemedium
Preconditions
- Web, desktop browser
Steps
- Press Tab until the checkbox is focused
- Press Space twice
Expected result
A visible focus ring appears. Space ticks and then unticks the box. The page does not scroll.
TC-CB-008Ticks are kept after navigating backedgemedium
Steps
- Tick Terms and untick marketing on step 1
- Continue to step 2
- Press Back (in-app and with the browser / system back)
Expected result
Step 1 shows exactly the choices the user made: Terms ticked, marketing unticked.
Tip: click a test case to expand it.
Accessibility
- Use the real thing if you can. A native
<input type="checkbox">gets keyboard support, form submission and screen reader announcements for free. Inspect custom checkboxes with DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary →. See ARIAARIAHTML attributes that tell assistive technology what custom elements are and what state they're in.Open in glossary →.Dev note
Custom checkboxes needrole="checkbox",aria-checked(true,falseormixed),tabindex="0"and a Space key handler. - Connect the label. The label makes the text clickable and gives the checkbox its spoken name. On mobile, make the whole row the touch targetTouch targetThe area of the screen that responds to a tap, which should be large enough for a finger.Open in glossary →: 44×44 pt on iOS, 48×48 dp on Android.
Dev note
<label for>(or wrapping the input in a<label>). - Group related boxes. A set like “Email me about” belongs in a group, so screen readersScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary → read the question before the options.
Dev note
A<fieldset>with a<legend>. - Announce errors. A required box needs a visible error text, and the error should be announced. A red outline alone fails users who can’t see it.
Dev note
Link the error witharia-describedby; announce it, for example, withrole="alert". - Not color alone. The tick or dash shape must show the state. The box border needs at least 3:1 contrast against the background (WCAGWCAGThe Web Content Accessibility Guidelines, the main international standard for accessibility.Open in glossary →).
- Keyboard (web): Tab moves between checkboxes (each one is its own tab stop, unlike radio buttons), and Space toggles. Enter usually submits the form instead.
Mobile vs web
- No native checkbox. Multi-select uses checkmarks in lists, selection circles in edit mode, or custom-drawn boxes.
- For custom boxes, VoiceOver should say something like “Leave at the door, button, selected” or “checkbox, checked” depending on how it’s built. Silence or just “button” is a bug.
- Test at the largest Dynamic Type sizes: long consent labels must wrap, not truncate.
- Check that the whole row is tappable, not just the drawn box.
- Material 3 checkboxes have an 18 dp box inside a 48 dp touch target, and support a tri-state (indeterminate) mode.
Dev note
Checkbox(Compose) orCheckBox(Views). - In lists, the whole row is usually tappable. Test tapping the text.
Dev note
ComposeModifier.toggleableon the row. - TalkBack announces “Checked” / “Not checked” plus the label. Test with font scale, display size and dark theme.
- Watch for state lost after rotation or process death (Don’t keep activities in Developer options helps).
- Native
<input type="checkbox">looks different in each browser. Custom ones are very common, so check keyboard and ARIA. - The
requiredattribute and disabled buttons are client-side only. Make sure the server validates consent too. - Test the browser Back button and a page reload: are ticks restored or reset as the spec says?
- Check
indeterminateparents, bulk actions and counts on long, paginated lists (“Select all 50 on this page” vs “all 1,240”).
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.In a "Choose your pizza toppings" form with an Add to cart button, which control fits best?
2.You tick "Select all" in an inbox, then untick one email. What should the parent checkbox show?
3.A sign-up form has "Send me offers by email" ticked by default. What do you do?
4.A custom checkbox can be focused with Tab, but Space does nothing. Who is affected most?
5.The Continue button stays disabled until "I agree to the Terms" is ticked. Is that enough?
Key takeaways
- Checkboxes are independent and usually apply on Submit. Exactly one choice → radio buttons. Instant on/off → toggle switch.
- States to test: unchecked, checked, mixed, disabled, error, focused, in light and dark mode.
- A “Select all” parent must always match its children: none = unchecked, some = mixed, all = checked, and counts must agree.
- The label is part of the target. Tapping the text must toggle the box.
- Consent boxes must not be pre-ticked, and required boxes must be enforced on the server with a clear, announced error.
- Custom checkboxes need Space to work, plus the right role and state for screen readers.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡