-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Badge] Improve accessibility #22070
Comments
Thanks for the detailed issue. Could you summarize what accessible label you would expect for your original markup. Seems like your original approach was <a href="/notifications">
<Badge badgeContent={notificationsCount} color="primary">
<MailIcon titleAccess="notifications" />
</Badge>
</a> Just want to make sure we're on the same page about the "normal" markup one would write. In the end we want a solution that is accessible by default. |
Thanks for the quick response @eps1lon! Ideally the original markup would return an accessible lable that has parity with the one encountered by sighted users and that makes sense when read aloud. So that would be
It would also be OK if the accessible label were different given the different constraints, as long as the word order was still correct, i.e.
This is a simpler solution, but with less parity (which may also be surprising to developers). If we think it's good for sighted users to hide the badge content by default when the count is 0, then the same should be true for non-sighted users. And the same reasoning would suggest that all users should get the |
I don't think it makes sense to bake this into the component since there are too many edge cases that require domain knowledge. Have you tried using <a aria-label={notificationsLabel(notificationsCount)} href="/notifications">
<Badge badgeContent={notificationsCount} color="primary">
<MailIcon titleAccess="notifications" />
</Badge>
</a>
function notificationsLabel(count) {
if (count === 0) {
return 'notifications'
}
if (count > 99) {
return '99 plus notifications'
}
return `${count} notifications`; // 'notifications' should be pluralized by locale
} Especially since the current label looks quite ok to me ("notifications 99+"). Did users of assistive technology tell you that they find that confusing? |
Unfortunately
The You're totally right that we can solve this other ways, like Demo: https://codesandbox.io/s/material-demo-ycehq?file=/demo.js const notificationsCount = 1000
<a href="/notifications">
<Badge badgeContent={notificationsCount} color="primary">
<MailIcon />
</Badge>
<span className="visually-hidden">notifications</span>
</a> We'll still have the issue of the label reading So while it's totally reasonable for MUI to do nothing in response to this issue, I think the following enhancements could be helpful to other developers:
|
Comparison with what? Are you sure that the code you compare against is correct?
I don't think these patterns are generally good. Letting screen readers make these calls is almost always the better choince since they have better domain knowledge. It might be a configuration issue with the screen reader, or just a bug. In the end the first rule of aria should always be considered: don't use aria.
I'm curious why you link a post title why "aria-label is xenophobe" but then immediately suggest that reversing the order is correct (effectively saying that there's a single correct position for the badge content and direction a language is in). Are you sure this would be correct for every language and every position of the badge content?
Why is
I don't understand how this applies to React generally or my code specifically? |
@eps1lon Could #22070 (comment) make a great new |
Hey @eps1lon I'm sorry if I've come across wrong, or, on the flip side, if I'm misinterpreting your responses. It feels like this conversation has gone astray somehow. I opened the issue with the understanding it wasn't an obvious show-stopper with a clear solution. But I thought I'd put it out there and that if the maintainers saw merit in some of the suggestions, that'd be great! And if not, that's totally fine too. Originally, the only suggestion I had written was that it'd be helpful (and seemingly on par for other MUI components) to spread props on the As I said earlier, I will absolutely understand if MUI doesn't think this is worth tackling. It's not important to me to be right, I was just hoping to find opportunities to contribute to MUI. My teammates and I work with the framework everyday and would love to give back more where it's helpful. So I would love start out on the right foot. 🙏 In any case, I'm still happy to answer your questions best as I understand them.
I don't believe there's an objective "correct" here when talking about strings that correspond to localized written language. If you were to ask me, "How many comments are there on this thing?" I would say, "8 comments" as opposed to "comments 8". That's not "correct" per se, just more conventional, at least in English. So I'm reading the accessible badge label from the same lens. I'm not sure how MUI's approach to localization would affect this, but my assumption is that the DOM order in this component doesn't change in different locales and that the default locale for the app is considered English. If those things are true, it doesn't seem like a stretch to imagine that some particular formulation(s) of English phrases are more conventional than others and thus worth aiming for in MUI's components. But I definitely don't believe there is one "correct" solution here, and if that makes it more challenging to find an agreeable solution that's totally fair.
I totally agree about the first rule of aria. And I agree that we should be deferring to AT as a rule of thumb about how to treat web content, that they should be considered the domain experts. But I think this particular case could be considered a real world shortcoming of AT that's worth acknowledging and working around. For example, NVDA ignores the plus and equals symbols. It says “five two seven” when it should say “five plus two equals seven.” They have clearly made that choice for some reason, but it may not align with this component's intent. Given that the screen reader is an entirely different medium and it's my assumption that MUI is not building primarily for AT, the screen reader naturally has to make some assumptions on our behalf. It's my understanding that the
I’ll try my best to clarify my earlier statements. I'm sorry if it came across this way, but I don't mean to suggest there's a single "correct" position or that changing the DOM order would create a conventionallly semantic label for all users in all locales. But if (it's a big if) MUI considers English the default locale, then
It's manually translatable just like any string in a codebase, but some of the common automatic machine translation services (e.g. Google Translate) won't reliably translate that attribute. This is one example Adrian gives in his article, where the
It's not a React problem or specifically related to the |
@andrewborstein We appreciate the initiative to report potential improvements in the library, however, if you could keep it short, it would be even better, we have limited bandwidth and covering long message takes time :). I went with a quick benchmark to look at what approach the most used badges on the internet are using:
In this context, I don't think that we should move forward with 1, nor 2, nor 3 but with:
|
Thanks for the feedback @oliviertassinari! That approach sounds totally fair. And I'll definitely keep it shorter in the future 👍 |
So the idea is that you don't handle translation at all but defer to google translate. And that approach does not work with certain naming techniques. While I understand that concern I don't see this as a viable restriction for us. aria-label is specified, implemented and used by authors. Are there any plans by the ARIA folks to remove this attribute since it's not translateable and if not what reasons did they give? Especially considering Seems like
be the best approach but I would only start with <a href="/notifications" aria-labelledby="display-node icon">
<Badge badgeContent={notificationsCount} color="primary" displayNodeId="display-node">
<MailIcon titleAccess="notifications" id="icon" />
</Badge>
</a> For the |
@oliviertassinari @eps1lon Can I contribute to this issue? |
@likitarai1 Yes, what solution do you have in mind? |
@likitarai1 From what I understand, we had a good path forward with #22070 (comment). A new section on accessibility would even help with #20600. |
Yeah I was thinking about implementing #22070 (comment). |
Summary 💡
It would be helpful to have a little more flexible control over both nodes used to compose the
<Badge>
, so the badge can display the correct UI for sighted users and also render a helpful label for assistive technology.Examples 🌈
In my app we have a link in the primary nav that wraps badge wrapping an icon, used to indicate the total number of notifications for the current user.
Demo: https://codesandbox.io/s/material-demo-cc7rw?file=/demo.js
But with the above markup the link's accessible name — used when screen readers encounter the link, for example — is
when it would ideally be
One option is to change the DOM order of these nodes and put the
{children}
after the<span>{displayValue}</span>
. If it's safe to assume that this is a logical DOM order for most users, and it doesn't affect the visual layout, then that seems like a reasonable solution.But I was hoping to have full props access to the
<span>{displayValue}</span>
node, so I could find another solution like hiding it from assistive technology usingaria-hidden
and moving the equivalent ofdisplayValue
to the accessible icon title. It's a little annoying that we'd have to recreate the logic ofdisplayValue
, but I could live with that.Demo: https://codesandbox.io/s/material-demo-3ghl7?file=/demo.js
Unfortunately that's not part of the
<Badge>
API, so that's not possible. And using theclasses
prop won't help here, either, since there's no CSS that will hide a node from assistive technology and preserve it visually, only the opposite (e.g.display: none
). So the resulting accessible label isThe next option I tried was wrapping the
badgeContent
in anaria-hidden
node directly, but that breaks some more default functionality, like passing in my owndisplayValue
and specifiying that it should be hidden when the count is0
. If it were possible for the<Badge>
component to readchildren
as the equivalent ofinnerText
and then parse it as an integer, then<span aria-hidden>8</span>
could still be parsed as the number8
instead of a string or a component, etc.Demo: https://codesandbox.io/s/material-demo-fxbpy?file=/demo.js
Finally, it all works! But it seems unnecessarily painful to get there.
I recognize there are other options, still, like
Demo: https://codesandbox.io/s/material-demo-ycehq?file=/demo.js
But either changing the default DOM order of the two nodes or providing props access to the
displayValue
both seem like reasonable enhancements, as well.Motivation 🔦
I'd love to leverage of all the great existing logic and UI built into
<Badge>
, instead of re-writing big chunks of it to accomodate an accessible label.The text was updated successfully, but these errors were encountered: