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

tabindex vs reading-flow property #10642

Open
tabatkins opened this issue Sep 24, 2024 · 9 comments
Open

tabindex vs reading-flow property #10642

tabatkins opened this issue Sep 24, 2024 · 9 comments

Comments

@tabatkins
Copy link
Contributor

What is the issue with the HTML Standard?

Currently, the reading-flow PR has the behavior that 'reading-flow' makes the element a scope container for tabindex, and within that scope, tabindex ordering is consulted first, with visual-order giving tiebreaking between identical values. (I think that's a correct summary of the current behavior?)

During the CSS/WHATWG joint session at TPAC, we concluded that it would be better to make tabindex more subordinate to 'reading-flow'.

The exact proposed behavior is:

  1. On the "reading flow items", tabindex is ignored for ordering purposes (it still makes the items focusable as normal, etc). 'reading-flow' is explicitly taking over the ordering of these elements.
  2. On the descendants of the "reading flow items", tabindex's ordering behavior is scoped to that reading flow item subtree. (Aka the reading flow items are scope containers for tabindex, like the spec currently defines for the "reading flow container".)

The participants in the discussion believed this has a more predictable and desirable behavior for authors. Ignoring tabindex's ordering effects on the reading order items themselves seems useful; it allows authors to use tabindex to define one particular ordering, in particular a non-visual one (or one for legacy UAs), and then let 'reading-flow' override that in other cases.

Similarly, having each item be a scope for their descendant tabindexes seemed to better match the mental model we were working with, where 'reading-flow' effectively rearranges the reading order items.

@dizhang168
Copy link
Contributor

From the conversation at issue 10533, there are some good feedback about tabindex. Right now, tabindex is already ignored in the accessibility tree. This causes confusion for users when they switch from using focus navigation to the screen reader. Further, positive tabindex values are bad because it causes confusion to the users and causes the order to jump around. Developers historically have also not been reliable about updating the tabindex value when the visual order of elements change, because they are unfamiliar with this attribute and accessibility best practices in general. Since we should aim to have the screen reader and the visual order match as much as possible, ignoring tabindex on reading-flow items does make sense.

What I am unsure about is the second part of the proposal:

On the descendants of the "reading flow items", tabindex's ordering behavior is scoped to that reading flow item subtree. (Aka the reading flow items are scope containers for tabindex, like the spec currently defines for the "reading flow container".)

I would argue that this change would be more confusing to the users, since most people are not familiar with the concept of focus scope owners. They would expect the HTML tabindex attribute to work across the document and would find this restriction weird.

For example, should we visit all elements with tabindex=2 together?
Or visit each reading flow item scope individually (go from A -> A2 -> A1)?

<style>
.box {
  display: grid;
  reading-flow: grid-order;
}

</style>

<div class="box" id="w" tabindex="0">
  <div id="A" tabindex="0" style="order: 3">
    <button tabindex="1">Item A1</button>
    <button tabindex="2">Item A2</button>
  </div>
  <div id="B" tabindex="0" style="order: 1">
    <button tabindex="1">Item B1</button>
    <button tabindex="2">Item B2</button>
  </div>
  <div id="C" tabindex="0" style="order: 2">
    <button tabindex="1">Item C1</button>
    <button tabindex="2">Item C2</button>
  </div>
</div>

I think there are too many possibilities for complexity. Considering the existing limitations with positive tabindex, I would prefer not making reading flow items focus scope owner and instead make their descendants also ignore tabindex.

@dizhang168
Copy link
Contributor

dizhang168 commented Oct 22, 2024

I take back my objection above. I looked for existing web developer feedback and there are no existing issues asking to use tabIndex weirdly like in the example above. Instead, what I see most is people setting the same tabIndex on grid/flex item and its descendants so they can have a behavior similar to reading-flow. We are adding complexity, but it can be easily documented through HTML standard and will overall be more intuitive. The current proposal is good because the CSS reading-flow property was always meant to only affect the reading flow items, not its descendants. I expand more on my research and how to change the spec here.

High level, here is how this proposal changes the specification:

A reading flow scope owner can be one of 3 cases:

  • A reading flow container scope owner
    • A reading flow container (has flex/grid and reading-flow properties)
    • A display: contents element, whose layout box parent is a reading flow container
  • A reading flow item scope owner
    • A reading flow item, whose layout box parent is a reading flow container but itself is not

Elements in reading flow container scope owner follows a reading-flow focus navigation scope. This takes the layout order into account to re-order the reading flow items. If a reading flow item has a positive tabindex, set it to 0.
Elements in reading flow item scope owner follows a tabindexed-ordered focus navigation scope.

For example:
image

Edit: Updated the example with different tabindex values

Here, the focus order is:
Wrapper -> A -> B -> Display: contents -> F -> C -> E -> D -> H -> position: absolute -> G

@mfreed7 mfreed7 added the agenda+ To be discussed at a triage meeting label Oct 22, 2024
@past past removed the agenda+ To be discussed at a triage meeting label Oct 24, 2024
@dizhang168
Copy link
Contributor

Here is a Shadow DOM example, with the same focus navigation order. The wrapper is a shadow host and the display: contents div is a slot. Its content are slotted from the light DOM.
Because a slot has CSS display: contents, it will follow a reading flow container scope navigation.

image

@dizhang168
Copy link
Contributor

During the joint CSSWG+HTML meeting, we discussed this issue again. I would like to summarize the concerns here and share my thoughts.

@fantasai was advocating that setting the reading-flow value should have the same behavior as if it is not set IF the source and visual order are the same. That is currently not true because a container with reading-flow set will create focus navigation scopes (as described in this issue). Having this side effect feels off.

Here is an illustration:
https://software.hixie.ch/utilities/js/live-dom-viewer/saved/13300

Without reading-flow set, the order would be: t1, t2, t3.
With reading-flow set, we must follow the reading flow order, order is t3, t1, t2.

I believe the proposal of having reading flow create focus navigation scopes (which scopes tabindex) is the right approach because:

  1. Focus navigation scopes is already used by existing API such as slot, shadow DOM, iframes, popover.
  2. Reading flow is meant to change the focus navigation and using focus navigation scopes have been historically, how this is done in HTML
  3. Both reading-flow and tabindex are meant to overwrite following the source order. As such, we cannot avoid side effects and must define how they interact together.
  4. This proposal avoids jumping around during the focus navigation. We don’t have a good use case of wanting to jump between reading flow items' content. The goal is to keep it as close to the visual order as possible (or at least as close to the order CSS is telling us to use).
  5. Tabindex is a misfeature and the accessibility tree ignores it. We should avoid building logic that depends on it and avoid encouraging web developers to use it.

Overall it feels off (it did for me too, see comment 2 and 3 above!). But I do think this proposal is clean and will be easier/intuitive to use and maintain.

ccing @tabatkins @annevk @mfreed7 for their input

@dizhang168
Copy link
Contributor

Could we Agenda+ this to be resolved at the next WHATNOT meeting?

@chrishtr chrishtr added the agenda+ To be discussed at a triage meeting label Dec 10, 2024
@chrishtr
Copy link
Contributor

Could we Agenda+ this to be resolved at the next WHATNOT meeting?

Done. Your preferred resolution is no change from the existing PR's behavior, right?

@dizhang168
Copy link
Contributor

Correct, my proposal is to keep the existing spec-ed behavior in this issue's fist comment: #10642 (comment).

@past past removed the agenda+ To be discussed at a triage meeting label Dec 18, 2024
@cookiecrook
Copy link

@tabatkins wrote:

having each item be a scope for their descendant tabindexes seemed to better match the mental model we were working with, where 'reading-flow' effectively rearranges the reading order items.

For the last several decades, the generally regarded accessibility advice re: tabindex has been to avoid values greater than 0. A few non-exhaustive instances of that advice:

  • Deque: "A tabindex attribute must never have a value greater than 0 to prevent an unexpected tab order that can give the appearance of skipping some elements entirely."
  • A11y Collective : "Steer clear of positive tabindex values. The default behaviour of HTML already ensures that all interactive elements are accessible via the Tab key, following the document’s reading order. By sticking to this natural flow, you maintain consistency and predictability in navigation."

Using an even simpler version of @dizhang168's markup from above… (w/o the separately focusable, unlabeled container divs)

<div class="box" id="w">
  <div id="A" tabindex="0" style="order: 3">
    <button tabindex="1">Item A1</button>
    <button tabindex="2">Item A2</button>
  </div>
  <div id="B">
    <button tabindex="1">Item B1</button>
    <button tabindex="2">Item B2</button>
  </div>
  <div id="C">
    <button tabindex="1">Item C1</button>
    <button tabindex="2">Item C2</button>
  </div>
</div>

…the default behavior (before any browser change) would result in this highly illogical tab order in all existing browsers. Not really backwards compatible.

  • A1
  • B1
  • C1
  • A2
  • B2
  • C2

@dizhang168 wrote:

Considering the existing limitations with positive tabindex, I would prefer not making reading flow items focus scope owner

I agree with this part 👆(at least not automatically scoping.)

and instead make their descendants also ignore tabindex.

…but I don't make the jump to the same conclusion. 👆 At a minimum, I would NOT want browsers to ignore tabindex values of 0 or -1.

There are a number of other possibilities to consider.

  • positive tabindex values inside any reading-flow container could potentially be ignored, and effectively treated as tabindex=0 (perhaps this is what @dizhang168 intended?)
    • Consider a nested reading-flow container that overrides tabindex if needed.
  • Consider that, if reading-flow is used ignoring the tabindex content attribute on load, but allow it to be modified via the DOM property (e.g. content attribute would provide the backwards compatible path, but allow post-load fix-up via JS once feature detection had been performed)
  • Consider NOT trying to solve tabindex scoping when solving reading-flow… As noted above, use of positive tabindex is almost considered an anti-pattern in the accessibility space, so we should not recommend markup that causes significantly undesirable behavior existing implementations.
    • IOW, leave tabindex working as-is wrt reading-flow, and solve tab container scoping with one of the other proposals....

Probably more possibilities we haven't considered.

@dizhang168
Copy link
Contributor

Hi! Thank you for sharing your accessibility expertise. Some of the quoted bits of your comment come from before we resolved on Tab’s proposal: #10642 (comment) - I’ll try to clarify those below.

The current design is to only ignore positive tabindex ( > 0) on the reading flow items. A reading flow item is a focus scope owner and its descendants will be traversed in a tabindex-ordered focus navigation scope, where tabindex will take effect.
For example, in your example (which you can try out in https://jsbin.com/kefeca/edit?html,output):
This will return the nonsensical order: A1 -> B1 -> C1 -> A2 -> B2 -> C2.
With reading-flow set to “flex-flow” and having A/B/C be their own focus scopes, the reading order becomes B1 -> B2 -> C1 -> C2 -> A1 -> A2, which seems “correct” from the user’s point of view.

positive tabindex values inside any reading-flow container could potentially be ignored, and effectively treated as tabindex=0 (perhaps this is what @dizhang168 intended?)

Yes, that is the proposal. But only on the reading flow items, not its descendants.

Consider that, if reading-flow is used ignoring the tabindex content attribute on load, but allow it to be modified via the DOM property (e.g. content attribute would provide the backwards compatible path, but allow post-load fix-up via JS once feature detection had been performed)

Correct me if I’m wrong, but it seems like the general concern you have is about fallback behavior, in browsers that don’t yet support the reading-flow feature. Is that correct? If so, I agree that this is something developers will need to think about, but it seems like sites can add @supports blocks containing the styles that should be used when reading-flow is supported, and then add JavaScript code to detect a lack of support and insert tabindex attributes on the appropriate elements to “fix up” the tab navigation order appropriately..
As you mentioned, tabindex>0 should be discouraged, since it isn’t properly taken into account in the accessibility tree.

Further, note that there is a new CSS reading-order property added to the spec. It works similarly to tabindex, overwriting the natural reading-flow order of the reading flow items, but is also understood by the accessibility tree.

Consider NOT trying to solve tabindex scoping when solving reading-flow… As noted above, use of positive tabindex is almost considered an anti-pattern in the accessibility space, so we should not recommend markup that causes significantly undesirable behavior existing implementations.

I agree, the goal here is not to solve tabindex, but instead solve reading-flow without needing to depend on tabindex. However, given how focus navigation is written in the HTML spec, it is impossible for us to not define reading-flow in the context of an overall tabindex-ordered world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

7 participants