Skip to content
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

Updates to aria-hidden #2037

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft

Updates to aria-hidden #2037

wants to merge 10 commits into from

Conversation

MelSumner
Copy link
Contributor

@MelSumner MelSumner commented Sep 13, 2023

If merged, this PR reflects discussions around how aria-hidden could be improved to better serve end-users. It provides improved author and user agent guidance.

The text content of this PR change were discussed during TPAC meetings and concept was drafted by @aleventhal and me.

We'd like to ensure implementor alignment, and then work to polish the wording for consistency with the rest of the spec. If reviewing before this PR is marked ready for review, please keep that in mind. Thank you!

PR tracking

Check these when the relevant issue or PR has been made, OR after you have confirmed the
related change is not necessary (add N/A). Leave unchecked if you are unsure. Read the
Process Document or
Test Overview for more information.

  • Related Core AAM Issue/PR:
  • Related AccName Issue/PR:
  • Related APG Issue/PR:
  • Any other dependent changes?

Implementation tracking

  • "author MUST" tests:
  • "user agent MUST" tests:
  • Browser implementations (link to issue or when done, link to commit):
    • WebKit:
    • Gecko:
    • Blink:
  • Does this need AT implementations?

Preview | Diff

index.html Outdated
@@ -11867,7 +11867,7 @@ <h2>Definitions of States and Properties (all aria-* attributes)</h2>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also visible in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a noop on visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element. When a repair is made, user agents should inform authors through developer tools (if available).</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uppercase to should.
remove awkward/recursive second mention of AT from the "this is because the at..." sentence.

Suggested change
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element. When a repair is made, user agents should inform authors through developer tools (if available).</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element. When a repair is made, user agents SHOULD inform authors through developer tools (if available).</p>

<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward. When this repair is made, user agents SHOULD inform authors through developer tools (if available).</p>
<p>Authors MUST NOT use <code>aria-hidden="true"</code> on the document element or body of the main document (e.g., <code>window.top</code>), where that would hide the entire accessibility tree. User agents MUST repair this situation by ignoring <code>aria-hidden="true"</code> when it would prevent all visible content from being available to ATs. Again, when this repair is made, user agents SHOULD inform authors through developer tools (if available). Notably, this situation does not occur when <code>aria-hidden="false"</code> is provided on a visible descendant, e.g. on a <code>role="dialog"</code>element, because that would generate some accessible content.</p>
<p class="note">Authors are advised to use extreme caution and consider a wide range of disabilities when hiding visibly rendered content from assistive technologies. For example, a sighted, dexterity-impaired individual might use voice-controlled assistive technologies to access a visual interface. If an author hides visible link text "Go to checkout" and exposes similar, yet non-identical link text "Check out now" to the accessibility API, the user might be unable to access the interface they perceive using voice control. Similar problems can also arise for screen reader users. For example, a sighted telephone support technician might attempt to have the blind screen reader user click the "Go to checkout" link, which they might be unable to find using a type-ahead item search ("Go to…").</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could improve this paragraph. I find it cumbersome to understand everything but the first 7 words.

Authors are advised to use extreme caution with aria-hidden. Misuse easily and often leads to inaccessible content. Starting with ARIA 1.3, several mitigations have been added to reduce the likelihood of this. However, aria-hidden is too powerful to make any mistakes with: many users have no way to perceive content that uses it, and therefore authors MUST avoid using aria-hidden on any content required for successful user journeys.

Copy link

@tranjocelyn tranjocelyn Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually liked the examples here! Would like them to be kept if possible.

@@ -11861,9 +11861,14 @@ <h2>Definitions of States and Properties (all aria-* attributes)</h2>
<div class="state" id="aria-hidden">
<sdef>aria-hidden</sdef>
<div class="state-description">
<p><a>Indicates</a> whether the <a>element</a> is exposed to an accessibility API. See related <sref>aria-disabled</sref>.</p>
<p><a>Indicates</a> whether the <a>element</a> can be included in the accessible tree as a visible accessibility node.</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not an objection, but not sure i understand the changing of this line. what is the difference between being included in the a11y tree (or a11y api) vs being included as a "visible" node. are these different things, or is this redundancy?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want the freedom, on platforms that can express visibility as a boolean state (non-Apple platforms) to use that rather than pruning. This gives some power back to the AT to repair.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeh i get that. i dunno, maybe i'll have a better way of framing the question tomorrow or something, i've been trying to re-ask the question for far too long now...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i get that the end goal here is to have more control over the 'visible' state of the node in the a11y tree. i question if this is the right way to introduce the attribute's purpose, particularly when the term 'visible' (and variants) are heavily used for whether something is visually rendered or not.

or another way to think about it, is aria-hidden identifying what nodes are visible in the a11y tree, or is it used to hide nodes (which can be re-made visible)? or is it being used to mark nodes as hidden OR visible (aria-hidden=false).

i hope that makes more sense? i'm not sure how else to describe this in text right now. the previous sentence made sense, this one doesn't (right now? maybe more changes are coming - it is a draft afer all) because it's unclear what the changing in words means since the rest of the text talks about hiding from the a11y tree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are good questions and will lead to more clarity. We would love your help with specific rewording suggestions to get there. I think we should start with what it does for the author, and then later discuss how the UA should apply that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: our talk today and looping in @MelSumner - i'm going to noodle on some ways to reword this and will submit that suggestion(s) asap

<p>User agents determine an element's [=element/hidden=] status based on whether it is rendered, and the rendering is usually controlled by CSS. For example, an element whose <code>display</code> property is set to <code>none</code> is not rendered. An element is considered [=element/hidden=] if it, or any of its ancestors are not rendered or have their <code>aria-hidden</code> attribute value set to <code>true</code>.</p>
<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this paragraph is referring to aria-hidden nodes plus their descendants? If so I think it would be good to clearly call out that all descendants of an aria-hidden node need to be marked as hidden, not just the root node itself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, correct. The value of aria-hidden (both true and false) inherits into descendants. An aria-hidden node is one where aria-hidden=true was set on the node itself, or it was inherited by a descendant.

@ggordon-vispero
Copy link

Great to see that the aria-hidden problem has gotten some traction. JAWS users have been prevented from accessing key content because of aria-hidden misuse. Can't wait to be able to try this change in canary builds of Chrome.

<p>User agents determine an element's [=element/hidden=] status based on whether it is rendered, and the rendering is usually controlled by CSS. For example, an element whose <code>display</code> property is set to <code>none</code> is not rendered. An element is considered [=element/hidden=] if it, or any of its ancestors are not rendered or have their <code>aria-hidden</code> attribute value set to <code>true</code>.</p>
<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While authors do not need to use aria-hidden when visibly hiding content with CSS

this should probably be more specific of where this is true, and/or allude to the fact that not all methods of visually hiding content with css will result in the content also being hidden from the accessibility tree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While authors do not typically need to use aria-hidden when using CSS methods like display: none to visibly hide content...

@scottaohara would that address your concern?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "methods like display: none or visibility: hidden"?

<p>User agents determine an element's [=element/hidden=] status based on whether it is rendered, and the rendering is usually controlled by CSS. For example, an element whose <code>display</code> property is set to <code>none</code> is not rendered. An element is considered [=element/hidden=] if it, or any of its ancestors are not rendered or have their <code>aria-hidden</code> attribute value set to <code>true</code>.</p>
<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This optional "prune or mark" behaviour presents a few challenges:

  1. It's not clear whether this is a user agent choice or dependent on the particular platform.
  2. If it's a user agent choice, that isn't great for interop.
  3. If it's a platform choice, that means we have very different trees on different platforms. That creates some performance challenges for browser engines (at least engines that don't already do this). It's also not great for interop. Yes, different APIs are obviously different, but now we're talking about the tree structure being fundamentally different. If we're going to do this, I'd argue it should be consistent across platforms, which might mean changes to platform APIs across the board.
  4. How does this impact hit testing? If hit testing returns aria-hidden content, that means an AT might not be able to work out what is actually visible on the screen at that point if there are multiple visual layers (z-index, stacking contexts, etc.).
  5. I understand that the intent of this is to allow AT to do repair, but that in itself creates interop issues. As I noted in Proposal to include visible aria-hidden objects, at least in some platform trees #1185 (comment), I'd much rather focus on consistent repair techniques across browsers than kicking the can down the road to AT, where the behaviour is a lot less likely to be consistent. Doing that is essentially a hack: we don't want to specify it here, so we let AT sort it out, since there are less stringent requirements on AT. There are already enough complaints about inconsistency across AT. This is only going to make it worse.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should chat about this one. The platforms that stand out are Mac/iOS, which don't provide a means of marking a node hidden. They've discussed it in the past and wish to remain an outlier rather than make this change, which would also involve changing their ATs. @cookiecrook, given James' concerns, is there a chance to reconsider?

Jamie, I was leaning toward it being a UA choice -- but if Firefox were to do the same thing, I suppose we could make it a platform choice based on availability of the hidden state. For Chrome, we would like to mark hidden on all platforms other than Apple platforms right now, because this gives us the best experience everywhere. This is what we think is best for our users. I understand what you're saying about interop, but on platforms that already have a hidden state, ATs already that by skipping those nodes just like the obviously must do for nodes that are there. But maybe you have a specific example of what might not be good for interop — I'd be interested to know if the problem created is bigger than the improvement we get by allowing ATs to do repairs on bad content.

I don't think hit testing should return hidden nodes.

Regarding responsibility of repairs all being on the UA, we don't agree about this. I think we should pass some responsibility to ATs, who are more nimble and better understand their users and use cases, and can apply more logic. ATs are never going to work exactly the same. So I guess we just disagree on this point. @ggordon-vispero has convinced me that they should have a chance at repairing content when their phone lines are lighting up about it, and they can't get traction anywhere else. Not every user is going to run with a bunch of bookmarklets or extensions to fix things.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making it a UA choice is a huge interop issue. Certain things (like how we render video controls or a date picker) are reasonably UA choices because they don't have a material impact on what a user can access that the site provides, even if a user might prefer one over the other. The different behaviour is surprising and confusing for both authors and users.

Especially having done this from both sides, I do understand the pragmatic need for repair, but we should still be doing our utmost to maximise interop here. Having significant differences in terms of what a user can access depending on what combination of software they use in multiple different dimensions doesn't really help anyone. This is why I'm more in favour of browser repairs here. That said, if we can get agreement that this is desirable across the board, we don't have inconsistencies across platforms and we iron out hit testing behaviour, etc., I can live with this.

If hit testing doesn't return hidden nodes, that solves one problem - an AT can easily get to visible content - but it also creates another. Your AT repair use case is now biased towards screen reader users who use a keyboard, where hit testing doesn't matter much. What about low vision screen reader users who use a touch screen or mouse? There's no way they could ever access this hidden content, repair or not, because explore by touch and mouse tracking require hit testing. The AT can't even work around that because the AT has no idea of visual layers created by z-order, stacking contexts, etc.

Finally, do Android and UI Automation have a way to expose hidden nodes in the tree? I didn't think they did.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Automation only has a hidden text attribute, looks like: UIA_IsHiddenAttributeId. Possibly not enough, I'll have to look into it.

I should note Chrome actually already uses STATE_SYSTEM_INVISIBLE and hidden:true on focusable nodes inside of aria-hidden, and has done so for a while. I'm not aware of it causing interoperability issues. We don't expose any other nodes in aria-hidden subtrees currently. If there are demonstrable issues with doing it then we would reconsider this part of the proposal.

The reason I like this mitigation overall is that the other mitigations aren't enough, say if the user is visiting via virtual buffer and not focusing the aria-hidden content, aria-hidden=false wasn't used, and it wasn't on the body or html. Those other mitigations are great but it won't get everything, and there are some mitigations that aren't appropriate in the browser. For example, some screen readers probably want to allow scripts or settings files to add aria-hidden content back to the virtual buffer by listing specific bad urls. I don't think it's a big interoperability issue so much, because the default is still to not put the aria-hidden content into the virtual buffer. So the default will essentially the same between screen readers -- to not present it. It's true I'm most concerned about screen readers and I'm glad for your help to find potential issues in other ATs.

@ggordon-vispero would appeciate your thoughts on potential uses for the aria-hidden nodes if you are willing to share, and on any potential interoperability issues.

index.html Outdated
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I explained in #1256 (comment), I'd argue that previous versions of the spec never explicitly allowed aria-hidden="false" to unhide anything, visually rendered or otherwise. This isn't really a substantive concern with the changes here, more an editorial thing: this paragraph claims that previous versions of the spec explicitly specified something which I don't believe they did.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thank you. We should fix this @MelSumner.

index.html Outdated
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward. When this repair is made, user agents SHOULD inform authors through developer tools (if available).</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Does "focusable elements" include scrollable containers that don't have a tabindex but are made focusable because they are scrollable? Gecko does this currently and I believe Chrome soon will. I don't mind either way, but this should be specified. I have a slight preference for this to apply to scrollable containers, since a user can tab to them, but I think there are arguments either way.
  2. "ignoring the aria-hidden attribute on the ancestor of the focused element": Is this intended to apply to an element that gets focus or to all focusable elements, regardless of whether they get focus? If the latter, this should probably say "focusable element".
    If this applies only to focused elements, that means content will magically appear in the document as the user tabs around, but won't otherwise appear. That's going to mean that many users never see it anyway; e.g. if they're using a screen reader browse mode/virtual cursor or a touch screen.
  3. I assume "from that point forward" means that if the element loses its focusability, aria-hidden will continue to be ignored? For the sake of absolute clarity, can we say "from that point forward, even if the element loses its focusability"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the wording got messed up on this one. It's intended to say that Authors MUST not allow elements within aria-hidden content to obtain focus, because there is more than one way of doing that, right? And it makes it clear that scrollable focusable areas are included.

For #2, it is meant to apply to elements that actually ended up receiving focus. At that point forward, aria-hidden="true" will be ignored on the entire subtree that has it from that point forward, even if the element within it loses focus. I agree it's not going to solve everything, but that's why we have multiple mitigations. There are definitely scenarios where something gets focus when the user has a screen reader and is navigating with a virtual buffer (e.g. the author places it there because the user clicked an open dialog button).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelSumner in our initial draft, I have different text. I have:

Authors MUST prevent elements from receiving focus within aria-hidden content, because when AT users reach this content, the AT can provide the user with no information about the content, leading to a disorienting and inaccessible experience. User agents MUST repair this situation by ignoring aria-hidden on the ancestor of the focused element.

I think it's better because it may be very difficult to avoid having focusable elements. But if the user can't actually focus the element via input means, maybe that's good enough. E.g. it's not tabbable, not clickable and hidden from AT, that prevents it from being focused.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it more, I guess it depends what is meant by focusable. I would thing that an will be considered focusable, but if I have code that prevents it from being focused, or redirects focus if it does occur — either seems fine, right? I don't think we want to ask authors to have to go through their aria-hidden content and apply tabindex=-1. Interested in other opinions on this wording.

Copy link

@jcsteh jcsteh Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I misunderstood the intent here. The repair is only intended to apply to focused elements, so the text is correct there. However, the mention of focusable elements earlier (which is effectively author requirement, not UA repair) confused me.

Perhaps we could say:

If an element inside an aria-hidden ancestor receives focus, user agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward, even once the element loses focus or is no longer focusable.

I didn't think of the "open dialog" style example. Thanks for that. As a side note, this is one reason I'd really like to see solid use cases being included alongside ARIA spec changes (probably not in the spec itself, but at least in the PR). This makes it a lot easier as a reviewer to understand why a change is being made and would avoid a lot of misunderstandings like this. It would also make it a lot easier when looking at historical changes to figure out why the change was made.

<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward. When this repair is made, user agents SHOULD inform authors through developer tools (if available).</p>
<p>Authors MUST NOT use <code>aria-hidden="true"</code> on the document element or body of the main document (e.g., <code>window.top</code>), where that would hide the entire accessibility tree. User agents MUST repair this situation by ignoring <code>aria-hidden="true"</code> when it would prevent all visible content from being available to ATs. Again, when this repair is made, user agents SHOULD inform authors through developer tools (if available). Notably, this situation does not occur when <code>aria-hidden="false"</code> is provided on a visible descendant, e.g. on a <code>role="dialog"</code>element, because that would generate some accessible content.</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This raises a couple of mutation edge cases that I think need to be clearly specified:

  1. aria-hidden="true" is set on the document, aria-hidden="false" is set only on a descendant dialog. Later, the dialog is removed. Should the UA now ignore aria-hidden="true" on the document, even though it wasn't ignoring it before?
  2. aria-hidden="true" is set on the document, no descendants have aria-hidden="false". The UA ignores aria-hidden on the document. Later, a diaog with aria-hidden="false" is added. Should the UA now stop ignoring aria-hidden on the document so that anything other than the dialog disappears?

If the answer to both of these is "yes", we might be able to cover that with a single sentence like "This repair should be re-evaluated whenever aria-hidden="false" descendants are added or removed from the document."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jamie, for #1, I would say that this is a realistic scenario that occurs. Basically, the author is trying to do a modal dialog and forgets to remove aria-hidden on the document. Since it's a realistic scenario, I think we should ask UAs to track that scenario and now ignore the aria-hidden.

For #2, once any aria-hidden is ignored on a given node for any of the repair reasons, it can just be ignored from there on out. Personally I think that makes implementation simpler in that we can keep a list of bad aria-hidden roots. Also, once a repair occurs, a warning of some kind should be emitted through developer tools -- this could take a number of different forms, where a console warning is the simplest.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. In that case, the spec wording needs to be clarified accordingly. Something like this:

User agents MUST repair this situation by ignoring <code>aria-hidden="true"</code> when it would prevent all visible content from being available to ATs. This repair should be re-evaluated whenever a descendant with <code>aria-hidden="false"></code> is removed. Once <code>aria-hidden="true"></code> is ignored on an element, it should remain ignored henceforth, regardless of changes to descendants.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another consideration -- what if the ignored aria-hidden attribute is removed, then re-added to the same node? Does it continue to be ignored, or does that reset it?

@tranjocelyn
Copy link

Can examples of good and bad use cases be included? I think that would be helpful for authors.

<p>User agents determine an element's [=element/hidden=] status based on whether it is rendered, and the rendering is usually controlled by CSS. For example, an element whose <code>display</code> property is set to <code>none</code> is not rendered. An element is considered [=element/hidden=] if it, or any of its ancestors are not rendered or have their <code>aria-hidden</code> attribute value set to <code>true</code>.</p>
<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making it a UA choice is a huge interop issue. Certain things (like how we render video controls or a date picker) are reasonably UA choices because they don't have a material impact on what a user can access that the site provides, even if a user might prefer one over the other. The different behaviour is surprising and confusing for both authors and users.

Especially having done this from both sides, I do understand the pragmatic need for repair, but we should still be doing our utmost to maximise interop here. Having significant differences in terms of what a user can access depending on what combination of software they use in multiple different dimensions doesn't really help anyone. This is why I'm more in favour of browser repairs here. That said, if we can get agreement that this is desirable across the board, we don't have inconsistencies across platforms and we iron out hit testing behaviour, etc., I can live with this.

If hit testing doesn't return hidden nodes, that solves one problem - an AT can easily get to visible content - but it also creates another. Your AT repair use case is now biased towards screen reader users who use a keyboard, where hit testing doesn't matter much. What about low vision screen reader users who use a touch screen or mouse? There's no way they could ever access this hidden content, repair or not, because explore by touch and mouse tracking require hit testing. The AT can't even work around that because the AT has no idea of visual layers created by z-order, stacking contexts, etc.

Finally, do Android and UI Automation have a way to expose hidden nodes in the tree? I didn't think they did.

index.html Outdated
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward. When this repair is made, user agents SHOULD inform authors through developer tools (if available).</p>
Copy link

@jcsteh jcsteh Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I misunderstood the intent here. The repair is only intended to apply to focused elements, so the text is correct there. However, the mention of focusable elements earlier (which is effectively author requirement, not UA repair) confused me.

Perhaps we could say:

If an element inside an aria-hidden ancestor receives focus, user agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward, even once the element loses focus or is no longer focusable.

I didn't think of the "open dialog" style example. Thanks for that. As a side note, this is one reason I'd really like to see solid use cases being included alongside ARIA spec changes (probably not in the spec itself, but at least in the PR). This makes it a lot easier as a reviewer to understand why a change is being made and would avoid a lot of misunderstandings like this. It would also make it a lot easier when looking at historical changes to figure out why the change was made.

<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
<p class="note">This is a change from ARIA 1.2 and earlier versions in several ways: 1) <code>aria-hidden="false"</code> no longer un-hides content that is also hidden in CSS, as there are other techniques to present content only to AT users that works more consistently (e.g. positioning offscreen), and 2) <code>aria-hidden="false"</code> previously was a <code>noop</code> on (e.g., did not affect) visible content, and 3) <code>aria-hidden="false"</code> previously operated only on a single element. However, the <code>aria-hidden="false"</code> behavior of previous versions was never consistently implemented, understood or commonly used.</p>
<p>Authors MUST NOT place focusable elements within <code>aria-hidden</code> content. This is because the AT will have no information about the content, so it cannot be presented to the user with AT. User agents MUST repair this situation by ignoring the <code>aria-hidden</code> attribute on the ancestor of the focused element from that point forward. When this repair is made, user agents SHOULD inform authors through developer tools (if available).</p>
<p>Authors MUST NOT use <code>aria-hidden="true"</code> on the document element or body of the main document (e.g., <code>window.top</code>), where that would hide the entire accessibility tree. User agents MUST repair this situation by ignoring <code>aria-hidden="true"</code> when it would prevent all visible content from being available to ATs. Again, when this repair is made, user agents SHOULD inform authors through developer tools (if available). Notably, this situation does not occur when <code>aria-hidden="false"</code> is provided on a visible descendant, e.g. on a <code>role="dialog"</code>element, because that would generate some accessible content.</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. In that case, the spec wording needs to be clarified accordingly. Something like this:

User agents MUST repair this situation by ignoring <code>aria-hidden="true"</code> when it would prevent all visible content from being available to ATs. This repair should be re-evaluated whenever a descendant with <code>aria-hidden="false"></code> is removed. Once <code>aria-hidden="true"></code> is ignored on an element, it should remain ignored henceforth, regardless of changes to descendants.

<p>User agents determine an element's [=element/hidden=] status based on whether it is rendered, and the rendering is usually controlled by CSS. For example, an element whose <code>display</code> property is set to <code>none</code> is not rendered. An element is considered [=element/hidden=] if it, or any of its ancestors are not rendered or have their <code>aria-hidden</code> attribute value set to <code>true</code>.</p>
<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "screen readers" use "assistive technologies". These are not interchangeable terms.

<p>Authors MAY, with caution, use aria-hidden to hide visibly rendered content from assistive technologies <em>only</em> if the act of hiding this content is intended to improve the experience for users of assistive technologies by removing redundant or extraneous content. Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>While authors do not need to use <code>aria-hidden</code> when visibly hiding content with CSS, </code>authors MAY, with caution, use <code>aria-hidden="true"</code> to hide visibly rendered content from assistive technologies. However, authors should <em>only</em> do so if the act of hiding this content is intended to improve the experience for users of assistive technologies (e.g., to remove redundant or extraneous content). Authors using <code>aria-hidden="true"</code> to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies.</p>
<p>User agents MUST either prune <code>aria-hidden</code> nodes from the accessibility tree, or mark all <code>aria-hidden</code> nodes as hidden.</p>
<p>Authors MAY use <code>aria-hidden="false"</code> within an <code>aria-hidden="true"</code> subtree to unhide the visibly rendered content within the <code>aria-hidden="false"</code> subtree.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a good idea. It has become incredibly common for devs to put things like aria-hidden={!open} in their code. Just look at a quick search for aria-hidden on GitHub:
https://github.com/search?q=aria-hidden%3D%7B+language:JavaScript&type=code&l=JavaScript

It is a rarity that devs set aria-hidden to null. That doesn't suggest to me that it is a common misunderstanding that putting aria-hidden="false" in an element with aria-hidden="true" will unhide the element. This is relied on a in a lot of places. Now not all of these might be real problems, since CSS might be at play there too, but assuming even 10% of those cases actually required aria-hidden, that is a lot of components that break because they can no longer be hidden with aria-hidden="true".

Very strong 👎 from me on this change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if you wanted an option to unhide with aria-hidden, a new value should be introduced like aria-hidden="reset", something explicit like that. Even done that way I think is very worrying. While that does add a new feature that may have some uses, it also removes the capability for a node to completely hide itself with aria-hidden="true". There is also some worrying interaction with inert, which cannot be unset and may result in inactive components that are still in the accessibility tree.

Copy link
Contributor

@aleventhal aleventhal Sep 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would only have any affect in a subtree that's both already aria-hidden="true" (marked on an ancestor) and visible to sighted users. Does that help?

We want to do this because some of the cases where we've seen inaccessible content has this pattern. The author didn't understand that aria-hidden="false" didn't do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would only have any affect in a subtree that's both already aria-hidden="true" (marked on an ancestor) and visible to sighted users. Does that help?

That's how most accessible modals work: aria-hidden="true" on the container wrapping all non-modal content. It is essential for modals that aria-hidden="true" hides everything, and that stuff doesn't "leak" out because a dev on some third party component library just happened to put aria-hidden={!open} instead of aria-hidden={!open ? true : null}.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @WilcoFiers, this change would have potentially huge consequences — I personally would need to go have a mild panic about all the places this could potentially break our UI 😅.

The caveat that this would only apply to nodes that are visible to sighted users doesn’t help at all — all our modal background content is still technically visible.

@alice
Copy link

alice commented Sep 19, 2023

This issue caught my attention as it floated by my inbox. I felt like I might have some useful thoughts, given my earlier work on inert and involvement in implementing other accessibility tree pruning mechanisms like aria-modal.

I tried to get my head around everything that's happening here, and it seems like there are several fairly distinct (albeit related) changes bundled together:

  1. A change in the semantics of aria-hidden, from (generally) accessibility tree pruning behaviour to adding a "hidden" state (where available);
  2. Adding guidance for authors around interactions between aria-hidden and CSS properties which make content non-rendered;
  3. Adding normative requirements for UAs to "repair" when elements which are ARIA hidden are focused;
  4. Adding normative requirements for UAs to ignore aria-hidden on a root element under certain circumstances, with a caveat added by the point below; and
  5. Adding an entirely new mechanism to use aria-hidden=false to "un-hide" content.

That's quite a lot of substantial and potentially controversial changes to put in one PR.

There's already been a fair amount of conversation on several of these points. I also have thoughts on several, as well as on the actual wording that’s been proposed, but I'd like to especially highlight a comment from @jcsteh:

this is one reason I'd really like to see solid use cases being included alongside ARIA spec changes (probably not in the spec itself, but at least in the PR). This makes it a lot easier as a reviewer to understand why a change is being made and would avoid a lot of misunderstandings like this. It would also make it a lot easier when looking at historical changes to figure out why the change was made.

I spent ... longer than I should probably admit trying to understand what changes are being proposed here and, more importantly, what problems they're trying to solve. Some of these changes are substantial enough that they could use a mini-explainer. At a minimum, there should be some top-level explanation of the problems being solved, and links out to previous discussion in github threads and minuted meetings. (It needn't be a separate document necessarily, but at least a substantial PR description.)

I realise this is a drive-by comment, but is it too late to split this change out into discrete PRs? That way, each could have a detailed description which adds the necessary context for each change. It should also help avoid having an overwhelming number of conversations happening in one issue. Plus, it should prevent the less controversial aspects getting blocked, and help with finding agreement on the more controversial ones.

@smhigley
Copy link
Contributor

smhigley commented Sep 28, 2023

I'd like to +1 @alice's comment about this being quite a lot of pretty independent and substantial changes being bundled together in one PR. Would it be at all possible to split them out into a few separate PRs? The UA repair guidance seems the least controversial, and I'd hate to see it held up by other controversial changes that it doesn't depend upon.

I was also wondering if the removed vs. hidden distinction might make more sense to spec out in Core AAM, since it seems more like a mapping/implementation detail and not related to the intent & functionality of the attribute. Then perhaps we could instead change the ARIA spec to use more generic language than "prune from the accessibility tree".

@aleventhal
Copy link
Contributor

aleventhal commented Sep 28, 2023 via email

@MelSumner
Copy link
Contributor Author

@aleventhal do we want to make some time to go through this?

@aleventhal
Copy link
Contributor

aleventhal commented Feb 9, 2024 via email

@scottaohara
Copy link
Member

there is the PR for adjusting expectations for aria-hidden=false - which was done to resolve a related issue(s). But it definitely has an impact on where this PR was headed.

scottaohara added a commit that referenced this pull request May 20, 2024
This PR closes #1765 and is related to work that was done in #2037, but scoped only to the original issue I filed.

The intent of this PR is to identify not only how user agents would need to handle focusable elements that are aria-hidden (explicitly or due to being a descendant of an aria-hidden container) - but for the case where a focusable element is within an aria-hidden container, that the entire subtree would need to be re-exposed so that any other relevant information to the user could be made available.  (e.g., so as to not just expose a "learn more" link, with no way to determine what someone would be learning about)

a simple example being like:

```
<div aria-hidden=true>
  <h3>Something or other</h3>
   some details about said something, or other.
  <a href=#>Learn more!</a>
</div>
```
@pkra pkra added the spec:aria label Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.