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

Please add "tab-index" to CSS specification #1748

Closed
Nadya678 opened this issue Aug 22, 2017 · 29 comments
Closed

Please add "tab-index" to CSS specification #1748

Nadya678 opened this issue Aug 22, 2017 · 29 comments

Comments

@Nadya678
Copy link

Nadya678 commented Aug 22, 2017

The needed specification:
tab-index: <numeric-signed> | none | inherit | unset;
If -1 or lower, the element is focusable by mouse/touch but not TAB key.
If 0, the element has default tab order and is focusable by mouse/touch.
If greater than 0, the number precise order in tab queue due to ascending values.
if "none" the element is not focusable by keyboard and not focusable via touch/mouse.

Reason:
The CSS including floating, position absolute, fixed and relative can change the visual order of elements, The developer shall have a tool to set appropriate TAB order for all elements. Additionally if any "modal" dialog "window" will be shown via CSS (it is possible!), here shall be possibility to disable tab-index (via "-1" value) in the background without changes in markup (HTML).

If it is needed, I can send my private (commercial) solutions for "modal" dialog "windows" that are using only HTML5 and CSS without any scripts. It is real requirement.

On the Stack Overflow you also can find questions how to disable element via CSS or how to change tabindex by CSS only.

BTW. Opera from 11.5 to 12 implemented this style as nav-index partially. Thus the name may also be: nav-index instead of tab-index.

Redirected from html thread: whatwg/html#2953

@Nadya678
Copy link
Author

Default value:

*
{
   tab-index: none;
}
input, textarea, a, button /* ... and other focusable by default */
{
   tab-index: 0;
}
[tabindex]
{
   tab-index: attr( tabindex );
}

@frivoal frivoal self-assigned this Aug 22, 2017
@frivoal frivoal added the css-ui-4 Current Work label Aug 22, 2017
@frivoal
Copy link
Collaborator

frivoal commented Aug 22, 2017

I agree that there is a need for controling the tabing order. As you said, layout can change the visual order of things. Often, it is still better to keep the tab order in the logical document order, but sometimes it is useful to reorder.

Since it depends on layout, I agree it should be done in css.

However, I don't think tab-index / nav-index is the right solution: the index is global to the page, so you need to be aware of all elements on the page to pick your number correctly, which is a huge pain. Its's been proposed before, and was removed as people (including browsers vendors) did not like that solution.

But that doesn't mean we don't need a solution. We do.

An alternative has been discussed, but not yet specified: nav-next and nav-pre. They would work with the same syntax as nav-up, nav-down, nav-left, and nav-right (see https://drafts.csswg.org/css-ui-4/#nav-dir).

This also allows you to reorder elements for tab navigation, but instead of needing to figure out a global order in your page, you can simply change the order locally. Specifying this is in my todo list.

It is used differently than tab-index / nav-index so it may take some time to get used to, but I hope that ends up being more usable and useful.

I understand it may be a little hard to evaluate without a full specification, but if you understand the idea, I'd appreciate your feedback about whether this seems like it would solve your use case, or whether you think it would have issues.

@Nadya678
Copy link
Author

Nadya678 commented Aug 22, 2017

Browsers correctly recognize tabindex="100" for many elements. They are tabbed then in the order of tags (after all other) elements. But tab-index can be also defined like "z-index" (similar behaviour) but then... the default value cannot be coded as CSS (like above) - local mode.

The tab-index is supported by all browsers identically except of firefox for input[type="radio"] if all are unchecked. Firefox tabs via all unchecked radio's, other browsers tabs only via first of group.

Thus this behaviour is the easiest to implement.
Ascending order - I used this term on purpose. We can treat [1, 2, 4, 8, 100, 105] as ascending and [1, 2, 2, 2, 3, 4, 5, 5, 5] also. Maybe "not descending order" will be more correct phrase?

My description can be updated by other users. I poorly speak english.

Nav-pre, nav-next (if there will be identifiers) can make infinite loop and trigger developers to "id-itis" ("id"-like div-itis/class-itis).

BTW. I tkink, the tab-index will by used mainly for elements with set "tabindex" property and form controls. In this purpose, here is a little elements for styling and developer for each skin (layout) can define it if it is possible.

If other solution is better, it also can be implemented instead this, but Opera (Presto, before WebKit) has implemented this algorithm (except "none"). Currently tab order in many layouts is chaotic and there is possible to access controls that should by inaccessible. It is more needed than pointer-events. Disabling of elements (for mouse and touch only) can be reached via negative z-index and additional layer (via ::after or ::before) on other element.

Does nav-pre/next is able to disable any element from tab-queue?

Regarding nav-up, nav-right, nav-down, nav-left - it is good idea for table DATA layouts (like online emulation of MS Excel) but... it generates id-itis. All elements need to have "id". Is there possibility to avoid of it (too many id's)?

@jihyerish
Copy link
Contributor

jihyerish commented Aug 23, 2017

I think nav-pre/nav-next is better design than nav-index.

For example, there are button elements like:

<button id="b1">Button 1</button>
<button id="b2">Button 2</button>
<button id="b3">Button 3</button>
<button id="b4">Button 4</button>
<button id="b5">Button 5</button>

if the tabing order is specified with nav-index, it will be:

button {
    position: absolute;
}
button#b1 {
    top: 20%; left: 25%;
    nav-index: 1;
}
button#b2 {
    top: 30%; left: 0%;
    nav-index: 2;
}
button#b3 {
    top: 50%; left: 40%;
    nav-index: 3;
}
button#b4 {
    top: 60%; left: 25%;
    nav-index: 4;
}
button#b5 {
    top: 70%; left: 20%;
    nav-index: 5;
}

otherwise, the same result using nav-pre & nav-next is:

button {
    position: absolute;
}
button#b1 {
    top: 20%; left: 25%;
    nav-next: #b2;
}
button#b2 {
    top: 30%; left: 0%;
    nav-prev: #b1;
    nav-next: #b3;
}
button#b3 {
    top: 50%; left: 40%;
    nav-prev: #b2;
    nav-next: #b4;
}
button#b4 {
    top: 60%; left: 25%;
    nav-prev: #b3;
    nav-next: #b5;
}
button#b5 {
    top: 70%; left: 20%;
    nav-prev: #b4;
}

If I want to add a new button element(#new) and make it have a tab order between the button #b2 and the button #b3, I have to modify the tab order of all the next button element after the button #new when using 'nav-index'.
But when using nav-pre & nav-next, I need to modify the tab order the very previous and next button of the button #new. This way is easier to use.

Regarding nav-up, nav-right, nav-down, nav-left - it is good idea for table DATA layouts (like online emulation of MS Excel) but... it generates id-itis. All elements need to have "id". Is there possibility to avoid of it (too many id's)?

I think we can just give the direction to find the previous or the next element to nav-pre or nav-next. So the previous or the next element will be the closest element in the direction.
For example, the value type can be ray() that specifies the straight line with an angle starting from the element.
nav-next: ray(45degree) means that the next element is the closest element which is at 45 degree angle from the current element.

@SebastianZ
Copy link
Contributor

@jihyerish Your example can be handled without defining any order, as the default order is how the elements appear in the DOM.

So, a better example is when you want to toggle through them in a different order. E.g. let's say we want the order to be #b1, #b3, #b5, #b2, #b4, then the CSS would look like this:

button#b1 {
  nav-index: 1;
}
button#b2 {
  nav-index: 4;
}
button#b3 {
  nav-index: 2;
}
button#b4 {
  nav-index: 5;
}
button#b5 {
  nav-index: 3;
}

vs.

button#b1 {
  nav-next: #b3;
}
button#b2 {
  nav-pre: #b5;
  nav-next: #b4;
}
button#b3 {
  nav-pre: #b1;
  nav-next: #b5;
}
button#b4 {
  nav-pre: #b2;
}
button#b5 {
  nav-pre: #b3;
  nav-next: #b2;
}

If I want to add a new button element(#new) and make it have a tab order between the button #b2 and the button #b3, I have to modify the tab order of all the next button element after the button #new when using 'nav-index'.

That's probably the biggest disadvantage of having an absolute index property. One big advantage is that it corresponds to the tabindex HTML attribute.

But when using nav-pre & nav-next, I need to modify the tab order the very previous and next button of the button #new. This way is easier to use.

As it probably doesn't make sense to specify two different navigation paths for forward and backward, I think only nav-next is needed. Applied to the example above this would then be:

button#b1 {
  nav-next: #b3;
}
button#b2 {
  nav-next: #b4;
}
button#b3 {
  nav-next: #b5;
}
button#b5 {
  nav-next: #b2;
}

And the backward navigation is automatically inferred from the forward navigation.

Regarding nav-up, nav-right, nav-down, nav-left - it is good idea for table DATA layouts (like online emulation of MS Excel) but... it generates id-itis. All elements need to have "id". Is there possibility to avoid of it (too many id's)?

I think we can just give the direction to find the previous or the next element to nav-pre or nav-next. So the previous or the next element will be the closest element in the direction.
For example, the value type can be ray() that specifies the straight line with an angle starting from the element.
nav-next: ray(45%) means that the next element is the closest element which is at 45 degree angle from the current element.

In my opinion, navigation should initially have specified that way, i.e. regarding the visual placement of the element for the default navigation order.

Your idea sounds quite interesting, though I'm not sure whether that's feasible. It requires people to calculate the angle between the elements to get the right order. What if there's no element in that direction?

In regard of my previous sentence, what about introducing a property that allows to specify a visual navigation order? E.g.:

nav-order: strutural | visual;

With visual just doing The Right Thing™ regarding writing mode.

Having said that, this might be introduced additionally to any explicit way to set a navigation index.

Sebastian

@Nadya678
Copy link
Author

Nadya678 commented Aug 23, 2017

<form>
   <input type="text" name="any1" />   
   <input type="text" name="any2" /><br/>   
   <input type="text" name="any3" />   
   <input type="text" name="any4" /><br/> 
   <input type="text" name="any5" />
</form>

input[type="text"]:nth-of-type(2n)
{
   tab-index: 1;
   width:300px;
}
input[type="text"]:nth-of-type(2n+1)
{
   tab-index: 2;
   width:300px;
}

It cannot be modified via nav-pre and nav-next without addition of identifiers (modify of markup).

ray() is also bad for scrolled pages with fixed-positioned elements. the ray() can be resolved differently in this case due to scrollbar position. It also cannot be resolved if width of site isn't constant and part of elements is displayed as inline-table or inline-block (or inline for non-replaced elements with tabindex set to equal or greater than 0).

The nav-index/tab-index hasn't these problems.

I always starting coding of page write the markup (HTML) for the page SEMANTICALLY. Next Iwant to use CSS (and only CSS if it is possible). Next step: required JS if client needs to reload fragment of page etc.). I like to use ::after, ::before, advanced CSS. I'm annoying when I must change markup (and thus part of CSS) to add additional ID's, <DIV/>s etc.

For example we have WordPress, Drupal AUTOUPDATED page, we want only use CSS to change visual effects. These visual effects result in chaotic tabbing. What is the best solution? We have not ID on all controls. the ray() is in part of layouts not constant (fixed-positioned elements).
If the page is autoupdated (to security fixes) your IDs can be removed during update but CSS not if is correctly linked (in appropriate file that is not updated).

@Nadya678
Copy link
Author

Nadya678 commented Aug 24, 2017

Regarding nav-order: strutural | visual; it is good idea but still part of elements are fixed positioned, transitioned, animated. The position can change dynamically.

Part of elements after displaying static "dialog" box shall be disabled via other mechanism. tab-index=-1 do it currently.

Btw. I don't want to calculate degrees (rays).

Is here possibility for nav-top/nav-bottom etc set full selector, not only ID selector? Handling 4 arrow in CSS has sense.

BTW. For the moment none browser is able to detect if backface-visibility is hidden and object rotated more than 90deg and invisible. TAB key still works on hidden elements (except if visibility is set to hidden). The backface-visibility has no sense I still must add in transitions "animation" for visibility and thus... I can remove backface-visibility.

@bradkemper
Copy link
Contributor

I agree that needing to add IDs to everything I want to change the tab order on would be a gigantic pain. I almost might as well add tabindex attributes, if I have to change markup anyway. I would consider this a non-starter.

you need to be aware of all elements on the page to pick your number correctly,

That could mostly be taken care of with a reset, like this:

input, a, select, textarea, button, iframe { tab-index: 100; }

Which would be way easier than the current situation with tabindex attributes. But even so, it is not ideal for when you wanted to change the order within one limited area, without having to type in numbers for everything after that.

It would be better, if we could isolate a group of elements to set tab order on. Something like this, maybe:

fieldset { tab-index-group: isolate; }
fieldset input { tab-index: 2; }
fieldset input:first-of-type { tab-index: 3; }
fieldset input:last-of-type { tab-index: 1; }

The result of that would be that the tab-index values would only affect elements within that container.

PS: even if it was only being able to set tab-index 0 and -1 values via CSS, that would be huge by themselves.

And having a 'none' value would also be cool, if it was like 'pointer-events:none', but also would stop propagation of focusing to items below it in the z direction.

@SebastianZ
Copy link
Contributor

I've split out my idea for a property influencing the default navigation order to #1764, so it can be discussed separately.

The tab group is a good idea to limit the indexes to a part of the element structure. It mitigates @jihyerish's issue about having to change the index of all following elements when inserting an element in between. It is still an issue for bigger groups, though.

Another idea would be to combine both, relative and absolute indexing in one property. The syntax for that could then look like this:

nav-index: <integer> | after(<complex-selector>)

Allowing complex selectors instead of only ID selectors also provides more flexibility and doesn't require to add IDs to the elements.

Sebastian

@SebastianZ
Copy link
Contributor

One more point, can we agree on nav- (or navigation-) as prefix? tab- sounds restricted to the Tab key.

Sebastian

@Nadya678
Copy link
Author

Nadya678 commented Aug 29, 2017

tab-index-group:isolate is not a good because I don't want to prevent TAB stops on the browser gui elements. Isolating may be annoying for users without mouse and without touch screen.

I currently set visibility hidden for input type radio and checkbox. For "a" elements I style ::before and ::after and hide "a". This prevent tab stops for check boxes (still visible label) and "a" (stil visible before or span for IE) but... the text-box and password and textarea doesn't accept ::before and additionally visibility:hidden will hide text. I only can simulate tab-index: none for radios, checkbox and "a" element.

nav-index: after(<complex-selector>)

how to find element that shall be focused with shift+tab key?

@jihyerish
Copy link
Contributor

jihyerish commented Aug 30, 2017

So, a better example is when you want to toggle through them in a different order. E.g. let's say we want the order to be #b1, #b3, #b5, #b2, #b4, then the CSS would look like this:

Thanks, @SebastianZ ! It's better. : )

Your idea sounds quite interesting, though I'm not sure whether that's feasible. It requires people to calculate the angle between the elements to get the right order. What if there's no element in that direction?

Yes, it's tricky for selecting the element to focus. Maybe if there is no element in that direction, then the closest element from the ray can be focused.

nav-order: strutural | visual;

The strutural value means the order of the DOM tree?

One more point, can we agree on nav- (or navigation-) as prefix?

Completely agree with you. 👍

@liamquin
Copy link

liamquin commented Aug 30, 2017 via email

@Nadya678
Copy link
Author

Nadya678 commented Aug 30, 2017

For me the nav-index or tab-index or navigation-index is no problem. For the moment no browser support it. Last versions of Presto supported nav-index to "manipulate" tabindex attribute. Thus I sugested the tab-index with hyphen (like attribute name, some people write "tabIndex" for the attribute) or nav-index - implemented in last versions of Presto (Opera version 11.5-12.0).

But... tabindex attribute support is shortened to define actions with TAB or SHIFT-TAB key. What browser supports other heys with use of tabindex?

@tomhodgins
Copy link

If you want to test out what using a tab-index:; property would be like, I've explored this idea before and written a plugin that allows CSS authors to set tabindex="" properties on elements from CSS, even responsively.

The plugin works as intended, but after actually trying out the functionality in browsers, seeing what it does and how browsers handle tabindex="" attributes versus how pages function without any tabindex="" set, I never went ahead and officially released this—I don't even use it myself.

Setting tabindex="" from CSS sounds like a good idea and it's a lot of fun to explore, but I wasn't able to detect any usable result at the end of this exploration that I thought would be a benefit if added to CSS. Try out the plugin yourself and see.

The only use case I could think of where it might be a help is in the case where you have re-ordered items using flexbox (which you shouldn't do…) and want to 'repair' a more natural tabindex="", but I fear this would allow you to repair just the tabindex="" while keeping the reading order of the rest of the content in a messed up order. Thinking more about that use case, setting a correct tabindex="" on messed up content might mess it up further. Maybe it's not even good in that case! 🤔💭

Problems

  • setting tabindex="" in HTML takes navigational control away from users, because of this I've heard accessibility people recommend against using tabindex="" in HTML, and I believe some accessibility tools will entirely ignore it even if it's present. Is tabindex="" even a useful HTML attribute today?

  • If we expose the ability to set or change tabindex="" values from CSS wouldn't that mean taking even more navigational control from the user than just setting tabindex="" in HTML alone?

  • How do we decide which value(s) to assign when a CSS selector matches more than one element in the document, like * { tab-index: 2; }

Video

Code

<section>
  <input placeholder=first>
  <input placeholder=second>
  <input placeholder=third>
</section>

<style>
  input {
    display: block;
    width: 33.33%;
    float: left;
    font-size: 14pt;
    padding: .5em;
  }

  [placeholder=first] {
    --tab-index: 3;
  }
  [placeholder=second] {
    --tab-index: 2;
  }
  [placeholder=third] {
    --tab-index: 1;
  }

  @media (min-width: 600px) {
    body {
      background: tan;
    }

    [placeholder=first] {
      --tab-index: 2;
    }
    [placeholder=second] {
      --tab-index: 1;
    }
    [placeholder=third] {
      --tab-index: 3;
    }
  }

  @media (min-width: 1200px) {
    body {
      background: pink;
    }

    [placeholder=first] {
      --tab-index: 3;
    }
    [placeholder=second] {
      --tab-index: 1;
    }
    [placeholder=third] {
      --tab-index: 2;
    }
  }
</style>

<script>
  /*

  # Tabby
  ## version 0.0.1

  Author: Tommy Hodgins

  License: MIT

  */

  const tabby = {}

  tabby.load = () => {

    tabby.findRules()

  }

  tabby.findRules = () => {

    // For each stylesheet
    Array.from(document.styleSheets, sheet => {

      // For each rule
      sheet.cssRules && Array.from(sheet.cssRules, rule => {

        tabby.process(rule)

      })

    })

  }

  tabby.process = rule => {

    // If rule is a qualified rule, process it
    if (rule.type === 1) {

      let node = tabby.transform(rule)

      document.querySelector(node.tag).setAttribute('tabindex', node.value)

    }

    // If rule is an at-rule, find all qualified rules inside
    if (rule.type === 4) {

      // Remember media query text
      let mediaText = rule.media.mediaText

      // If there are qualified rules, find all rules
      rule.cssRules && Array.from(rule.cssRules, mediaRule => {

        let node = tabby.transform(mediaRule)

        if (window.matchMedia(mediaText).matches) {

          document.querySelector(node.tag).setAttribute('tabindex', node.value)

        }

      })

    }

  }

  tabby.transform = rule => {

    let selector = rule.selectorText.replace(/(.*)\s{/gi, '$1')
    let ruleText = rule.cssText.replace(/.*\{(.*)\}/gi, '$1')

    // For each rule, search for `--tab-index`
    let index = ruleText.replace(/(--tab-index:\s*)(\d*)(\s*\!important)*\s*(?:;|\})/i, (string, property, value, important) => {

      return parseInt(value)

    })

    return {"tag": selector, "value": index}

  }

  // Update every `load`, `resize`, `input`, and `click`
  window.addEventListener('load', tabby.load)
  window.addEventListener('resize', tabby.load)
  window.addEventListener('input', tabby.load)
  window.addEventListener('click', tabby.load)
</script>

@Nadya678
Copy link
Author

Nadya678 commented Aug 30, 2017

Is tabindex="" even a useful HTML attribute today?

Yes. It is useful. The most useful values are tabindex="0" for example on <span/> or <div/> element and tabindex="-1" to disable tab stops or make elements focusable without tab-navigation. I created HTML+CSS libraries that use this.

The tabindex="none" (currently not defined) will able used to make "disabled" visual/behaviour effect for input, area, select etc. Disabled vithout disabling them from sending via http.

Thank you for JS but I want NOT to use JS to change navigation behaviour if I need "modal" dialog window with a form. Here is needed CSS equivalent.

@bradkemper
Copy link
Contributor

@Nadya678 I didn't mean that tab-index-group:isolate would prevent tabbing outside that group. I meant that once you had tabbed into the group, the tab-index property would only be affecting the tab order within that group.

So suppose you had tab-index: 0 for every tabbable element outside the group, and 3, 2, and 1 respectively for the values inside the group. You would tab into the group in DOM order, but the first one in the group to tab to would be the one with tab-index:1.

@bradkemper
Copy link
Contributor

bradkemper commented Aug 30, 2017

You could also have an index on the group element itself, not to cause the grouping box to receive focus, but to set the where in the tabbing order the descendants of the box fall in relation to items outside that box. This would give the author a lot of power to control the tabbing at micro and macro levels.

For instance:

<input id=a>
<input id=b>
<input id=c>
<div>
    <input id=d>
    <input id=e>
</div>

<style>
    #a { tab-index:0; }
    div { tab-index-group: isolate; tab-index:1; }
    #b, #c { tab-index: 2; }
    div #d { tab-index: 1; }
    div #e { tab-index: 0; }
</style>

The order would be a, e, d, b, c

@bradkemper
Copy link
Contributor

Almost like the inputs inside of the tab-index:1 box are tab-index 1/1 and 1/0.

@Nadya678
Copy link
Author

Nadya678 commented Aug 31, 2017

The more appropriate tab order in this case will be:
div, #d, #e #b, #c, #a (0 means first free number)
maybe tab-index-group: <number> (default 0: first group) will be better for grouping.

<style>
    #a { tab-index:0; }
    div { tab-index-group: 1; tab-index:none;}
    #b, #c { tab-index: 2; }
    div #d { tab-index: 1; }
    div #e { tab-index: 0; }
</style>

("tab-" may by changed to "nav-"). It will be still compatible with tabindex in HTML.

@bradkemper
Copy link
Contributor

Ugh. Yeah, I forgot 0 comes after all the positive numbers when using tabindex. It is so unintuitive, but I agree that diverging would probably be worse.

I don't have a problem with nav-index or tab-index as names. I prefer tab-index because it makes it easier to understand as a corollary of the attribute.

IMO, tab-index-group: 1; in your example makes it seem like all DIVs are in the same group, rather than each forming its own group.

@Nadya678
Copy link
Author

Nadya678 commented Aug 31, 2017

Summary of discussion about tab-index/nav-index, tab-index-group, nav-up, nav-left, nav-top, nav-right.

Please remember, the TAB key is the the most important way for navigation, the nav-up etc. is only auxiliary way (works for the moment only for radiobutton gruop and option in select?)

tab-index: <signed number> | none| inherit | unset | initial;
tab-index-group: <signed number> | inherit | unset | initial;

The default values:

*
{
   tab-index: none;
   tab-index-group: inherit;
}
input, textarea, a, button /* ... and other focusable by default */
{
   tab-index: 0;
}
[tabindex]
{
   tab-index: attr( tabindex );
}
html
{
   tab-index-group: 0;
}

Specification:
tab-index: 0 - means "use first free number in range [1..infinite] in DOM document". The element is accessible via keyboard TAB/SHIFT+TAB keys and is focusable. 0 is converted to any number due to DOM position.
tab-index: none- means The element is unfocusable (also via keyboard)
tab-index: <negative number> means: the element is focusable only via keyboard. It is removed from tab queue. In this case tab-index**-group** can pointer to start tab queue after using TAB key if the element is focused.
tab-index: : the non-descending position in TAB queue. If two or more elements has the same tab-index, there is used internal TAB queue according to DOM position.

tab-index-group: enclose elements in tab group. tab-groups are arranged in non descending behaviour.

An example (with inline styles for easier understanding):

<form id="e1" style="tab-index:none;">
   <input type="text" style="tab-index:2;" id="e2"/>
   <input type="text" style="tab-index:4;" id="e3"/>
   <input type="text" style="tab-index:0;" id="e4"/>
   <input type="text" style="tab-index:0;" id="e5"/>
   <div id="e6" style="tab-index:-1; tab-index-group:2">
      <input id="e7" type="text" style="tab-index:3" />
      <input id="e8" type="text" style="tab-index:0" />
      <div id="e9" style="tab-index:-1; tab-index-group:1">
         <input id="e10" type="text" style="tab-index:1" />
      </div>
   </div>
   <input id="e11" type="text" style="tab-index-group:-3; tab-index:100;" />
   <input id="e12" type="text" style="tab-index-group:-3; tab-index:100;" />
</form>

translated to group/index:
e1: removed from queue
e2: 0/2,
e3: 0/4
e4: 0/1 (first free number)
e5: 0/3 (first free number)
e6:2/-1 (removed from queue)
e7: 2/3
e8: 2/1 (first free tab-index in the group 2)
e9: 1/-1 (removed from queue)
e10: 1/1
e11: -3/1
e12: -3/1 (but after e11 due to DOM model)

Tab-order (ascending): #e11, #e12, #e4, #e2, #e5, #e3, #e10, #e8, #e7, [browser GUI] .

Navigation with arrows:

nav-top: "<CSS3 selector>" | parent "<CSS3 selector>" | grand-parent "<CSS3 selector>" | inherit | none | unset | initial; /* pressing of top arrow */ 
nav-left: "<CSS3 selector>" | parent "<CSS3 selector>" | grand-parent "<CSS3 selector>" | inherit | none | unset | initial; /* pressing of left arrow */ 
nav-right: "<CSS3 selector>" | parent "<CSS3 selector>" | grand-parent "<CSS3 selector>" | inherit | none | unset | initial; /* pressing of right arrow */ 
nav-bottom: "<CSS3 selector>" | parent "<CSS3 selector>" | grand-parent "<CSS3 selector>" | inherit | none | unset | initial; /* pressing of bottom arrow */ 

Note: here can be recognized (by nav-up, nav-down, nav-left, nav-right) only elements with tab-index other than none.

Behaviour of detection:
(for LTR reading):
nav-down and nav-right: first FOCUSABLE element found in selected set after the current element. If no element is found after, then the first* element in selected set.
nav-up and nav-left: first FOCUSABLE element found in selected set before the current element. If no element is found before, then the last* element in selected set.
If no element is found the arrow key do nothink or do default browser action.

* last and first: if any selector inside current element meet the selector the "first" and "last" words points it. If no element inside current element meets the definition, the words mean "first inside document" and "last inside document".

Option parent: the CSS set is calculated via currentElement.parentNode.querySelectorAll(selector) instead of document.querySelectorAll(selector).

If selector contains "\" or double quote - it shall be escaped via back-slash (0x5C) character.

<CSS selector> can contain comma to specify multiple selectors, for example a, div>b, p+p.

default value:

*
{
   nav-top:none;
   nav-bottom:none;
   nav-left:none;
   nav-right:none; 
}
input[type="text"], input[type="password"], input[type="email"], input[type="tel"] /* , etc.*/
{
   /* to prevent discord with edition of text */
   nav-left: none !important;
   nav-right: none !important;
}
textarea
{
   /* to prevent discord with edition of text */
   nav-left: none !important;
   nav-right: none !important;
   nav-left: none !important;
   nav-top: none !important;
}
select
{
   nav-up: parent "select option:not(:disabled)";
   nav-down: parent "select option:not(:disabled)";
}
option
{
   nav-down: parent "option:not(:disabled)";
   nav-up: parent "option:not(:disabled)";
}
optgroup>option
{
   nav-down: grand-parent "option:not(:disabled)";
   nav-up: grand-parent "option:not(:disabled)";
}
input[type="radio"]:checked
{
   /* the initial definition shall use "name" of this element. the attr(name) cannot 
   be used in selector. The browser shall select next/previous radio due to nav-left 
   and nav-right and this shall not be possible  to change*/
}

if RTL reading is set the behaviour for nav-right and nav-left are swapped (words: after and before shall be swapped).

Does it have a sense? Can we ask Google, Mozilla and Microsoft developers what problem they see and how many work it needs?

If tab-index-group is really needed, I propose "tab-group" name for it. It is shorter.

@jihyerish
Copy link
Contributor

jihyerish commented Aug 31, 2017

I think it's better to discuss about tab-index(or nav-index) and nav-top, nav-bottom, nav-left, nav-right separately because they works in different way.
The nav-index is the sequential navigation, but nav-top, nav-bottom, nav-left, nav-right is the directional navigation.
Finding the next element is simple in the sequential navigation.
But in the spatial navigation, we need to consider the visual order on the screen that is much more complex than RTL or LTR.

@SebastianZ
Copy link
Contributor

SebastianZ commented Aug 31, 2017

nav-index: after(<complex-selector>)

how to find element that shall be focused with shift+tab key?

As I indicated earlier, it is implied by the forward order. So, in the case of the after() function, the element that is focused by pressing Shift+Tab (going backwards in the order) is the first one matched by the selector of that function.
If the selector doesn't match any element, nav-index is interpreted as it weren't defined.

nav-order: strutural | visual;

The strutural value means the order of the DOM tree?

Yes. I'm open for better names.

The CSS including floating, position absolute, fixed and relative can
change the visual order of elements, The developer shall have a tool
to set appropriate TAB order for all elements.

Very good point! It's an important point to think of vision impaired people in regard of this feature.

Won't this be really confusing for someone using a text reader, e.g.
someone blind?
the DOM order isn't changed, and that's what the text
reader will use. So the right thing isn't to change the tab order to be
different from the document order, perhaps, but to tell the browser
that the document order has (in a sense) changed. What do you think?

How would you do that?

When talking about the "visual order" for the navigation path, I see two solutions for screen readers:

  1. Use the DOM order.
  2. Follow the layout algorithm and also use the visual order.

Having said that, I wonder if screen readers currently interpret the tabindex HTML attribute.

Of course, this also applies to the nav-order property I suggested in issue #1764.

Sebastian

@Nadya678
Copy link
Author

complex-selector only for "up, left, right, down". For nav-index/tab-index there are no complex-selector. It is equivalent of HTML tabindex attribute with additional "none" (non-focusable). Thus Shift+tab is no problem.

nav-order - is it needed if tab-index/nav-index (and tab-index-group) will be implemented? How should work "Visual" for fixed positioned elements? If it will be implemented, the one medium shall be "screen/tv".

Yes, screen readers interpret the tabindex including "-1". Thus the tab-index / nav-index shall be easy to implement.

@bkardell
Copy link
Contributor

bkardell commented Sep 13, 2017 via email

@SebastianZ
Copy link
Contributor

SebastianZ commented Sep 13, 2017

complex-selector only for "up, left, right, down". For nav-index/tab-index there are no complex-selector. It is equivalent of HTML tabindex attribute with additional "none" (non-focusable). Thus Shift+tab is no problem.

I don't get your reason why you are against a selector for the nav-index property.

nav-order - is it needed if tab-index/nav-index (and tab-index-group) will be implemented? How should work "Visual" for fixed positioned elements? If it will be implemented, the one medium shall be "screen/tv".

Yes, nav-order is independent of nav-index, defining the default navigation order. This and what "visual" means in this context is discribed in #1764.

When talking about the "visual order" for the navigation path, I see two solutions for screen readers:

  1. Use the DOM order.
  2. Follow the layout algorithm and also use the visual order.

Having said that, I wonder if screen readers currently interpret the tabindex HTML attribute.

Can you explain what you mean by that? As in screen readers 'interpreting' tabindex?

I never used a screen reader. So, I am wondering how they read a page. Do they read the whole page at once? And if so, do they consider the tabindex attribute set on elements, i.e. do they read text of focusable elements in navigation order or in DOM order? Or do they just read the text of the currently focused element, meaning they implicitly follow the navigation order?

Sebastian

@robdodson
Copy link
Contributor

robdodson commented Nov 7, 2017

I meant that once you had tabbed into the group, the tab-index property would only be affecting the tab order within that group.

For what it's worth, this behavior is available today if you're using Shadow DOM. Here's an example (need to view it in Chrome): https://jsbin.com/lidaguf/edit?html,output

I never used a screen reader. So, I am wondering how they read a page. Do they read the whole page at once?

Generally speaking, screen readers navigate the page in DOM order. Think of it kind of like linearly going from element to element. Just navigating with the Tab key would not be useful because that would only move between interactive controls, so a screen reader user would miss all the other content on the page.

My very rough understanding is when a user lands on an unfamiliar page they will typically use a shortcut to navigate by headings (h1-h6 tags). When they find an interesting heading, they will then drill into that section, navigating in a linear fashion, going element by element or line by line.

And if so, do they consider the tabindex attribute set on elements

A user could press the tab key to jump to the next interactive element, but if they're just browsing through text content they'd be doing so in DOM order.

@frivoal
Copy link
Collaborator

frivoal commented Nov 15, 2017

Based on the CSS-WG having decided to push this general topic to WICG (see meeting minutes), I'm closing this issue in favor of WICG/spatial-navigation#10.

To be clear, this is not meant to be a rejection of what has been discussed here, only moving the discussion to a group that is tailored for this problem space and to which the CSSWG has decided to move the discussion to.

This specific thread has been documented in the wiki page dedicated to collecting discussions on this topic.

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

No branches or pull requests

9 participants