For those of us who are introverts or have social anxiety, this concept is not a surprise — everything is more complicated in groups. Deciding where to eat as one person is easy. If you try to decide on where to eat as a group, be prepared to eat dinner late with your favorite hangry friends. But getting together in groups can be a great thing, worth the extra planning and complexity. This is true for both social situations and code.
Sometimes accessibility engineers forget about the nuance of groups when we teach accessible forms or provide general guidance. As in life, there is more nuance to groups. When developers encounter this nuance for the first time, they can feel overwhelmed.
But you don’t have to feel overwhelmed! In this post, I’ll address one way in which groups are different from individual fields: How to indicate when groups as required. Using HTML, a little bit of ARIA , and visual indicators, this post will explore how to communicate a required status for groups.
Groups — General Information
There are already great articles about grouping related fields, like Fieldsets, Legends and Screen Readers again. But just so we’re all on the same page, here’s the basic concept. When you have related fields, they need to be grouped. Here are some examples of related fields:
- A group called “Date of Birth” might have fields for “month”, “day”, and “year”.
- A group called “Is this purchase a gift?” might have two radio buttons, “Yes” and “No”.
- A group called “Check all symptoms that apply” might contain checkbox responses for “headache”, “dizziness”, and “fatigue”.
All these examples show related fields that rely on the grouping information to make full sense of the individual fields. “Yes” doesn’t make sense on its own, you have to know that the question is “Is this purchase a gift?”. “Year” relies on “Date of Birth” to provide the full context.
In HTML, grouping is simple. Use a <fieldset>
element as the parent of all the related controls. Then make sure that a <legend>
is the first child of that <fieldset>
and the legend provides an accessible name for the group. Let’s look at an example.
<fieldset>
<legend>Is this purchase a gift?</legend>
<input type="radio" name="gift" value="yes" id="yes">
<label for="yes">Yes</label>
<input type="radio" name="gift" value="no" id="no">
<label for="no">No</label>
</fieldset>
This markup should look something like this:
This markup makes sure that the grouping is programmatically defined. Assistive technologies, like screen readers, will convey this grouping information to users so that the context for these controls is clear. Now a screen reader user isn’t encountering a “Yes” radio button in isolation but as part of a group. “Is this purchase a gift? — Yes” (Wording will differ between screen readers).
As usual, always reach for the HTML solution first. If you find yourself in a jam, though, there’s also an ARIA solution for grouping.
Required State — General Information
The required state of a control (or group!) needs to be indicated visually and programmatically. There’s already great information out there about designing and implementing individual required fields (including this article: Doing what’s required: Indicating mandatory fields in an accessible way). But, again, so that we’re on the same page, here’s a basic rundown:
- For all required fields, either put the word “required” in the label, or add an asterisk to it. If you use an asterisk, explain the asterisk at the top of the form. For example, “Required fields are marked with an asterisk (*).”
- If all fields are required, you can simplify this approach by writing one message at the top. Something along the lines of “All fields are required.”
- To programmatically indicate what fields are required, add
aria-required="true"
to each required field. This makes sure that assistive technologies, like screen readers, convey the required status of each field.- If you already have the word “required” in your label, use
aria-hidden="true"
to hide it. This way you’re not telling screen readers to announce the required status twice.
- If you already have the word “required” in your label, use
Ultimately, you end up with HTML that looks something like this:
<p>All fields marked with an asterisk (*) are required.</p>
...
<label for="fn">First Name<span class="req">*</span></label>
<input type="text" id="fn" aria-required="true" autocomplete="given-name">
Which will look something like this on the page:
Now visual users can see that the first name field is required, and screen readers will announce that the first name field is required.
Required Status — Groups
Things get trickier in groups (or, at least, there are fewer resources about them).
If your group doesn’t contain radio buttons or checkboxes, then maybe no change is necessary. Let’s go back to our previous example.
If there is a group “Date of Birth” that contains three fields (month, day, and year), maybe putting the required status (visual and programmatic) on each of these fields is better than trying to address the fields as one required group.
HTML:
<p>All fields marked with an asterisk (*) are required.</p>
...
<fieldset>
<legend>Date of Birth</legend>
<label for="mo">Month <span class="req">*</span> </label>
<select id="mo" aria-required="true" autocomplete="bday-month">...</select>
<label for="day">Day <span class="req">*</span> </label>
<input type="number" min= "1" max= "31" id="day" aria-required="true" autocomplete="bday-day">
<label for="year">Year <span class="req">*</span> </label>
<input type="number" id="year" min= "1900" max= "2099" aria-required="true" autocomplete="bday-year">
</fieldset>
Screenshot:
Even though these fields are grouped, each field in the group is required. So, including requirement information for each text input makes sense.
Radio Buttons and Checkboxes
Where requirement status gets more complex is when you try to add a required status to groups of radio buttons and checkboxes.
Let’s take, for example, a group of radio buttons requesting the user’s current role at their job.
The first thing you should ask yourself is, “Is a radio group the right tool for this job?” For example, would a simple <select>
element be a better solution here? Requirement information, error messages, invalid status, and further instructions all work very cleanly with a <select>
element.
Look at this example of a required <select>
element submitted with errors. So clean! No fuss! Problem solved!
<p>All fields marked with an asterisk (*) are required.</p>
...
<label for="company-role">What is your current role?<span class="req">*</span></label>
<select id="company-role" name="current-role" aria-required="true" aria-describedby="instruction-text error-text" aria-invalid="true">
<option value="" disabled>--Choose a role--</option>
<option value="dev">Developer</option></p> <blockqu
<option value="des">Designer</option>
<option value="cm">Content Manager</option>
<option value="pm">Project Manager</option></p> </blockqu
<option value="lead">Leadership</option>
</select>
<p id="instruction-text">Pick the option that best reflects your role.</p>
<p id="error-text">Error: You must select a role before the form can be submitted</p>
It looks something like this:
Let’s break this down a little further:
- The
aria-required
attribute is set directly on the<select>
element, which makes it clear that selecting an option is required - The
<select>
has instructions that are always visible. They say “Pick the option that best suits your role.” These instructions are associated with the<select>
using thearia-describedby
attribute. Here’s more information about aria-describedby - Because the form has been submitted in this scenario and no selection has been made, the
<select>
is marked as invalid, using thearia-invalid
attribute - The error message is also associated with the
<select>
in error using the samearia-describedby
attribute- The value of the
aria-describedby
is the values of theid
attributes for both the element containing the instructions and the element containing the error message, separated by whitespace
- The value of the
- I will also point out that the
<select>
has an accessible name throughfor/id
association - Using a
<select>
element avoids the need for group semantics because instead of a group of radio buttons, you’re using one<select>
element
It looks like a lot when you break it all down, but these are all the standard implementations. The solutions are easy to look up and don’t take much to implement.
With radio button and checkbox groups, what you lose is clarity about what to do and certainty about what will work best for your users. But let’s say you’ve already decided that what you really need is a radio button group or checkbox group. In that case, you’ll need to read on.
At this point, your instinct might be telling you, “Slap an aria-required
on the <fieldset>
and call it a day!” And I wish it were that simple. Unfortunately, aria-required
isn’t a valid attribute for the <fieldset>
element. Neither is required
. So, what can you do?
One option is to make every control within the group required. If you can’t make the <fieldset>
required, then why not make every control required? Screen readers announce this as you’d expect, so you can be pretty sure that screen reader users will be informed that these fields are required.
But it’s not exactly the same, is it? Consider the difference between these two checkbox examples:
Two required checkboxes:
<p>All fields marked with an asterisk (*) are required.</p>
...
<input type="checkbox" id="terms" name="terms" value="terms" aria-required="true">
<label for="terms">I agree to the terms and conditions.<span class="req">*</span> </label>
<input type="checkbox" id="pp" name="privacy" value="privacy" aria-required="true">
<label for="pp">I have read the privacy policy.<span class="req">*</span></label>
These would look something like this:
Checkboxes in a required group:
<p>All fields marked with an asterisk (*) are required.</p>
...
<fieldset>
<legend>How would you like us to remind you of your appointment?<span class="req">*</span></legend>
<input type="checkbox" id="email" name="contact" value="email" aria-required="true">
<label for="email">Email: alicia@email.com</label>
<input type="checkbox" id="call" name="contact" value="phonecall" aria-required="true">
<label for="call">Phone call: (555) 555 - 5555</label>
<input type="checkbox" id="text" name="contact" value="text" aria-required="true">
<label for="text">Text: (555) 555 - 5555</label>
</fieldset>
These look more like this:
In the first example, there are two individual checkboxes that are each required to continue. In the second, the assumption is that at least one checkbox needs to be selected to continue (although the number of required checkboxes is not clear). Because each checkbox is announced as required by screen readers, it might be unclear to screen reader users if every checkbox must be checked (like in the first example).
Marking every single control as required indicates a required status, but at best it causes too many screen reader announcements (poor UX), and at worst it’s straight-up confusing.
While this is a tempting solution, it’s not the best way to indicate required groups. So, let’s explore more options. For these, I’ll be separating strategies by type. First, we’ll look at radio groups, then checkbox groups.
Radio button groups
I’m going to do that thing I sometimes dislike in blogs where I describe several solutions before getting to what I consider my best recommendation. If you don’t want to go on this journey with me, it won’t hurt my feelings. You can skip to the last recommendation.
But for those of you who are still with me, I’m going to show you three options for making radio groups required that can all be good solutions. The last one is where I tend to land, but maybe one of the other three works better for you, and it’s always a good idea to do user testing and see what works best for your users.
Option 1: Add aria-required
to an element with role="radiogroup"
While Adrian Roselli goes into more depth on this technique than I will here, here are the basics:
- Add
role="radiogroup"
to the group container. If you’re already using a<fieldset>
element, then that’s the container. Adding aradiogroup
role to a field set (which already has an implicitgroup
role) may feel redundant, but it’s necessary to make sure that the required status is announced properly- Make sure your group is named. (This isn’t necessary for the required status, it’s just part of good grouping)
- If you are already using the
<fieldset>
element, then make sure there is a<legend>
that is the first child of that<fieldset>
element - If you’re using a
<div>
or something else generic, then add a name using anaria-labelledby
attribute to reference the visual group label.
- If you are already using the
- Make sure your group is named. (This isn’t necessary for the required status, it’s just part of good grouping)
- Add
aria-required="true"
to the element withrole="radiogroup"
- Make sure your visual indication of requirement (for example, an asterisk) is applied to the visual group label.
Here’s an example:
<p>All fields marked with an asterisk (*) are required.</p>
<fieldset role="radiogroup" aria-required="true">
<legend>Is this purchase a gift?<span class="req">*</span></legend>
<label for="yes">Yes</label>
<input type="radio" id="yes" name="gift" value="yes">
<label for="no">No</label>
<input type="radio" id="no" name="gift" value="no">
</fieldset>
This markup ends up looking something like this:
With this markup, most screen readers announce the radio button group as required. But the required status won’t be announced with TalkBack (latest version as of May 2025). Because of this, I tend to look for different strategies.
Option 2: Pre-select a radio button
It makes it clear that a radio group is required if you already have a radio button selected. In that case, there’s no need to mark it as required.
The problem here is that you might be funneling users toward one radio button over others. At worst that can be a kind of deceptive pattern, but even at best you might not get the results or outcomes you want. For our previous example, you might get a lot more people reporting themselves as “developers” than what is actually the case:
<fieldset role="radiogroup">
<legend>What is your current role?</legend>
<input type="radio" id="dev" name="role" value="developer" checked>
<label for="dev">Developer</label>
<input type="radio" id="des" name="role" value="designer">
<label for="des">Designer</label>
<input type="radio" id="cm" name="role" value="content">
<label for="cm">Content Manager</label>
<input type="radio" id="pm" name="role" value="pm">
<label for="pm">Project Manager</label>
<input type="radio" id="lead" name="role" value="leadership">
<label for="lead">Leadership</label>
</fieldset>
That would look something like this:
This is still a pretty standard pattern, and I’m not mad at it when I find it in the wild. Still, my preference is for Option 3.
Option 3: Be explicit with requirements
This technique is simple. Just add “required” directly in the legend. If you already have the asterisk there, you can visually hide the required text. Like this:
.visually-hidden {
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
<p>All fields marked with an asterisk (*) are required.</p>
<fieldset role="radiogroup">
<legend>
Is this purchase a gift?<span class="req">*</span>
<span class="visually-hidden">Required</span>
</legend>
<input type="radio" id="yes" name="gift" value="yes">
<label for="yes">Yes</label>
<input type="radio" id="no" name="gift" value="no">
<label for="no">No</label>
</fieldset>
The visually-hidden text won’t appear visually.
A benefit of this method is that the “required” status will be available to everyone, regardless of screen reader / browser combination. You’ve written it out in text. It’s explicit. It’s clear. Hooray!
It’s also very similar to the checkbox solution.
Checkbox Groups
Checkbox groups are even trickier than radio buttons because fulfilling the required status is more nebulous. Does the user have to check one checkbox to fulfill the requirement? Two? Three?
How then do you programmatically let users know what the requirements are?
Much like with radio buttons, you can put instructions directly in the legend or group label. This lets users know exactly what the expectations are. Make this visible so that all users can benefit.
For example:
<fieldset>
<legend>How would you like us to remind you of your appointment? (You must pick at least one):</legend>
<input type="checkbox" id="email" name="contact1" value="email">
<label for="email">Email address: alicia@email.com</label>
<input type="checkbox" id="call" name="contact2" value="call">
<label for="call">Phone call: (555) 555 - 5555</label>
<input type="checkbox" id="text" name="contact3" value="text">
<label for="text">Text: (555) 555 - 5555</label>
</fieldset>
This would look something like this:
(Note that if there is only one required checkbox, like “I have read and accept the terms and conditions”, you can treat this exactly like any other single field as shown in this section.)
Key Takeaways
And there you have it. This article was much longer than I intended, so let’s wrap up with some key takeaways:
- Grouped controls might not work the same as individual controls when applying a required status. This is especially true for groups of radio buttons or checkboxes.
- Before trying to make one of these groups required, consider if there is a better solution. For example, maybe a
<select>
would work better than radio buttons for this. - If your group contains text inputs, text areas, and/or combo boxes, it might be best to mark these individually as required.
- If a group of checkboxes or radio buttons is required, marking each control as required will make sure it’s announced by screen readers, but the required status might be confusing. This is not advised.
- If a group of radio buttons or checkboxes is required, put the visual indication of required status (the word “required” or an asterisk, typically) in the group label (usually the
<legend>
). - If a group of radio buttons or checkboxes is required, add the requirement information to the group label. For radio buttons, add the word “required”. This can be visually hidden if you’re using an asterisk already. For a group of checkboxes, make the requirement information visible and specify how many checkboxes the user needs to check to fulfill the requirement.
- For a group of radio buttons, you can alternatively add
role="radiogroup"
andaria-required="true"
to the group container (like the<fieldset>
), but this won’t be announced by all screen reader / browser combinations. - For a group of radio buttons, you can alternatively pre-select one of the radio buttons.
- For a group of radio buttons, you can alternatively add
Sometimes groups can be a pain, but they can also be necessary. In code and in life, it helps to make a plan before moving forward. So, bookmark this page for the next time you need it, and share it with your favorite group of designers or developers!
Comments