Use Legend and Fieldset
It’s 2022 and people are still afraid to use <fieldset>
and <legend>
. I understand the layout challenges can be frustrating, but swapping to an ARIA group
role will result in a more inaccessible experience.
A Solution
Try this:
<fieldset>
<legend>Choose</legend>
<div aria-hidden="true">Choose</div>
[…]
</fieldset>
legend:not(:focus):not(:active) {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
width: 1px;
height: 1px;
white-space: nowrap;
}
If the <legend>
is never at risk of receiving focus (through some rogue script or whatever), then dump the :not(:focus):not(:active)
from the selector.
I took three existing examples of mine and adjusted them to use this approach.
The unmodified original is from my post Under-Engineered Dependency Questions. It uses CSS flex (debug of this flex example):
See the Pen <legend> with `flex` by Adrian Roselli (@aardrian) on CodePen.
I made this one on a lark and it has no associated post. There is also a debug version of this grid-template-columns
example.
See the Pen <legend> with `grid-template-columns` by Adrian Roselli (@aardrian) on CodePen.
I grabbed the scrolling example from my post Under-Engineered Multi-Selects to try this with position: sticky
(which also has a debug version):
See the Pen <legend> with `grid-template-columns`, `position: sticky` by Adrian Roselli (@aardrian) on CodePen.
Why I Suggest This
The rest of this post goes into more detail on layout and accessibility challenges, along with some alternatives. You can stop reading here and hope my example does what it claims, or you can read more to decide if there is a better approach based on what I present.
Layout
Essentially the <legend>
element does not participate in <fieldset>
’s layout box. This is not new information. Folks have been frustrated by this for years.
Looking at the grid bugs and flexbox bugs trackers, we see that there have been issues with fieldsets that go beyond the intentional layout model:
- Chromium
- Issue 375693: [css-flex][css-grid] Flexbox/grid layout model does not work on fieldset elements, opened May 21, 2014, fixed August 14, 2020, regressions / new issues logged after.
- Issue 1229458: Flexbox (column) with min-height broken in fieldset, opened July 14, 2021.
- Issue 1298079: Fieldset on print not using display flex, opened February 16, 2022.
- WebKit
- Bug 169082 - Enable fieldsets to be flex boxes and grids, opened March 2, 2017, fixed March 6, 2017.
That covers layout, but what about how this information is exposed to users?
Accessibility Bugs
I made a demo to compare how a <fieldset>
/ <legend>
construct compares with a group
/ aria-labelledby
construct. The debug view, for your own testing.
See the Pen Untitled by Adrian Roselli (@aardrian) on CodePen.
- NVDA with Firefox: both work
- JAWS with Chrome: both work (though JAWS announced the
group
role as “grope”) - Narrator with Edge: both work
- VoicerOver/macOS with Safari: both work
- TalkBack with Chrome or Firefox: both are broken
- VoiceOver/iOS with Safari: the
group
is broken
Overall an (expected) improvement from when Steve Faulkner wrote about it in 2007 and tracking with more recent PowerMapper results. It corresponds with my testing from 2019 as well.
TalkBack on Android
Providing group labels has always been a problem with TalkBack, no matter the technique. I cannot find related bugs, but if you have them please share.
VoiceOver on iOS
VoiceOver is the primary mobile screen reader for almost 72% of users. It is also the one I see most commonly referenced by front-end generalists (as opposed to accessibility practitioners).
A stroll through Accessibility Support shows us that group
support is poor on iOS (and Android). In other words, it is effectively non-existent. Using it is no different than not. I found no bugs detailing this, but it appears to be well-enough known by library developers.
Conversely, Bug 147466 - Voiceover on iOS not announcing legend associated with a fieldset when browsing in safari, opened July 2015 (updated February 2021) notes that <legend>
is not announced when in a landmark that is not in a form or region, nor apparently when it is a form in a main region.
What Do We Do?
Given the layout concerns and browser bugs, what are our options? Here are a few, with caveats, for you to make your own decisions.
Not Yet: Use display
Properties
I have seen folks use display: table-cell
before, but this was only effective in some browsers. It also ran the risk of Chrome applying layout-table semantics to the element, which gets handed off to the accessibility tree for screen readers.
In fact, changing display
properties has been risky for years because browsers would drop all semantics from elements. Riskiest of all was using display: contents
, even though the CSS spec has suggested it for <legend>
since at least 2017, despite knowing it has been a major accessibility bug which Apple, that last holdout, has not fixed it in Safari, despite promises in March and again in June.
Using display
properties is too risky unless you can absolutely guarantee all of your audience is using the latest browsers with the latest hardware (because in many cases hardware limits how current you can get).
No: Wrap the <legend>
I have seen a couple cases where folks wrap the <legend>
in a <div>
and then apply their layout to the <div>
.
Some quick screen reader testing will show this is not feasible since it breaks the relationship between the <fieldset>
and the <legend>
.
Meh: Use aria-describedby
You could use aria-describedby
to assign the group label to each item within the group as its accessible description. This is not its name, so it can be missed, but it will at least be exposed.
When used with a group
role, it will not give quick insight to a user when they have entered a new group, so they will need to listen the full announcement for each control if unfamiliar with the page.
Ugh: Use aria-labelledby
This is a bit riskier because you would override the accessible name of every control in the group
container. You would use a space-separated list of id
values to build it back up, concatenating the group label and the control name.
I say it is risky because it is too easy to append the wrong id
value, and is compounded when done so with generated controls and no manual screen reader testing (an automated tool cannot know if the accessible name is right or wrong, only that it is present).
It also makes for verbose controls. Every control will announce the group label and it cannot be skipped. A <fieldset>
/ <legend>
construct only announces the group label when moving into the group.
This can also make a frustrating experience for voice users. As long as the control’s visible label is part of the accessible name, you will be fine to pass Success Criterion 2.5.3 Label in Name. You will almost definitely want to make sure the group label comes after the field name, however (Technique G208 Including the text of the visible label as part of the accessible name speaks to this).
Sure: Hide the <legend>
Use <fieldset>
and <legend>
, not a group
role. Visually hide the <legend>
and replicate its content into a node in the <fieldset>
. Add aria-hidden="true"
to that node.
This approach, unfortunately, replicates content on the page, so it violates the DRY (don’t repeat yourself) principle. But that also allows you to automate it more easily since you can replicate the string (risks that I noted with aria-labelledby
apply, but only once as opposed to for each control).
This does not fix TalkBack’s lack of support for <fieldset>
, nor does it address iOS/VoiceOver’s bugs with <fieldset>
s in some kinds of regions. But it does not add more problems that come from using the group
role. This is also potentially easier to remove once <fieldset>
support is fixed — strip the duplicate node and remove the visually-hidden styles.
Am I Right?
Maybe? I do not know all wacky layout requirements and arbitrary use cases. Consider this post and its comments a form of peer review.
But please, if you have an idea and want to comment here, come with receipts — identify your code (a linked example is best), explain why it is what it is (the use case or design goal), and outline browser and assistive technology support (version numbers help).
Let’s not claim something is accessible without any insight into how it performs, what decisions were made, and what features apply.
Update: 16 May 2023
Above in this post I reference Steve Faulkner's 2007 write-up of screen reader support for <fieldset>
and <legend>
. He has just shared a new post looking at same: Fieldsets, Legends and Screen Readers again
He also looks at ARIA patterns as comparison. And his is now more current that this you are reading now. And it matches. Which is nice.
Update: 10 December 2024
The results of my testing are the same. TalkBack 15.1 is still functionally useless for group labels (ARIA or HTML). VoiceOver on iOS / iPadOS 18.1.1 still falls down on ARIA group labels and HTML group labels in a form or main region. But at least JAWS is no longer saying “grope.” So I guess yay?
9 Comments
Thanks for another fantastic post!
on my iOS 15.5 + Safari, VO doesn’t announce fieldset/legend or group+labelledby
here: https://codepen.io/WW3/pen/VwXeKdo?editors=1100
In response to .Comment out the
<main>
. The last paragraph in the VoiceOver on iOS section addresses that bug.
Another benefit of using
<fieldset>
is that it will show up in theHTMLFormControlsCollection
of the<form>
(i.e.HTMLFormElement.elements
). Recently, something I’ve experimented to remediate layout issues and maintain the benefits of using<fieldset>
is to drop the<legend>
and usearia-labelledby
on the<fieldset>
. This seems valid as<legend>
isn’t required for<fieldset>
. I’m not sure if this is what you are referring to in the section under the heading “Ugh: Usearia-labelledby
“. I haven’t tested this approach much outside of VoiceOver with Safari. I’d be interested to see how it compares.
In response to .Nathan, I forked Neil’s pen (prior comment), because it contains the construct your describe, and tested it with JAWS/Chrome and NVDA/Firefox. In those two combinations it announced the same as if the
<legend>
were there instead.I was referring to a construct like I saw with React Spectrum’s calendar control, where each field gets an accName from a compound
aria-labelledby
.
Thanks for sharing!
I’m building a design system and focusing on a11y I just stumble upon some trouble with “ elements inside “ with `display: flex` but messing around with it I found that `float: left` on the “ make it work like it should both with parent’s being `flex` or `grid`, it’s strange to have a `float: left` in my code in 2022 but its the most straightforward solution I found, lemme know what you think!
In response to .In a vacuum, there is nothing wrong with using
float
. Obviously there are risks when users scale text or shrink viewports. But if it tests well and does not create other exciting barriers, then don’t feel bad about using it.
I find that having screen readers announce legends/group labels on touch-screen devices is a bit different/less important than on desktop.
ON desktop browsers, especially in forms, screen reader users are likely to start navigating with the tab key, not using arrow keys to browse through the virtual buffer.
Since the group label is not focusable (nor should it be), this means that you have to programmatically attach it to the the group so screen readers can announce when you enter/leave the group.On touchscreen devices, however, screen reader users are not bound by the keyboard, swiping is not restricted to “focusable” elements only.
This means that they will swipe to the group label element before hitting the grouped controls (assuming the group label is at the top of the group.I would enhance the legend/group label by making it a heading (more specifically, coding the content of the legend tag as a heading).
This provides a way to further distinguish the group label element from the rest of the group.
If this is done consistently, users can easily navigate back to the group label if they’re not sure (navigating by headings).
Sure, we could attach the group label to every interactive control label, but I have found that technique verbose to the point of making the experience almost unusable in some cases.The one thing that may not be clear is the end of the grouped controls, especially when the content that comes after the group is not another group or a submit button but other form controls.
You could address this by simulating the Windows screen reader treatment of groups.
Insert two visually hidden paragraphs*a span with “Group start” + the group label at the start of the group
* a span with “group end” + group label at the end of it.(screen readers on windows typically announce “group start” + the group label when you navigate to the first focusable element in a group with the tab key, and announce “group end” before announcing the accessible name of the first focusable element after the group.
Ultimately this is what the mobile screen readers should do, but we could fake it in the meantime, if we so choose.
In response to .Minor notes…
I find that having screen readers announce legends/group labels on touch-screen devices is a bit different/less important than on desktop.
My “desktop” computer is a touch screen. My “touch-sreen devices” all have keyboards (phones and tablets). I only say this because other than macOS, all input modalities exist on all devices. Again, other than macOS.
ON desktop browsers, especially in forms, screen reader users are likely to start navigating with the tab key, not using arrow keys to browse through the virtual buffer.
In my own testing experience, “desktop” screen reader users with some experience start navigating my landmarks or headings. Your point about using Tab
for forms corresponds to my own testing experience.On touchscreen devices, however, screen reader users are not bound by the keyboard, swiping is not restricted to “focusable” elements only.
This means that they will swipe to the group label element before hitting the grouped controls (assuming the group label is at the top of the group.For the benefit of readers, users of mobile devices not using a keyboard / using only touch may absolutely encounter a group that way. Many switch to navigating by control type once entering a form.
I would enhance the legend/group label by…
I would certainly be interested in seeing the results of an A/B test. Especially as accounting for a persistent VoiceOver/iOS bug (which may be intentional given all the transparency we get from Apple) and similar TalkBack bug means making a more verbose experience for Windows, macOS, and Linux experience.
And with JAWS announcing “group” as “grope”, certainly there is room for… I dunno. Messing around I guess.
Leave a Comment or Response