Everything’s More Complicated in Groups: Required Groups

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:

A fieldset with the group label "Is this purchase a gift?" with yes and no radio buttons.

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.

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:

The text "All fields marked with an asterisk (*) are required" above a First Name field marked with an asterisk.

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:

The text "All fields marked with an asterisk (*) are required" above a fieldset with the group label "Date of Birth" followed by a "Month" select, "Day" number input and "Year" number input. All have asterisks by their labels.

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:

After the key explaining the asterisks, there is a select element with the label "What is your current role?" and an asterisk. There are also instructions and an error message below the field.

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 the aria-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 the aria-invalid attribute
  • The error message is also associated with the <select> in error using the same aria-describedby attribute

    • The value of the aria-describedby is the values of the id attributes for both the element containing the instructions and the element containing the error message, separated by whitespace
  • I will also point out that the <select> has an accessible name through for/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:

After the key communicating that asterisks mark required fields, there are two check boxes, both with asterisks by their labels. One says "I agree to the terms and conditions" and the other says "I have read the privacy policy".

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:

After the key that says that asterisks mark required fields, there is a fieldset with the group label "How would you like us to remind you of your appointment?" Below are three checkboxes for email, phone call, and text.

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 a radiogroup role to a field set (which already has an implicit group 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 an aria-labelledby attribute to reference the visual group label.
  • Add aria-required="true" to the element with role="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:

Under the key saying that asterisks mark required fields is a fieldset with the group label "Is this purchase a gift?" followed by an asterisk. There are radio buttons for yes and no.

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:

A fieldset with the group label "What is your current role" followed by radio buttons for developer, designer, content manager, project manager, and leadership. The radio button for "developer" is selected.

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.

Under the text "All fields marked with an asterisk (*) are required" is fieldset with the group lable "Is this purchase a gift?" followed by an asterisk. There are yes and no radio buttons.

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:

After the key that says that asterisks mark required fields, there is a fieldset with the group label
"How would you like us to remind you of your appointment? (You must pick at least one)." Below are three checkboxes for email, phone call, and text.

(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" and aria-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.

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!

Categories: Technical
Tags:

About Alicia Evans

Alicia Evans has been with TPGi since January 2022 and has been working in digital accessibility since 2017. Previously, Alicia spent several years working in accessibility and advocacy through nonprofits in New York City and Southern California.

Comments

Add Your Comment