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

When is hidden content taken into calculation of name and description? #57

Closed
JAWS-test opened this issue Sep 5, 2019 · 62 comments · Fixed by #150
Closed

When is hidden content taken into calculation of name and description? #57

JAWS-test opened this issue Sep 5, 2019 · 62 comments · Fixed by #150
Assignees
Labels
Milestone

Comments

@JAWS-test
Copy link

JAWS-test commented Sep 5, 2019

Unfortunately for me (as well as for the browser manufacturers and developers of assistive technology: FreedomScientific/standards-support#251) the procedure for determining labeling and description is too complex (https://w3c.github.io/accname/#terminology) and I would therefore like to know how to proceed with hidden content according to the specification.

For which label types is hidden content output valid?

  • aria-labelledby
  • aria-describedby
  • label for
  • enclosing label
  • legend in fieldset
  • figcaption in figure
  • caption in table
  • determining accessible name and description for elements within a Live Region
  • ...

When does it apply?

  • Label or description itself is hidden
  • Descendant elements (of label or description) are hidden
  • Ancestor elements (of label or description) are hidden

If I'm not the only one who doesn't see this directly from the specification, I'd ask you to be more precise in the specification and possibly give some examples of when hidden content is output and when not.

See also: w3c/aria-practices#1136

@JAWS-test
Copy link
Author

I understood before testing and before the discussion with @mcking65 (FreedomScientific/standards-support#251)

According to the specification, hidden text should be output as label when using label for or aria-labelledby or aria-describedby. However, this only applies if the label itself or an ancestor element is hidden

Since the test and the discussion, I no longer know exactly what the specification actually wants to say. In addition, the specification leaves open the labeling method for which the rule applies (see above: List of labeling methods).

@accdc
Copy link
Contributor

accdc commented Sep 6, 2019

Hi,
This is something that will be better explained in future edits of the AccName spec, and there are some misconceptions as you documented above.

The only attributes that are permitted to reference hidden elements are aria-labelledby and aria-describedby. Also, this is only true if the referenced element is explicitly hidden using CSS display:none or visibility:hidden, or the HTML5 hidden attribute.

However, if the ancestor element of the referenced element is hidden instead, making the referenced element implicitly hidden, then it will not be exposed. This decision goes back to prior discussions we had regarding this subject in the ARIA WG, where we agreed this would be the most intuitive course to document going forward.

In contrast, if a referenced element includes hidden children, whether by CSS or the hidden attribute or the aria-hidden attribute, then those hidden elements will not be included in the computation.

You can try out these various combinations on the live test page at
https://whatsock.github.io/w3c-alternative-text-computation/Editable%20Live%20Input%20AccName%20Test.html

Simply add id="test" to the markup that you want to compute the accessible name for by pasting the code in the edit field at the end of the page, then activate the Paste & Test button, which will load the markup and compute the name for the referenced element. Also, if your markup includes focusable elements, the accessible name and description property values will automatically be computed as you tab between each focusable element after you render it within the page.

@JAWS-test
Copy link
Author

Hi @accdc
Thank you so much for your explanation. It is very clear and understandable. Unfortunately this clear language is missing in the various specifications (Accname, ARIA) and other documents (like ARIA Practice). Also this is not implemented by assistive technology at the moment. Therefore I would like to suggest to add this in the specification so that it is clear for everyone.

@JAWS-test
Copy link
Author

JAWS-test commented Sep 7, 2019

@accdc

I tried https://whatsock.github.io/w3c-alternative-text-computation/Editable%20Live%20Input%20AccName%20Test.html and discovered a case that I find strange:

<input aria-labelledby=1 id=test>
<span hidden id=1>1 
     <span aria-hidden=true>2</span>
</span>

I would have expected accName to be "1," but it'd give out "1 2." The "2" should not belong to the accName because of the aria-hidden=true at the inner span. It also works correctly if the outer span element is not hidden.

@JAWS-test
Copy link
Author

JAWS-test commented Sep 7, 2019

The result of

<input aria-labelledby=1 id=test>
<span aria-hidden=true id=1>1</span>

I also find surprising: "1" because you wrote indirectly that it does not apply to aria-hidden

@mcking65
Copy link

@accdc commented:

However, if the ancestor element of the referenced element is hidden instead, making the referenced element implicitly hidden, then it will not be exposed. This decision goes back to prior discussions we had regarding this subject in the ARIA WG, where we agreed this would be the most intuitive course to document going forward.

Bryan, If I understand, you are saying that in the following code, the first input has a name and the second one does not.

<div id="1" hidden>My Label</div><input type="text" aria-labelledby="1">
<div hidden><div id="2">My Label</div></div><input type="text" aria-labelledby="2">

Can you point to where and when the WG made a decision to do this? I don't recall when we may have gone in this direction.

I don't understand the purpose. To me, it seems like a very odd querk to throw into the algorithm, and it makes it even more mysterious . It feels counter to what would be intuitively expected. I don't see language in the definition of aria-labelledby or aria-describedby to support it. For that matter, there is no language in the spec that even suggests hidden content may be referenced, which seems like a major oversight.

@jnurthen jnurthen added this to the 1.2 milestone Sep 26, 2019
@jnurthen jnurthen removed the Agenda label May 7, 2020
@jongund
Copy link

jongund commented May 7, 2020

Based on the discussion in the 5 May 2020 teleconference a summary of this issue:

In general the aria-labelledby algorithm for computing accessible name ignores whether something is hidden or visible. Hidden currently only makes a difference when concatenating descendant nodes of a referenced visible node and a descendant node is hidden. The hidden node and its descendants are currently not included in the accessible name. There is probably a special case for when something is hidden using the CSS visibility property and a descendant of the hidden node is switched to being visible. In this case the visible content would be included in the accessible name. The following is a table of different conditions and the affects of a hidden descendant node.

Effect on Accessible Name Calculation for hidden descendants of a visible node referenced in an aria-labelledby

Descendant Hiding Technique accname Calculation Special Cases
HTML hidden attribute hidden descendant node and all of its descendants are not included none
CSS display: none The hidden descendant node and all of its descendants are not included none
CSS visibility: hidden The hidden descendant node and all of its descendants are not included Any descendant nodes of the hidden node that are made visible through visibility: visible are included in the accessible name calculation
ARIA aria-hidden: true The descendant node not included in the accessibility tree and all of its descendants are not included Depends on resolution of Issue 1256

@accdc
Copy link
Contributor

accdc commented May 7, 2020

Hi,
As we discussed today:
https://www.w3.org/2020/05/07-aria-minutes.html#item04

I've updated the AccName Prototype to reflect the WG decision to ignore both implicit and explicitly hidden states on elements directly referenced by aria-labelledby and aria-describedby, regardless if the referenced element is hidden or if one of its parent elements is hidden instead.

It also takes into account if a child element is explicitly hidden as well, such as when aria-hidden="true" is used, which should be ignored as documented in the AccName algorithm for recursive processing.

Example:

<div hidden>
<i id="desc">This is a description.
<b aria-hidden="true">This should stay hidden.</b></i>
</div>
<input title="Field Name" id="test" aria-describedby="desc" />

Result:
https://whatsock.github.io/w3c-alternative-text-computation/Editable%20Live%20Input%20AccName%20Test.html

accName: "Field Name"
accDesc: "This is a description."
(Running AccName Computation Prototype version: 2.49)

This may need to be clarified within the spec text.

@jaragunde
Copy link
Contributor

I'm sorry to add a diverging opinion... It seems that, from the point of view of UA implementation, it's near impossible to tell if a node is explicitly hidden or if it's hidden because its parent is. This information kind of "goes away" in early stages of stylesheet processing, because it's considered redundant. For example, these two pieces of markup are equivalent after the initial processing:

<div id="ID1" style="visibility: hidden;">Rich's button<span style="visibility: hidden;">this should not be part of the name</span></div>
<div id="ID1" style="visibility: hidden;">Rich's button<span>this is not part of the name</span></div>

As a consequence, iterating over Bryan's example above:

<div hidden>
<i id="desc">This is a description.
<b aria-hidden="true">This should stay hidden.</b>
<b hidden>And this should stay hidden too.</b></i>
</div>
<input title="Field Name" id="test" aria-describedby="desc" />

Firefox current output is: "This is a description. This should stay hidden. And this should stay hidden too.", and Chromium will do the same soon (part of an ongoing refactor). In general, if accname is calculated through an aria-labelledby relation, and the referenced node is hidden, all of its children will be included in the calculation, regardless they are explicitly hidden or not (because that information is gone).

We might be able to make a separate case for aria-hidden... In which the output would be: "This is a description. And this should stay hidden too."

@JAWS-test
Copy link
Author

@jaragunde

This information kind of "goes away" in early stages of stylesheet processing, because it's considered redundant

Do you think that browsers are not able to recognize nested hidden information correctly and thus the specification would have to be adjusted? Or would it be acceptable to require browsers to make this distinction? The current version of Chrome is currently still able to do this. The error you describe occurs entirely in Firefox and only partially in Chrome.

@jaragunde
Copy link
Contributor

@JAWS-test I've been working on Chromium's accname code for a while, trying to fix some cases we are doing wrong there. At the same time, I tried to enhance results for hidden elements to what I thought was more "consistent" (e.g. https://crbug.com/1204042), and found out the reasons why Chromium and Firefox have the output they have now.

Going back to the previous example:

<div hidden>
<i id="desc">This is a description.
<b aria-hidden="true">This should stay hidden.</b>
<b hidden>And this should stay hidden too.</b></i>
</div>
<input title="Field Name" id="test" aria-describedby="desc" />

Chromium's current output for description is: "This is a description. And this should stay hidden too."

Firefox's is: "This is a description. This should stay hidden. And this should stay hidden too."

I'm suggesting that the spec should be adjusted to what browsers do in this case. We might still keep one way to hide a node for accname inside a hidden tree, using aria-hidden, like Chromium does right now; I suppose that could be doable in Firefox too.

@JAWS-test
Copy link
Author

JAWS-test commented May 18, 2021

@jaragunde The specification is actually very clear:

  • It says that hidden content is ignored unless it is directly referenced (e.g. via aria-labelledby).
  • If there is additional hidden content within the referenced hidden content, this nested hidden content should not become part of the accessible name or description.
  • The specification does not make a difference between hidden and aria-hidden. This distinction also seems illogical to me, since hidden and aria-hidden are equivalent.

I.e. in your example the description should be: "This is a description". So it also says https://whatsock.github.io/w3c-alternative-text-computation/Editable%20Live%20Input%20AccName%20Test.html

If the browsers agree on a different approach, perhaps the specification can be adapted (but I can't decide that, other people have to comment on that).

@joanmarie
Copy link
Contributor

TL;DR: I agree with what @JAWS-test just said. Whether or not we like what's in the spec, I think what's there (with respect to this issue) is quite clear.

Given <input aria-labelledby="label">, what should the name of the input be given each of the below snippets and why? I've included my responses:

Case 1: <div id="label" class="hidden">foo <span class="hidden">bar</span> baz</div>

  • Name: "foo baz"
  • Why: Hidden gets discarded for the div because it's directly referenced by aria-labelledby. The span, however, is NOT directly referenced by aria-labelledby. PLUS it's got an explicit hidden on it. Apparently the author means it. 😄
  • Comment: I think this is both consistent with the current AccName language and with what an author would expect.

Case 2: <div id="label" class="hidden">foo <span>bar</span> baz</div>

  • Name: "foo baz"
  • Why: Hidden gets discarded for the div because it's directly referenced by aria-labelledby. The span, however, is NOT directly referenced by aria-labelledby.
  • Comment: This is the same result as Case 1 and is consistent with the current AccName language. But it might not be what the author expects. IF the WG concurs that it is not what an author would expect, THEN we need to change AccName. I'll make a proposal for how in a moment.

Case 3: <div id="foo" class="hidden"><span>foo </span><span>bar</span><span> baz</span></div>

  • Name: ""
  • Why: Hidden gets discarded for the div, the spans are NOT directly referenced by aria-labelledby so.... Consistent with the language of the spec, but I'm guessing that this result is NOT what the author expects. In which case, again, we'll need to change AccName. More about how in a moment.

Case 4: <div class="hidden"><div id="label">foo <span>bar</span> baz</div></div>

  • Name: "foo baz"
  • Why: Same reason as Case 2. It doesn't matter why the div with id="label" is hidden (directly or via an ancestor). But the span with "bar" is hidden and NOT directly referenced by aria-labelledby.

As for implementing what is currently in the spec and this comment from @jaragunde:

it's near impossible to tell if a node is explicitly hidden or if it's hidden because its parent is.

I don't think it matters why it's hidden. I think what matters is, again, checking if it's directly referenced via aria-labelledby. Thus I think browsers should be able to do something like the following:

  1. Keep track of the node we're calculating the name for (i.e. the input with aria-labelledby="label").
  2. Start the AccName calculation like usual.
  3. For each node that is hidden -- regardless of how/why it's hidden:
    a. Get the id of the element associated with that node
    b. See if that id is in the list of nodes from item 1.
    c. If yes, include it; if no, don't include it.

For example, these two pieces of markup are equivalent after the initial processing:
<div id="ID1" style="visibility: hidden;">Rich's button<span style="visibility: hidden;">this should not be part of the name</span></div>
<div id="ID1" style="visibility: hidden;">Rich's button<span>this is not part of the name</span></div>

Makes sense to me. In both cases I think the name should be "Rich's button". The first example is like my Case 1. The author is saying that they really, really mean to keep the span out of the name calculation. The second example is like my Case 2: That span is not directly referenced by aria-labelledby so poof! Might not be what the author expected though, which brings me to the following:

We could update the AccName spec to say something in the ballpark of:

If the current node is hidden and is directly referenced by aria-labelledby or aria-describedby, treat that node as if it were explicitly marked as visible by the author.

The impact of that change would be the following:

  • Case 1: "foo baz" (unchanged because "bar" is explicitly "hidden")
  • Case 2: "foo bar baz" (it's like class="hidden" weren't there, and nothing else is hiding "bar")
  • Case 3: "foo bar baz" (for the same reason as Case 2)
  • Case 4: "foo bar baz" (ditto, though my ballpark language above seems inadequate to cover this case clearly)
  • Jacobo's Example 1: "Rich's button"
  • Jacobo's Example 2: "Rich's button this is not part of the name" (for the same reason as Case 2)

I don't know if that's what authors would expect given the cases above. Thoughts?

Also, I think implementing something like that would be harder because now user agents would have to do some checks for nodes which are not explicitly hidden, i.e. by a property on that node. You couldn't just check that node's element's id to see if it's directly referenced. That might be a drag for implementors. Thoughts?

@joanmarie
Copy link
Contributor

Chromium's current output for description is: "This is a description. And this should stay hidden too."

Firefox's is: "This is a description. This should stay hidden. And this should stay hidden too."

I'm suggesting that the spec should be adjusted to what browsers do in this case. We might still keep one way to hide a node for accname inside a hidden tree, using aria-hidden, like Chromium does right now; I suppose that could be doable in Firefox too.

Sometimes bringing a spec into alignment with reality makes a lot of sense. In this particular case, I'm afraid I disagree. Look at specific case:

<div hidden>
<i id="desc">This is a description.
<b aria-hidden="true">This should stay hidden.</b>
<b hidden>And this should stay hidden too.</b></i>
</div>

I would think that adding explicit hiding on the b elements is a pretty clear indication on the part of the author that they really mean those elements to be hidden with only "This is a description." being the accessible description.

And, yeah, I get that user agent code might not currently be able to distinguish if a given node is explicitly hidden or hidden via inheritance. But I think fixing that in the user agents is better and more consistent with what I imagine authors expect. And right now (i.e. without making the change I tossed out for discussion above), I would think your test case should be fixable by what I stated in my previous comment: Get the id of the element associated with each node in your example. If that id ain't "desc" it's not included in the description.

@JAWS-test
Copy link
Author

@joanmarie Now I was surprised myself: I always read Accname as saying that nested elements not marked as hidden become part of the accessible namen when the ancestor element marked with hidden is referenced with aria-labelledby.

In my opinion it is not exactly stated in the specification who is right now. This should be added!

@joanmarie
Copy link
Contributor

@JAWS-test: I think your reading is what is the spirit of the spec. But I don't believe that's what the spec actually states. More specifically, Step 2A states:

If the current node is hidden and is not directly referenced by aria-labelledby or aria-describedby, nor directly referenced by a native host language text alternative element (e.g. label in HTML) or attribute, return the empty string.

So question 1: What does "directly referenced" mean exactly? Given:

<input aria-labelledby="label">
<div id="label" class="hidden"><span id="child">foo</span></div>

The div with id="label" is clearly directly referenced by aria-labelledby. Is the span with id="child" directly referenced by aria-labelledby? I personally do not think it is.

And here's the definition of "hidden" in the ARIA spec:

Indicates that the element is not visible, perceivable, or interactive to any user. An element is considered hidden if it or any one of its ancestor elements is not rendered or is explicitly hidden.

Taking my example above, the span with id="child" meets the definition of "hidden" because one of its ancestor elements is not rendered/explicitly hidden.

So if we hit step 2A and current node is that span with id="child", I believe we're expected to return the empty string for that node.

Will we hit step 2A? Of course! 😁

Step 1: root node and current node are the input
Step 2B applies and current node becomes the hidden div. GOTO 2
Step 2A does not apply: current node (the div) is hidden BUT it's directly referenced by aria-labelledby
Step 2B does not apply: current node (the div) doesn't have an aria-labelledby
Step 2C doesn't: current node isn't a control embedded within the label for another widget
Step 2D doesn't: current node doesn't have an aria-label
Step 2E doesn't: current node doesn't have native markup defining a text alternative
Step 2F does apply: current node (the div) is referenced by aria-labelledby
Step 2Fiii: current node becomes the span with id="child". GOTO 2

And that brings us to Step 2A with a hidden node that is not directly referenced by aria-labelledby so we return the empty string for it, and proceed to the next child of the div but there is only that one hidden child so, 🤷‍♀️

@JAWS-test
Copy link
Author

@joanmarie You are absolutely right. If you read the specification carefully, it is exactly as you say. Unfortunately, I didn't read it that closely and always just thought how it could be meant and how it also seems logical.

The question now would be whether to adjust the specification to allow descendant elements within hidden areas to also contribute to the Accessible Name/Description (because that would make sense and most would expect it that way) or to leave it as is

@cyns
Copy link
Contributor

cyns commented Jun 15, 2021

Adding some historical context... The original intent of this feature was to supply text for screen-readers (accessibility tree) that was hidden from visual users. It's sort of the opposite of aria-hidden, which hides things from the accessibility tree while leaving it for visual users.

<label class="hidden" id="lbl1">foo</label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo"
there is no visible text

<label class="hidden" id="lbl1">foo <span>bar</span></label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo bar"
there is no visible text

<label id="lbl1">foo <span class="hidden">bar</span></label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo bar"
Visible text is "foo". (bar is only for accessibility)

<label class="hidden" id="lbl1">foo <span>bar <span>baz</span></span></label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo bar baz"
If this tree were deeper, it would include all the text children. The main use case for this is a "super-tool-tip," which could have many elements inside it, being used as in aria-describedby. (labelledby and describedby would work the same way)

<label class="hidden" id="lbl1">foo <span class="hidden">bar</span></label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo bar".
There is no visible text
There's a use case for this double-hidden one being left out of the name, if the hidden class is being used for not-currently-active interactive elements in the super-tool-tip. BUT, (personal opinion) there's value in keeping it simple and always ignoring the hidden for labels and descriptions.

<label class="hidden" id="lbl1">foo <span aria-hidden="true">bar</span></label>
<input id="inp1" aria-labelledby="foo">

accname for inp1 is "foo"
There is no visible text
aria-hidden hides it from the accessibility tree.
"hidden" css hides it from the visual rendering

@JAWS-test
Copy link
Author

@cyns Your examples are not easy to follow, because the source code was marked so that it is not visible

@pkra
Copy link
Member

pkra commented Jun 15, 2021

I've edited @cyns comment to escape the html.

@cookiecrook
Copy link
Contributor

Weird… and interesting that all three engines have that behavior.

@cookiecrook
Copy link
Contributor

cookiecrook commented Jul 2, 2021

Hmmm… Nested hidden (display: none;) doesn't return either.

> $0
< <p>
"foo "
<span id="test1" hidden>bar</span>
<span id="test2" style="visibility: hidden;">baz</span>
" bop"
</p>
> $0.innerText
< "foo  bop"

This might be exactly what authors expect…

@cookiecrook
Copy link
Contributor

Not exactly what authors would expect, but this seems consistent between engines.

> $0.outerHTML
< "<p>
  rendered text node 
  <span>descendant element</span>
  <span hidden>display:none</span>
  <span style='visibility: hidden;'>visibility:hidden</span>
  <span aria-hidden=true>aria-hidden</span>
  rendered text node
  </p>"
> $0.innerText
< "rendered text node descendant element  aria-hidden rendered text node"

⬆️ When element is rendered, only the rendered, visible text contents are returned via innerText.

> $0.hidden = true
< true
> $0.innerText
< "rendered text node descendant element display:none visibility:hidden aria-hidden rendered text node"

⬆️ All text contents concatenated when element not rendered via display:none.

> $0.hidden = false
< false
> $0.style.visibility = "hidden"
< "hidden"
> $0.innerText
< ""

⬆️ All text contents omitted when element not rendered via visibility:hidden.

@jnurthen
Copy link
Member

jnurthen commented Jul 2, 2021

For reference here is the spec text https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute

@jaragunde
Copy link
Contributor

Thanks for the link to the spec. The behavior of visibility:hidden is understandable under its light because:

The innerText and outerText getter steps are:

  1. If this is not being rendered or if the user agent is a non-CSS user agent, then return this's descendant text content.

Where:

An element is being rendered if it has any associated CSS layout boxes, SVG layout boxes, or some equivalent in other styling languages.

An element with visibility:hidden has an associated layout box, hence it's considered "rendered" and follows the steps 2 and beyond:

  1. Let results be a new empty list.

  2. For each child node node of this:

    1. Let current be the list resulting in running the rendered text collection steps with node. Each item in results will either be a string or a positive integer (a required line break count).

Where "rendered text collection steps" say:

  1. Let items be the result of running the rendered text collection steps with each child node of node in tree order, and then concatenating the results to a single list.

  2. If node's computed value of 'visibility' is not 'visible', then return items.

The value of 'visibility' is 'hidden', hence it returns items, which is empty because we had just started.

@accdc
Copy link
Contributor

accdc commented Aug 6, 2021

Hi,
I've created the following pull request to include the new spec text for addressing this issue as we last discussed.
#137

This is proposed spec text at the moment, which hopefully will have implementor review if not feedback by the time of our next meeting regarding this on Aug 19.

@aleventhal
Copy link
Contributor

I missed this before. In this example, only the display:none has an innerText, but I would have expected visibility:hidden innerText to behave the same.

<div id="dn" style="display:none">disp-none</div>
<div id="vh" style="visibility:hidden">visi-hidden</div>

Therefore, this approach will break labels where authors thought that visibility:hidden was an acceptable way to hide a label.

Thoughts? Can we still rescue this innerText approach?

@cookiecrook
Copy link
Contributor

…this approach will break labels where authors thought that visibility:hidden was an acceptable way to hide a label.

Thoughts? Can we still rescue this innerText approach?

I think the way to do that would be to leave it as innerText in the ARIA spec, because it's consistent between browsers even though some details are unexpected by authors. Then the HTML spec could address each individual place (display:none for example) where innerText returns something unexpected...

@aleventhal
Copy link
Contributor

@cookiecrook visibility:hidden/collapse were the only unexpected ones, and returned ''. In these cases we can't use the innerText algorithm.
display:none returned the full innerText, so we can use it there.

@cookiecrook
Copy link
Contributor

In these cases we can't use the innerText algorithm.

Why not? Perfect is the enemy of the good.

I understand that some results were unexpected, including visibility:hidden and nested display:none. I’m suggesting there is benefit to using the existing innerText algorithm anyway. Interoperability being the main one.

If there’s a quirk in the innerText algorithm that behaves consistently between engines but is unexpected by authors, maybe we can agree on how the algorithm for innerText should change. If we can’t agree on how innerText should change, keep innerText the way it is. 🤷

Either way, we’d have browser interop and it would not require further update to the ARIA spec.

@aleventhal
Copy link
Contributor

I'm not aware that innerText behaves differently between browsers.
My concern is that we wouldn't really have a great reason to break existing content that uses aria-labelledby or aria-describedby with a visibility:hidden/collapse element. Breaking existing stuff is just bad IMO. We don't know how prevalent that is.

@cookiecrook
Copy link
Contributor

cookiecrook commented Sep 22, 2021

I'm not aware that innerText behaves differently between browsers.

Correct. But the AccName computation does behave differently between browsers. If we standardize the AccName algorithm to use innerText in these contexts, we'll have interoperability for those portions of the computation.

My concern is that we wouldn't really have a great reason to break existing content that uses aria-labelledby or aria-describedby with a visibility:hidden/collapse element. Breaking existing stuff is just bad IMO. We don't know how prevalent that is.

I'm aware Google has tools able to determine that prevalence. Is there precedence for providing that info into the public discussion? If so, would you be willing to gather and share it?

@aleventhal
Copy link
Contributor

aleventhal commented Sep 22, 2021 via email

@cookiecrook
Copy link
Contributor

Do you have an edit suggestion for #137?

@aleventhal
Copy link
Contributor

aleventhal commented Sep 22, 2021 via email

@cookiecrook
Copy link
Contributor

cookiecrook commented Sep 23, 2021

From the call, the examples requested are:

Rendered:

  • rendered on screen as author would expect
  • rendered but off screen (e.g. negative position offset)
  • "rendered" as visibility:hidden; (explain to authors the render box is reserving the element's layout position and bounds)

Not Rendered:

  • style="display: none;"
  • hidden (which is just implemented as display: none; in the UA style sheet)

@pkra
Copy link
Member

pkra commented Sep 24, 2021

Do we need a separate example for "rendered as visibility:hidden but with visible child"?

@cookiecrook
Copy link
Contributor

@aleventhal is working on a test implementation without innerText in Chrome, and will comment or update the PR (to replace the innerText reference in the current PR) once that's reasonably complete.

@aleventhal
Copy link
Contributor

Specifically, @jaragunde is working on it.

aarongable pushed a commit to chromium/chromium that referenced this issue Dec 7, 2021
The comments in AXObject::IsHiddenForTextAlternativeCalculation()
implied that, in case of obtaining a name out of a hidden subtree via
aria-labelledby or -describedby relations, it would exclude those
nodes that were explicitly hidden. This is an interpretation that does
not match the spec and, moreover, it was not possible to implement.
We cannot really tell if a node is directly hidden by style or if it
inherited that property, because that information gets optimized out
in the early steps of style computation and it would be costly to
retrieve.

The above was discussed extensively at:
w3c/accname#57. In particular, our problems
to find out if a node was explicitly hidden are shared with WebKit,
and Firefox does not work like that either.

The existing code was actually working, but it was not doing what
code comments implied to. I think the combination of these three facts
was what made the existing code work, although for wrong reasons:

* This condition in AXObject::IsHiddenForTextAlternativeCalculation():
  return IsHiddenViaStyle() &&
         (!ParentObject() || !ParentObject()->IsHiddenViaStyle());
  It detected the topmost node that gets hidden, preventing the name
  from contents algorithm from entering it when traversing a visible
  subtree.

* During an aria-labelledby traversal of a hidden subtree, the above
  condition was always false, which effectively exposed all nodes.

* This condition in AXObject::AriaTextAlternative():
  if (!aria_label_or_description_root &&
      IsHiddenForTextAlternativeCalculation())
    return String();
  Combined with the previous one, it detected when the target of an
  aria-labelledby relation was explicitly hidden and its parent was
  not, exposing its contents.

So we can say this is more a legibility change than anything else.
This patch makes explicit that we contribute all nodes inside a hidden
target of an aria-labelledby or -describedby relation, and changes the
conditions to detect this situation, removing the faulty heuristics.

Bug: 1255036
Change-Id: I80fac3ce883f9c165841636db3d6e4c8fa884260
AX-relnotes: simplify conditions for accessible name calculation
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3312490
Reviewed-by: Daniel Libby <dlibby@microsoft.com>
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Jacobo Aragunde Pérez <jaragunde@igalia.com>
Cr-Commit-Position: refs/heads/main@{#949109}
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
The comments in AXObject::IsHiddenForTextAlternativeCalculation()
implied that, in case of obtaining a name out of a hidden subtree via
aria-labelledby or -describedby relations, it would exclude those
nodes that were explicitly hidden. This is an interpretation that does
not match the spec and, moreover, it was not possible to implement.
We cannot really tell if a node is directly hidden by style or if it
inherited that property, because that information gets optimized out
in the early steps of style computation and it would be costly to
retrieve.

The above was discussed extensively at:
w3c/accname#57. In particular, our problems
to find out if a node was explicitly hidden are shared with WebKit,
and Firefox does not work like that either.

The existing code was actually working, but it was not doing what
code comments implied to. I think the combination of these three facts
was what made the existing code work, although for wrong reasons:

* This condition in AXObject::IsHiddenForTextAlternativeCalculation():
  return IsHiddenViaStyle() &&
         (!ParentObject() || !ParentObject()->IsHiddenViaStyle());
  It detected the topmost node that gets hidden, preventing the name
  from contents algorithm from entering it when traversing a visible
  subtree.

* During an aria-labelledby traversal of a hidden subtree, the above
  condition was always false, which effectively exposed all nodes.

* This condition in AXObject::AriaTextAlternative():
  if (!aria_label_or_description_root &&
      IsHiddenForTextAlternativeCalculation())
    return String();
  Combined with the previous one, it detected when the target of an
  aria-labelledby relation was explicitly hidden and its parent was
  not, exposing its contents.

So we can say this is more a legibility change than anything else.
This patch makes explicit that we contribute all nodes inside a hidden
target of an aria-labelledby or -describedby relation, and changes the
conditions to detect this situation, removing the faulty heuristics.

Bug: 1255036
Change-Id: I80fac3ce883f9c165841636db3d6e4c8fa884260
AX-relnotes: simplify conditions for accessible name calculation
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3312490
Reviewed-by: Daniel Libby <dlibby@microsoft.com>
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Jacobo Aragunde Pérez <jaragunde@igalia.com>
Cr-Commit-Position: refs/heads/main@{#949109}
NOKEYCHECK=True
GitOrigin-RevId: 276cddcc13f2fdd921dc9331f576058a77229bb1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment