focusgroup (Explainer)
Authors: Travis Leithead, David Zearing, Chris Holt
Introduction
Authors create many interactive control patterns that expect or can benefit from keyboard-accessible behavior such as arrow-key navigation.
This proposal enables authors to add a set of built-in keyboard behaviors for moving focus among a list or grid of focusable elements without having to write common boilerplate code (e.g., roving tabindex) to do so in their custom controls.
Authors may use the proposed focusgroup
HTML attribute (or related CSS properties) to declare that a subtree of focusable elements will get:
- focus navigation (not selection) using keyboard directional arrow keys (for lists or grids)
- a guaranteed tab stop (when at least one focusable element is present)
- automatic return to the last focused focusable element
- start/end navigation (with Home/End keys)
- optional limited-axis arrow key navigation and optional wrap-around semantics
By standardizing focusgroup
, authors can leverage these behaviors in control patterns to provide users with keyboard consistency, default accessibility, and interoperability over existing solutions.
While this document emphasizes the usage of the keyboard arrow keys for accessibility navigation, we acknowledge that there are other input modalities that work (or can be adapted to work) equally well for focusgroup navigation behavior (e.g., game controllers, gesture recognizers, touch-based ATs, etc.)
Quickstart
focusgroup
enables authors to provide keyboard roving tabindex with almost no code. Compare the web platform’s native radio button group with a custom control using the radio group pattern.
A platform native radio group:
<p>Choose your pet:</p>
<div id=radiogroup>
<label>Dog <input type=radio name=mygroup value=dog></label>
<label>Cat <input type=radio name=mygroup value=cat></label>
<label>Turtle <input type=radio name=mygroup value=turtle></label>
</div>
A custom radio group using focusgroup
:
<p id=label>Choose your pet:</p>
<div role=radiogroup focusgroup onfocusin="/*update aria-checked values*/" aria-labelledby=label>
<span role=radio aria-checked=false tabindex=-1>Dog</span>
<span role=radio aria-checked=false tabindex=-1>Cat</span>
<span role=radio aria-checked=false tabindex=-1>Turtle</span>
</div>
What to notice:
focusgroup
enables down/right arrow keys to advance the focus to the next focusable element in DOM order (up/left keys will move the focus in reverse) just like in the native radio group.tabindex
is still required to make the<span>
elements focusable (thefocusgroup
attribute doesn’t make any elements focusable by default, including the<div>
it’s declared on).- All the
tabindex
values are set to “-1” and no code has to update those values.focusgroup
can set keyboard focus on elements declared focusable viatabindex="-1"
via arrow keys.tabindex="-1"
still prevents these elements from being part of sequential focus navigation (e.g., the “tab sequence”)—with one exception:focusgroup
provides a special behavior that enables one focusable item within the group to get keyboard focus via sequential focus navigation even if all the focusable elements within are opting out of sequential focus navigation (i.e., all “focusables” are settingtabindex=-1
). - The
onfocusin
event handler is present becausefocusgroup
does not manage selection state (and native radio groups do have selection linked to focus). - In the native radio group, repeatedly pressing the down arrow key will eventualy wrap the focus back around to the first focusable element. In the
focusgroup
example, focus will stop on the last focusable element. (But there’s an option to turn on wrapping if desired.) - If the user leaves the focusgroup (via tab key) and returns again, the last element that was focused previously will be the focused element on reentrance (it is remembered).
In this next example, the author is using a tab control pattern where the tab activation behavior is decoupled from selection (“manual tab activation”):
<div role=tablist focusgroup="inline wrap no-memory" aria-label="Common Operating Systems">
<button id=tab-1 type=button role=tab aria-selected=false aria-controls=tabpanel-1 tabindex=-1>Mac</button>
<button id=tab-2 type=button role=tab aria-selected=true aria-controls=tabpanel-2 tabindex=0>Windows</button>
<button id=tab-3 type=button role=tab aria-selected=false aria-controls=tabpanel-3 tabindex=-1>Linux</button>
</div>
<div id=tabpanel-1 role=tabpanel tabindex=0 aria-labelledby=tab-1 hidden> … </div>
<div id=tabpanel-2 role=tabpanel tabindex=0 aria-labelledby=tab-2> … </div>
<div id=tabpanel-3 role=tabpanel tabindex=0 aria-labelledby=tab-3 hidden> … </div>
What to notice:
- Sequential focus navigation within a
focusgroup
is respected. When entering thefocusgroup
, focus will always go to the first selected tab (withtabindex=0
). Theno-memory
value prevents the focusgroup from remembering the last focused tab so that focus will always go to the selected tab on reentrance regardless of which element was focused last. - If focus is moved via left arrow key to
tab-1
, then pressing the tab key moves focus to the selected tab, which is next in sequential focus navigation order. Pressing tab again moves the focus totabpanel-2
which is next in sequential focus navigation order (because the otherrole=tabpanel
s arehidden
). - focus will
wrap
from one end of the tablist to the other because offocusgroup=wrap
attribute value. - the up and down arrow keys will not move the focus because of
focusgroup=inline
which restricts the axis of movement to keyboard directional arrow keys in therole=tablist
’s inline direction (assuming the<div>
’swriting-mode
ishorizontal-tb
). - The author code required to manage the selection of a tab is omitted. Such code on tab selection change would update
aria-selected
values, thehidden
state of the controlledrole=tabpanel
and thetabindex
values of tabs such that the newly selectedrole=tab
element istabindex=0
while all others aretabindex=-1
.
In a third example, the author is creating a navigation menubar. Both menuitem
s in the menubar
(“About” and “Admissions”) have popover menu
s. The “Admissions” menu has an additional submenu under “Tuition”.
<ul role=menubar aria-label="Mythical University" focusgroup="inline wrap">
<li role=none>
<a role=menuitem popovertarget=aboutpop href="…" tabindex=-1 title="">About</a>
<ul role=menu focusgroup="block wrap" autofocus id=aboutpop aria-label=About popover>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Overview</a></li>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Administration</a></li>
</ul>
</li>
<li role=none>
<a role=menuitem popovertarget=admitpop href="…" tabindex=-1 title="">Admissions</a>
<ul role=menu focusgroup="block wrap" autofocus id=admitpop aria-label=Admissions popover>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Apply</a></li>
<li role=none>
<a role=menuitem popovertarget=tuitpop href="…" tabindex=-1 title="">Tuition</a>
<ul role=menu focusgroup="block wrap" autofocus id=tuitpop aria-label=Tuition popover>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Undergraduate</a></li>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Graduate</a></li>
</ul>
</li>
<li role=none><a role=menuitem href="…" tabindex=-1 title="">Visit</a></li>
</ul>
</li>
</ul>
What to notice:
focusgroup
declarations can be nested inside of other focusgroups. When a nested focusgroup is declared on an element, it creates a new focusgroup and opts-out of its ancestor focusgroup.- menuitems in
role=menubar
are limited to inline-direction arrow keys (e.g., left and right), while menuitems inrole=menu
are limited to block-direction arrow keys (e.g., up and down). This allows the orthogonal arrow keys (e.g., up and down on the menubar, left and right on the menus) to be used for activation purposes (extra code that is not shown in the example). - Placement of focus on the menus (the nested focusgroups) from the menubar is not a feature of
focusgroup
(nested focusgroups are completely independent of their ancestor focusgroup). In this case, the focus placement is handled by built-inpopover
andautofocus
attribute behaviors. - the “memory” of the nested
focusgroup
s is reset when the content is hidden/shown—this allows theautofocus
attribute to pick the first focusable element each time a menu is shown—the desired behavior in this case.
Here, the author is adding up/down arrow key behavior to focusable headings using an accordion pattern. The arrow keys move the focus between the headings, but skip anything in the accordion bodies.
<div focusgroup=block>
<h3><button type=button aria-expanded=true aria-controls=sec1 id=h1>Heading 1</button></h3>
<div focusgroup=none role=region aria-labelledby=h1 id=sec1>
… accordion panel w/form controls that are Tab focusable …
</div>
<h3><button type=button aria-expanded=false aria-controls=sec2 id=h2>Heading 2</button></h3>
<div focusgroup=none role=region aria-labelledby=h2 id=sec2>
… 2nd panel …
</div>
<h3><button type=button aria-expanded=false aria-controls=sec3 id=h3>Heading 3</button></h3>
<div focusgroup=none role=region aria-labelledby=h3 id=sec3>
… 3rd panel …
</div>
</div>
What to notice:
- The accordion headers only respond to up/down arrow keys because the
focusgroup
is limited to the ‘block’ direction (assuming this content has awriting-mode
horizontal-tb
). The headers also participate in the sequential focus navigation (i.e., tab order)—focusgroup
does not change that. - Any focusable content in the accordion’s
role=region
sections is excluded from arrow key navigation via the inclusive opt-outfocusgroup=none
. - Content in the accordion’s
role=region
is still available via sequential focus navigation (e.g., tab order). The tab order is unaffected byfocusgroup=none
declarations.
Finally, the author is creating a data grid following the data grid pattern where each of the cells in the table are focusable.
<table focusgroup=grid role=grid aria-label=Transactions>
<tbody><tr>
<th>Date</th>
<th>Type</th>
<th>Description</th>
<th>Amount</th>
<th>Balance</th>
</tr>
<tr>
<td tabindex=-1>01-Jan-16</td>
<td tabindex=-1>Deposit</td>
<td><a tabindex=-1 href=#>Cash Deposit</a></td>
<td tabindex=-1>$1,000,000.00</td>
<td tabindex=-1>$1,000,000.00</td>
</tr>
<tr>
<td tabindex=-1>02-Jan-16</td>
<td tabindex=-1>Debit</td>
<td><a tabindex=-1 href=#>Down Town Grocery</a></td>
<td tabindex=-1>$250.00</td>
<td tabindex=-1>$999,750.00</td>
</tr>
</tbody></table>
What to notice:
focusgroup=grid
understands table layout and will provide logical cell navigation with arrow keys around all the focusable grid cells.<th>
header cells are not made focusable in this example, and so are not navigable by thefocusgroup
.- all focusable elements are declared with
tabindex=-1
to take them out of sequential focus navigation. Thefocusgroup
ensures that at least one of these focusable elements participates in the sequential focus navigation order regardless. Thefocusgroup
also remembers the last focused element, and returns the user to that element when they re-enter the table via sequential focus navigation.
Goal
The goal of this feature is to “pave the cow path” of an existing authoring practice (and accessibility best practice) implemented in nearly every Web UI library: the roving tabindex [react, angular, fluent, elix]]. Note however, that certain design choices have been made to generalize the behavior so that additional scenarios are possible. See comparing roving tabindex and focusgroup for further details.
To achieve this goal, we believe the solution must be available in declarative markup or CSS. If JavaScript is required, then there seems little advantage to using a built-in feature over what can be implemented completely in author code. Furthermore, a declarative solution provides the key signal that allows the platform’s accessibility infrastructure to make the focusgroup
accessible by default:
- signaling to the AT to switch to a “Focus mode” by default, e.g., that the user has entered a type of control group.
- allowing user agents to provide visual hints that the use of arrow-key navigation is possible, e.g., through a recognizable focus-ring or visual indicator.
- providing a consistent and reliable navigation usage pattern for users with no extra author code required.
Non-Goals
In some control patterns (such as radio groups or tablists) moving the focus to an element also toggles its selection state. While some use cases will require the selection state to follow the focus, in others these need to be decoupled. focusgroup
is decoupled from selection. Tracking and changing selection based on focus will require author code. Note that a related proposal for tracking selection state, CSS Toggles, is no longer being pursued.
Implementations are welcome to experiment with additional UI (e.g., a “focusgroup focus ring”) in order to help make users aware of focusgroups, however this proposal does not include any specific guidelines or recommendations.
Principles
- Intuitive use in declarative scenarios. Focusgroups
- are easy to reason about (self-documenting) in the source markup or CSS.
- provide a rational behavior when nested.
- integrate well with other related platform semantics (e.g.,
tabindex
).
- Focusgroups are easy to maintain and configure.
- Configuration is managed in one place.
- Provide easy to understand usage into HTML and CSS patterns.
- Avoid “spidery connections” e.g., using IDRefs or custom names that are hard to maintain.
- Complimentary declarative representations in HTML and CSS
- HTML attributes offers focusgroup usage directly with impacted content and provide for the most straightforward scenarios.
- CSS properties allow for responsive design patterns and can avoid redundant markup in some scenarios.
Use Cases
- (Element and subtree opt-in) A focusable element and its entire subtree can participate in a single focusgroup.
- (Cross Shadow DOM) Focusable elements contained inside a Shadow DOM are discoverable and focusable when their Shadow Host or an ancestor element declares a focusgroup.
- (Wrap) Focusgroup can be configured to have wrap-around focus semantics.
- (Limit directional arrow keys) A focusgroup can be configured to respond to either the logical inline-axis navigation keys (e.g., left and right arrow keys when the focusgroup is in a
horizontal-tb
writing mode) or block-axis navigation keys or both (to trivially reserve one axis of arrow key behavior for supplementary actions, such as opening nodes in a tree view control). See CSS Logical Properties and Values for more about logical directions. - (Focus movement arrow keys follow content direction) The user’s arrow key presses move the focus forward or backward in the DOM according to the writing mode and directionality of the content. E.g., in RTL, an Arrow-Left key moves the focus forward according to the content direction.
- (Opt-out) Individual elements can opt-out of focusgroup participation
- (Grid) Focusgroups can be used for grid-type navigation (
<table>
-structured content or other grid-like structured content).
A use case we are evaluating:
- (Grid) Focusgroups can be used on elements with
display: grid
to provide 2d grid navigation.
Focusgroup Concepts
A focusgroup is a group of related elements that can be navigated by directional arrow keys and home/end keys and for which the web platform provides the navigation behavior by default. No JavaScript event handlers needed in many cases! The arrow keys controlling the navigation are mapped to “forward” and “reverse” directions according to whether they point in the “block/inline-end” or “block/inline-start” directions, respectively (based on the element’s directionality that declares the focusgroup).
There are two kinds of focusgroups: linear focusgroups and grid focusgroups. Linear focusgroups provide arrow key navigation among a list of elements. Grid focusgroups provide arrow key navigation behavior for tabular (or 2-dimensional) data structures.
HTML | CSS | Explanation |
---|---|---|
focusgroup or focusgroup="" (no value specified) | focus-group-type: linear | Defines a linear focusgroup |
focusgroup=grid or focusgroup=manual-grid | focus-group-type: grid or focus-group-type: manual-grid | Defines an automatic grid or manual grid focusgroup. Grid focusgroup are covered in more detail below |
In the case that HTML attribute values conflict with CSS properties, the CSS values override the HTML-defined values.
Focusgroups consist of a focusgroup definition that establishes focusgroup candidates and focusgroup items. Focusgroup definitions manage the desired behavior for the associated focusgroup items. Focusgroup items are the elements that actually participate in the focusgroup (from the set of focusgroup candidates). Focusgroup candidates are all the elements under the scope of a focusgroup definition. The focusgroup scope consists of the element with the focusgroup definition and its shadow-inclusive descendants, excluding elements that have opted out.
The minimal focusgroup below demonstrates that the element declaring a focusgroup is also a focusgroup candidate and (in this case) the single focusgroup item.
<p tabindex=0 focusgroup>World's smallest, but least useful focusgroup</p>
Focusgroup candidates become focusgroup items if they are focusable, e.g., implicitly focusable elements like <button>
, or explicitly made focusable via tabindex
or some other mechanism (e.g., contenteditable
, being a scroll-container).
An element can only have one focusgroup definition added via the focusgroup
attribute or CSS focus-group-type: linear
property:
Example:
<p id="ancestor" focusgroup>
<span>
<span>
<span>
Some text and
<a id="one" href="…">a link</a>. A
<span id="two" tabindex="-1">focusable span</span> and
<a id="three" href="…">another link</a>.
</span>
</span>
</span>
</p>
The ancestor
element has the focusgroup definition. The elements with id=one
, two
, and three
(and any other shadow-inclusive descendants of ancestor
that may be added) are focusgroup candidates. Because each of these candidates are focusable, they are also focusgroup items. When one of the focusgroup items becomes focused, the user can move focus sequentially among all the focusgroup items using the arrow keys (up/right moves focus forward, down/left moves focus backwards assuming the <p>
element has writing-mode
horizontal-tb
and direction
ltr
).
Note that only elements with id=one
and three
can be focused using the Tab key. The <span>
has tabindex=-1
set, which takes it out of the tabindex sequential navigation ordering.
Last-focused memory
By default, focusgroups will remember the last-focused element, and for sequential focus navigation, will restore focus to that element when a focusgroup is re-entered. This is important for large lists or tables so that users are returned the the context they previously left without having to navigate from the start or end sequentially.
The focusgroup’s memory is initially empty. In that state, sequential focus navigation will pick the next element to focus using existing platform behavior with the exception noted below.
The focusgroup’s memory is cleared whenever there is any change to the focusability of its remembered element or the relationship that the element has with its focusgroup definition element (such as either one being removed from the tree). See additional details below.
Guaranteed tab stop
Focusgroups provide a special behavior when used in conjunction with sequential focus navigation (“tab navigation”). Focusgroups ensure that at least one focusgroup item will participate in sequential focus navigation even if all focusgroup items are declared to opt-out via tabindex=-1
. This behavior ensures that a focusgroup can always be entered via sequential focus naviation. See below for further details. Aside from this one behavior change, there is no other impact to the way tab navigation works with tabindex
nor the tab ordering behavior.
Focusgroups can therefore be used to provide a roving tabindex among a set of related focusable controls such as this menubar:
Example:
<div role="toolbar" focusgroup aria-label="Text Formatting" aria-controls="…">
<div>
<button type="button" aria-pressed="false" value="bold" tabindex="-1"><span>Bold</span></button>
<button type="button" aria-pressed="false" value="italic" tabindex="-1"><span>Italic</span></button>
<button type="button" aria-pressed="false" value="underline" tabindex="-1"><span>Underline</span></button>
</div>
</div>
When pressing tab to enter this “toolbar” focusgroup from an element before it, focus will go to the first <button>
because:
- There is no other element within the focusgroup with a
tabindex
>= 0 or that is sequentially focusable by default (these<button>
s are taken out of sequential focus navigation withtabindex=-1
). - This focusgroup has no “memory” of a last-focused element within (e.g., it has not been entered before).
- Since neither of the above cases resulted in focusing an alternate element, then the first focusgroup item in the group is focused.
At this point, the user can use the arrow keys to move from the beginning of the toolbar to the end, or press tab again to move outside of the focusgroup.
Alternatively, focusgroup can be used to supplement existing tab-stop behavior to provide arrow key navigational support in addition to tab navigation. No problem: just ensure the deisred elements are sequentially focusable via tabindex=0
(or stop excluding them via tabindex=-1
):
Example:
<div role="toolbar" focusgroup aria-label="Text Formatting" aria-controls="…">
<div>
<button type="button" aria-pressed="false" value="bold"><span>Bold</span></button>
<button type="button" aria-pressed="false" value="italic"><span>Italic</span></button>
<button type="button" aria-pressed="false" value="underline"><span>Underline</span></button>
</div>
</div>
Shadow DOM boundaries
Focusgroup definitions apply across Shadow DOM boundaries in order to make it easy for component developers to support focusgroup behavior across component boundaries. (Component authors that want to opt-out of this behavior can do so.)
Example:
<list-component focusgroup role="listbox" aria-label="Cute dogs">
<template shadowrootmode="open">
<my-listitem role="option" tabindex="0" aria-selected="true">Terrier</my-listitem>
<my-listitem role="option" tabindex="-1" aria-selected="false">Dalmation</my-listitem>
<my-listitem role="option" tabindex="-1" aria-selected="false">Saint Bernard</my-listitem>
</template>
</list-component>
Key conflicts
The focusgroup is a default handler for certain keystrokes (keydown events for arrow keys, home/end, etc.) that will cause focus to move among focusgroup items. This default keyboard handling could interfere with other actions the application would like to take. A common pattern is to limit focusgroup directionality so that certain cross-axis keystrokes won’t trigger focusgroup behavior. However, if these doesn’t address the use case, then authors may cancel the focusgroup’s default behavior at any time by canceling (preventDefault()
) the specific keydown event. Keydown events are dispatched by the currently focused element, and bubble through the focusgroup ancestor element in most cases.
Interactive content inside focusgroups
Some built-in controls like <input type=text>
provide keyboard behaviors that “trap” nearly all keys that would be handled by the focusgroup. Others such as <input type=number>
trap only certain keys like the arrow keys that are also used for focusgroup navigation. This proposal does not provide a built-in workaround to prevent this from happening. Instead, authors are advised to be sure users can “escape” these elements. Built-in elements provide this via the tab key. Other strategies might include requiring an “activation” step before putting focus into the interactive control (and an Esc key exit to leave).
The focusgroup’s memory may also cause unexpected user interactions if authors are not careful. For example, without any author mitigations, an interactive control inside a focusgroup may inadvertently prevent the user from accessing other focusgroup items:
<div role=toolbar focusgroup aria-label="Font Adjustment" aria-controls="…">
<label for=font-input>Font</label>
<div>
<div>
<input type=text id=font-input role=combobox aria-autocomplete=both aria-expanded=false aria-controls=font-listbox aria-activedescendant="">
<button type=button aria-label="Font List" aria-expanded=false aria-controls=font-listbox tabindex=-1>🔽</button>
</div>
<ul id=font-listbox role=listbox aria-label="Font List">
<li role=option>Ariel</li>
<li role=option>Monospace</li>
<li role=option>Verdana</li>
</ul>
</div>
<button type=button value="bigger" tabindex=-1><span>Increase Font</span></button>
<button type=button value="smaller" tabindex=-1><span>Decrease Font</span></button>
</div>
When the combobox
input element is focused, it is remembered by the focusgroup’s memory. The <input>
element traps nearly all keystrokes by default, including the arrow keys that might have been used to reach the “Increase/Decrease Font” buttons. When the user presses tab, focus exits the focusgroup. Later, when focus re-enters, the focusgroup will put focus back on the <input>
element (because of the memory), and the cycle continues with no way to get to the two following buttons via keyboard interaction alone.
Fortunately, there are several solutions to this problem:
- Remove
tabindex=-1
from the “Increase/Decrease Font” buttons. - Move the “Increase/Decrease Font” buttons before the
combobox
. (Refer to “Avoid including controls whose operation requires the pair of arrow keys used for toolbar navigation” in the Toolbar control pattern.) Additionally, opt-out the<input>
control from focusgroup participation so that arrow keys skip it. Alternatively, turn off the focusgroup’s memory so that focus isn’t automatically returned to thecombobox
. - Use script to intercept focusgroup-related keydown events on the
<input>
and move focus manually. Also consider limiting the focusgroup to one axis and reserving the other axis for operating the<input>
.
Restricted elements
Because focusgroup definitions are intended for grouping related controls, it does not make sense to provide focusgroup functionality on all elements. While the focusgroup attribute may de defined as a global attribute, it’s applicability is limited to a subset of elements.
At the time of writing, we are evaluating which elements will honor a focusgroup attribute definition. We welcome comments on this section. To submit your feedback, please see the issue Restrict usage of focusgroup on certain elements.
The current proposal is to limit focusgroup to only the elements whose name match the DOM’s valid shadow host names (which are the elements allowed to call attachShadow()
). However, <table>
and some table parts will need to be an exception in order to properly support grid focusgroups.
Feature detection
To enable feature detection, the DOM will include a focusgroup
property, whose existence on elements is useful for feature detection.
partial interface HTMLElement {
[CEReactions] attribute DOMString focusgroup;
};
Additional features
Focusgroups have the following additional features:
- Wrap-around semantics - what to do when attempting to move past the end of a focusgroup. The default/initial value is
no-wrap
, which means that focus is not moved past the ends of a focusgroup with the arrow keys.wrap
and other tabular wrapping behaviors are available forgrid
focusgroups. - Directional axis limits - applies to linear focusgroups only; respond to arrow keys in one axis only (either up/down or left/right when the arrow key pressed matches the corresponding flow of the content). By default, linear focusgroups respond to all four arrow keys.
- Focusgroup candidacy opt-out - prevent an element and its shadow-inclusive descendants from participating in an anscester’s focusgroup.
- Memory opt-out - prevent the focusgroup from remembering what the last focused element was when focus leaves a focusgroup. By default focusgroups remember that element and will restore the focus to that element when the focusgroup is re-entered via sequential focus navigation.
In HTML these feature options are applied as space-separated token values to the focusgroup
attribute. In CSS, these definitions are specified as properties (including a focus-group
shorthand property for convenience).
Enabling wrapping behaviors
By default, focusgroup traversal with arrow keys ends at boundaries of the focusgroup (the start and end of a linear focusgroup, and the start and end of both rows and columns in a grid focusgroup). The following focusgroup definition values change this behavior:
HTML | CSS | Explanation |
---|---|---|
(default) focusgroup="" (unspecified) | focus-group-wrap: none | Disables any kind of wrapping. |
focusgroup=wrap | focus-group-wrap: wrap | Causes movement beyond the ends of the focusgroup to wrap around to the other side. |
Additional values are available for grid focusgroups. Note, row and column-specific wrapping and flowing can be specified together: focus-group-wrap
supports two-token specifiers for this purpose.
HTML | CSS | Explanation |
---|---|---|
(default) focusgroup="grid" | focus-group-wrap: none or focus-group-wrap: row-none col-none | Rows and columns do not wrap or flow (default). |
focusgroup="grid wrap" | focus-group-wrap: wrap or focus-group-wrap: row-wrap col-wrap | Movement at the ends of the rows/columns wrap around to the opposite side of the same rows/columns. |
focusgroup="grid row-wrap" | focus-group-wrap: row-wrap or focus-group-wrap: row-wrap col-none | Rows wrap around, columns do not wrap. |
focusgroup="grid col-wrap" | focus-group-wrap: col-wrap or focus-group-wrap: col-wrap row-none | Columns wrap around, rows do not wrap. |
focusgroup="grid flow" | focus-group-wrap: flow or focus-group-wrap: row-flow col-flow | Movement past the end of a row wraps the focus to the beginning of the next row. Movement past the beginning of a row wraps focus back to the end of the prior row. Same for columns. The last row/column wraps to the first row/column and vice versa. |
focusgroup="grid row-flow" | focus-group-wrap: row-flow or focus-group-wrap: row-flow col-none | Rows “flow” from row ends to the next/prior row as described above, columns don’t wrap or flow. |
focusgroup="grid col-flow" | focus-group-wrap: col-flow or focus-group-wrap: col-flow row-none | Columns “flow” from column ends to the next/prior column as described above, rows don’t wrap or flow. |
focusgroup="grid row-wrap col-flow" | focus-group-wrap: row-wrap col-flow | Rows wrap around, and columns flow (as described above). |
focusgroup="grid row-flow col-wrap" | focus-group-wrap: row-flow col-wrap | Rows flow (as described below) and columns wrap around. |
Specifying both row-wrap
and row-flow
in one HTML focusgroup definition is an author error. Only one declaration for row behavior is allowed. Similarily for col-wrap
and col-flow
.
Limiting linear focusgroup directionality
In many cases, having multi-axis directional movement (e.g., both right arrow and down arrow linked to the forward direction) is not desirable, such as when implementing a tablist control pattern, in which case it may not make sense for the up and down arrows to also move the focus left and right. Likewise, when moving up and down in a vertical menu, the author might wish to use JavaScript to provide other behavior for the left and right arrow keys such as opening or closing sub-menus. In these situations, authors can limit the linear focusgroup to one-axis traversal.
Note that the following only apply to linear focusgroup definitions (they have no effect on grid focusgroups).
HTML | CSS | Explanation |
---|---|---|
(default) focusgroup="" (unspecified) | focus-group-direction: both | The focusgroup items will respond to forward and backward movement with both directions (horizontal and vertical). The default/initial value. |
focusgroup=inline | focus-group-direction: inline | The focusgroup items will respond to forward and backward movement only with arrow keys that are parallel to this element’s “inline” axis (e.g., left and right arrow keys for horizontal-tb writing mode). |
focusgroup=block | focus-group-direction: block | The focusgroup items will respond to forward and backward movement only with arrow keys that are parallel to this element’s “block” axis (e.g., up and down arrow keys for vertical-* writing modes). |
Example:
<tab-group role=tablist focusgroup="inline wrap">
<a-tab role=tab tabindex=0 aria-selected=true aria-controls="…">…</a-tab>
<a-tab role=tab tabindex=-1 aria-selected=false aria-controls="…">…</a-tab>
<a-tab role=tab tabindex=-1 aria-selected=false aria-controls="…">…</a-tab>
</tab-group>
In the above example, when the focus is on the first <a-tab>
element, pressing either the up or down arrow key does nothing because the focusgroup is configured to only respond to the inline (left/right in this case) arrow keys.
Because 2-axis directionality is the default, specifying both inline
and block
at the same time on one focusgroup is not allowed:
Example:
<!-- This is an example of what NOT TO DO -->
<radiobutton-group focusgroup="inline block wrap" role=radiogroup>
⚠️This focusgroup configuration is an error--neither constraint will be applied (which is actually
what the author intended).
</radiobutton-group>
Opting-out
Focusgroup definitions assigned to an element create focusgroup candidates that include the element itself and all its shadow-inclusive descendant elements. Any element within that focusgroup scope that is (or becomes) focusable will automatically become a focusgroup item belonging to its ancestor’s focusgroup.
With such an expansive opt-in behavior, it is important to provide an opt-out for elements or element subtrees. For example: focusable elements that wish to remain in sequential focus navigation and have arrow key navigation pass them over; or, components nested across a Shadow DOM boundary that wish to be excluded from focusgroup participation.
Opting-out applies to the element making the declaration as well as its shadow-inclusive descendants.
To opt-out:
HTML | CSS | Explanation |
---|---|---|
focusgroup=none | focus-group-type: none | Opt-out of a focusgroup: this element and its shadow-inclusive descendants will not be considered focusgroup candidates. |
In the following example of an accordion pattern, the accordion’s panels opt-out of focusgroup
behavior so that any interactive content in a panel is bypassed when navigating among the accordion headers.
Example:
<div focusgroup=block>
<h3>
<button type=button aria-expanded=true aria-controls=sec1 id=h1>Heading 1</button>
</h3>
<div focusgroup=none role=region aria-labelledby=h1 id=sec1> … accordion panel w/form controls that are Tab focusable … </div>
<h3>
<button type=button aria-expanded=false aria-controls=sec2 id=h2>Heading 2</button>
</h3>
<div focusgroup=none role=region aria-labelledby=h2 id=sec2> .. 2nd panel … </div>
<h3>
<button type=button aria-expanded=false aria-controls=sec3 id=h3>Heading 3</button>
</h3>
<div focusgroup=none role=region aria-labelledby=h3 id=sec3> .. 3rd panel … </div>
</div>
When a focusgroup definition is appled to an element, it implicicly opts out of any ancestor’s focusgroups. This ensures that every element can only belong in one focusgroup at a time.
Example:
<div focusgroup> <!-- Parent -->
<div focusgroup> <!-- implicit focusgroup=none -->
<button>Am I a candidate, if yes, for which group?</button>
<div focusgroup></div> <!-- what happens here? -->
</div>
</div>
<-- Parent -->
is afocusgroup
that currently has no focusable candidates in it. If other children are added to it, then they and their descendants would be candidates for inclusion in that Parent focusgroup.<button>
is in the focusgroup declared by its parent.<-- what happens here? -->
declares another focusgroup with no focusable candidates. At the same time it is also opting itself out of inclusion in the focusgroup that includes the<button>
.
Disabling focusgroup memory
By default, when a linear or grid focusgroup is defined it includes a “memory” of the last-focused element within its scope, initially empty. Each time the focus is changed within a focusgroup, the “memory” is updated to refer to that element. This behavior is akin to the roving tabindex in which the “memory” is the statefull tabindex=0
value assigned to the currently focused element.
In some scenarios it is not desireable to have a focusgroup maintain a memory. Usually this is because there is a more relevant element which should take focus when entering the focusgroup instead of the most-recently-focused element. For example, an active (selected) tab in a role=tablist
container, rather than the last-focused tab (when selection does not follow focus).
To disable the focusgroup’s default memory, use the value no-memory
:
HTML | CSS | Explanation |
---|---|---|
(default) focusgroup="" (unspecified) | focus-group-memory: auto | The focusgroup will remember the last-focused element and redirect focus to it when entered via sequential focus navigation. |
focusgroup=no-memory | focus-group-memory: none | The focusgroup will not remember the last-focused element. |
After the focusgroup’s memory has been set, it must be cleared whenever any one of the following change:
- The element with the focusgroup definition is
hidden
or un-hidden; or if the currently-remembered element ishidden
or un-hidden. - The element with the focusgroup definition has its
disabled
orinert
status changed; or if the currently-remembered element has itsdisabled
orinert
status changed. - The element with the focusgroup definition is removed from the shadow-inclusive tree; or if the currently-remembered element is removed from the shadow-inclusive tree.
- The currently-remembered element stops being focusable (e.g., a
<div>
with atabindex
has itstabindex
attribute removed). - The currently-remembered element is changed to become excluded from the focusgroup (through
focusgroup=none
on itself or a shadow-inclusive ancester, or by changing focusgroups: if a new focusgroup definition appears on itself or one of its shadow-inclusive ancestor elements).
Adjustments to sequential focus navigation
To ensure that a focusgroup always has at least one tab stop in the sequential focus navigation order, and to provide the appropriate “hook” for a focusgroup’s “memory” to redirect focus to the last-focused element in a focusgroup, a change to sequential focus navigation is needed.
This change is intended to ensure that focus is directed to one of the following focusgroup candidates whenever focus enters a focusgroup. The first matching condition is always taken:
- If there is an element in the focusgroup memory, focus is set on that element.
- If there is an element with
tabindex=0
(or other built-in element with UA-defined keyboard focusability like<input>
,<button>
, etc.) that is also a focusgroup candidate for the current focusgroup, focus will be set on the first such element in DOM order (regardless of the direction of traversal, i.e., viatab
orShift+tab
). Note: this provides authors with a predictable “entry” of their choosing within a focusgroup. - The first focusable element (even if not intended for sequential focus navigation) if sequential focus navigation is moving “forward”, or the last focusable element if sequential focus navigation is traversing “backward”.
Specifically, each focusgroup definition must maintain a:
- first focusable focusgroup candidate - in DOM order, the first focusable element that is also a focusgroup candidate (e.g., not excluded from focusgroup participation or in another focusgroup).
- last focusable focusgroup candidate - see previous, but the last focusable in DOM order.
- first sequentially-focusable focusgroup candidate - in DOM order, the first focusable element that would participate in sequential focus navigation order (e.g., the element with the lowest positive value of
tabindex
, or withtabindex=0
, or that is a built-in sequentially focusable element that has not opted-out viatabindex=-1
).
Algorithmically, during “forward” sequential focus navigation, if the element being considered is:
- a member of a focusgroup AND is the first focusable focusgroup candidate of that focusgroup THEN:
- if the focusgroup’s memory is available and not empty, then move focus to the element referred to by the focusgroup’s memory.
- otherwise, if there is NO first sequentially-focusable focusgroup candidate in that focusgroup, then move focus to this element.
- otherwise, continue with the sequential focus navigation algorithm as normal.
For “reverse” sequential focus navigation, the algorithm is similar, but swap occurances of the “first focusable focusgroup candidate” for “last focusable focusgroup candidate”.
Because this algorithm applies only when interrogating the first (or last) focusable focusgroup candidate, then any descendants that preceed (or follow) the first (or last) focusable focusgroup candidate that themselves define a focusgroup are considered first. In other words, broad-reaching ancestral focusgroups won’t necessarily “steal” focus from descendant focusgroups during sequential focus navigation.
Grid focusgroups
Some focusable data is structured not as a series of nested linear groups, but as a 2-dimensional grid such as in a spreadsheet app, where focus can move logically from cell-to-cell either horizontally or vertically. In these data structures, it makes sense to support the user’s logical usage of the arrow keys to move around the data.
Grid navigation is expected to happen within well-structured content with consistent rows and columns where DOM structure reflects this organization. In focusgroup grid navigation, only the cells in the grid are focusgroup candidates and only the focusable cells become focusgroup items. It is not currently possible to use grid focusgroups to support other focusable tabular parts such as focusable row elements (see comment in issue 1018 for a possible future addition for this use case).
Applicability to tabular data
The arrow navigation in the grid (and in the previous non-grid scenarios) should reflect the accessible structure of the document, not the presentation view made possible with CSS. For example, it is easy to create views that visually appear grid-like, but do not make sense to navigate like a grid if considering that the data model is fundamentally a list, which is how users of accessibility technology would perceive it. Wrapping a list of contact cards on screen in a grid-like presentation allows for more content density on screen for sighted users. In that scenario, arrow key navigation to move linearly (left-to-right following the line-breaking across each line) through the contents makes sense (especially if these are alphabetized), but orthogonal movement through the “grid” (up/down when cards are aligned or in a masonry layout) jumps the focus to seemingly arbitrary locations. Multi-directional arrow key navigation may seem appropriate for sighted users that have the visual context only, but are not appropriate for assistive technology. In the case of the list-presented-as-a-grid scenario, a linear focusgroup will make the most sense for sighted as well as users with accessibility needs.
When considering using a grid focusgroup, be sure that the data is structured like a grid and that the structure makes semantic sense in two dimensions (and not just for a particular layout or presentation).
Tabular data can be structured using column-major or row-major organization. Given that HTML tables and ARIA attributes for grids (role=grid
, role=row
, role=gridcell
) only exist for row-major grid types, this proposal does not define grid focusgroup organization for column-major data structures (and assumes row-major data structures throughout).
Setting up a grid focusgroup
Grid focusgroups can be created “automatically” or manually. Automatic grids use the context of existing HTML semantics for tables as the structural components necessary to provide grid-based navigation. Any elements with computed table layout are suitable for an automatic grid (e.g., display: table-row
in place of using a <tr>
elements). Manual grid creation requires annotating specific elements with their focusgroup grid component names.
Note: We are evaluating the suitability for CSS display: grid
to create automatic grids.
The automatic grid approach will be preferable when the grid contents are uniform and consistent and when re-using semantic elements for grids (typical). The manual approach may be necessary when the grid structure is not uniform or structurally inconsistent (atypical), and involves identifying the parts of the grid on specific focusgroup candidates using CSS.
Elements with the grid
focusgroup definition on the root element of the structural grid become automatic grid focusgroups. The implementation must attempt to validate the structure of the grid to ensure it has appropriate row and cell structures. In the event that the implementation cannot automatically determine the grid structure, then the definition is ignored (i.e., there is no fallback to a linear grid).
HTML | CSS | Explanation |
---|---|---|
focusgroup=grid | focus-group-type: grid | Establishes the root of an automatic grid focusgroup. Shadow-inclusive descendants of the automatic grid are identified and assigned focus-group-type: grid-row and focus-group-type: grid-cell focusgroup candidate status automatically. |
Example:
<table aria-label="Tic tac toe grid" role=grid focusgroup=grid>
<tr>
<td tabindex=-1></td>
<td tabindex=-1></td>
<td tabindex=-1></td>
</tr>
<tr>
<td tabindex=-1></td>
<td tabindex=-1></td>
<td tabindex=-1></td>
</tr>
<tr>
<td tabindex=-1></td>
<td tabindex=-1></td>
<td tabindex=-1></td>
</tr>
</table>
The <table>
’s grid focusgroup definition automatically establishes each of its descendant <tr>
s as focusgroup rows (the parser-generated <tbody>
is accounted for) and <td>
s as focusgroup cells. Each focusgroup cell is a scope root for one focusgroup item per cell, and the cell and its shadow-inclusive decendants are all focusgroup candidates. Among all focusgroup cells, the left/right arrow keys navigate between cells in the table, and up/down arrow keys will compute the new target based on the DOM position of the current focusgroup candidate cell in relation to the focusgroup candidate row.
Manual grids: row and cell connections
A manual grid is declared in a focusgroup definition with the name manual-grid
. With a manual grid, the rows and cells must be explicitly indicated using grid-row
and grid-cell
:
HTML | CSS | Explanation |
---|---|---|
focusgroup=manual-grid | focus-group-type: manual-grid | Establishes the root of a manual grid focusgroup. Shadow-inclusive descendants of the manual grid must be identified with focus-group-type: grid-row and focus-group-type: grid-cell explicitly. |
focusgruop=grid-row | focus-group-type: grid-row | Must be a shadow-inclusive descendant of a grid focusgroup root (i.e., the manual-grid -named focusgroup element). |
focusgroup=grid-cell | focus-group-type: grid-cell | Must be a shadow-inclusive descendant of a grid focusgroup root (i.e., the manual-grid -named focusgroup element). Must also be a shadow-inclusive descendant of a focus-group-type: grid-row focusgroup candidate. |
Cells cannot be descendants of other cells, and rows cannot be descendants of other rows.
Each focusgroup candidate will perform an ancestor search to locate its nearest grid structural component: cells will look for their nearest row, and rows will look for their nearest grid root.
In the following example, the <my-cell>
s are all meant to be on the same row of the grid, and the rows are designated by <my-row>
elements:
Example:
<my-root role=grid focusgroup=manual-grid>
<div role=none class=presentational_wrapper>…</div>
<my-row role=row focusgroup=grid-row>
<first-thing role=gridcell focusgroup=grid-cell>…</first-thing>
<cell-container role=none>
<my-cell role=gridcell focusgroup=grid-cell>…</my-cell>
<my-cell role=gridcell focusgroup=grid-cell>…</my-cell>
</cell-container>
<cell-container role=none>
<my-cell role=gridcell focusgroup=grid-cell>…</my-cell>
<my-cell role=gridcell focusgroup=grid-cell>…</my-cell>
</cell-container>
</my-row focusgroup=grid-row>
<!-- repeat pattern of div/my-row pairs... -->
</my-root>
The following non-uniform structure can still have grid semantics added via manual-grid
:
Example:
<div role=grid focusgroup="manual-grid flow">
<div role=row focusgroup=grid-row>
<div>
<div role=gridcell focusgroup=grid-cell></div>
<div role=gridcell focusgroup=grid-cell></div>
</div>
</div>
<div>
<div role="row" focusgroup=grid-row>
<div role=gridcell focusgroup=grid-cell></div>
<div role=gridcell focusgroup=grid-cell></div>
</div>
</div>
<div>
<div>
<div role="row" focusgroup=grid-row>
<div>
<div role=gridcell focusgroup=grid-cell></div>
<div role=gridcell focusgroup=grid-cell></div>
</div>
</div>
</div>
</div>
</div>
Grid focusgroup nesting
Unlike linear focusgroups, an automatic or manual grid focusgroup requires a small degree of DOM structure to work correctly. Unless the proper structure exists, the grid focusgroup won’t work.
Attempts to define new grid or linear focusgroups among the DOM elements that makeup the structure of a grid focusgroup (such as on or between elements defining the root grid container, the grid-rows and the grid-cells) will be ignored. However new grid or linear focusgroups can be defined on elements that are shadow-inclusive descendants of grid-cell elements (e.g., that are outside the set of elements making up the grid’s DOM structure).
Empty Cell data
Like linear focusgroups, focus is only set on elements that are focusable. The arrow key navigation algorithms look for the first focusgroup item (in DOM order) of a grid focusgroup cell in the direction the arrow was pressed. Non-focusable grid focusgroup candidates of a focusgroup cell are passed over in the search.
Non-uniform cells
It is entirely possible to have rows with non-uniform numbers of cells. In these cases, focusgroup navigation behaviors may not work as visibly desired. Algorithms for navigating grid focusgroups will work based on content the grid content structure as specified. If the algorithms conclude that there is no “next candidate cell” to move to (e.g., in a grid with two rows, and the bottom row has three cells, and the top row only two, if the focus is on the 3rd cell, a request to move “up” to the prior row cannot be honored because there is no “3rd cell” in that row.
Privacy and Security Considerations
Privacy
No considerable privacy concerns are expected, but we welcome community feedback.
Security
No significant security concerns are expected.
Design decisions
Here is a short list to issue discussions that led to the current design of focusgroup.
focusgroup
works across Shadow DOM boundaries by default- arrow key movement and directionality contraints should be aligned with content direction (add inline/block)
- focusgroup definitions should not be limited to direct-children
- focusgroup should include a memory
Alternative Solutions
We considered various alternative solutions before arriving at the current proposal:
- We considered a design more closely aligned with HTML’s radio button groups, wherein to form a ‘focusgroup’ each participating element used a similarly-named moniker. The platform was expected to automatically link same-named groups together. This enabled groups to be widely dispersed across the DOM. In practice few scenarios require that degree of flexibility and data locality helps ensure that lists of items stay relatively adjacent to each other structurally. Furthermore, this approach provided no common “controller” element, and additional attributes were proposed to potentially link these groups together. Finally, this proposal suggested that the attribute also enable the element to get keyboard focus if it didn’t already have it—another new way of making elements focusable wasn’t deemed a great idea.
- In another iteration, we considered codifying the ARIA authoring practices applying platform behavior automatically based on the presence of certain ARIA attributes (such as role=“tab”). This idea didn’t get too far, as we ultimately felt that aria attributes really shouldn’t have native behavior locked into them as that sets a bad precedent. It was also considered that aria in some fundamental ways is a patch on top of the native HTML engine for support that should eventually become a native part of the platform. That was the insight that took us in the direction of building a native HTML feature to expose the built-in platform arrow-key navigation of certain controls.
Related Work
CSS Basic UI 4 Keyboard Navigation properties
Since at least 2002 the CSS WG has defined related CSS properties nav-up
, nav-right
, nav-down
, nav-left
in CSS Basic UI 4’s Keyboard Control. These properties are expected to provide focus navigation control similar to focusgroup
. They differ in some significant ways:
nav-*
are defined using CSS (vs HTML). Not that HTML-without-CSS is really a modern concern, but proposing focusgroup in HTML enables the user agent to setup focusgroups without the CSS dependency (or behavior change should CSS application be delayed due to network conditions).- Each of
nav-up
, etc., require an explicit content selector (id) for ordering, which makes them relatively brittle. This design presents at least three challenges:- The possibility of mis-alignment to content. If stylesheet
nav-*
selectors are not updated in terms of changes to document content (at authoring time) the navigation expectations can get out of sync. - Unexpected side-effects with responsive design. If the wrapping and reflow of presentational content changes depending on device characteristics (such as orientation, zoom level), authors must likely provide dynamic run-time updates to
nav-*
properties in order to keep top/left/bottom/right navigation logical per the current layout. - Related to the previous point, it can be easy to make logical errors in navigation sequences when targeting specific directions. This can lead to directions that don’t match the property names (e.g.,
nav-left
actually navigates up or right) while also opening up the possibility of unidirectional navigation (e.g., after navigating right, the user can’t go “back” to the left due to missing or erroneous selectors).
- The possibility of mis-alignment to content. If stylesheet
- Ordering is not based on content. These properties serve visual presentations, but possibly make focus navigation illogical for accessibility users (especially when any element can be targeted by selector).
- Verbose descriptors. To get four-direction navigation on one element requires specifying four unique CSS properties.
- Special focus handling. The use of a selector on one of the
nav-*
properties has the unique property of making that element focusable upon keyboard navigation to it. Additional clarity on how this special semantic applies would be needed for implementation. It is unclear (especially given the related Note in the spec) if this is a desirable behavior.
In nearly 20 years, user agents have not implemented these properties. It is likely that focusgroup
will create an incongruency in the platform with these nav-*
CSS properties. Should the two exist simultaneously, the nav-*
properties might provide override semantics for directional movement, and take precedence over the focusgroup
attribute. However, we hope that such a conflict will not occur.
Spatial Navigation
Another approach to focusable navigation has been defined in CSS Spatial Navigation. This specification enables user agents to deterministically navigate focusable elements in logical directions (top, left, bottom, and right) according to those element’s visual presentation. It assumes that a user agent will use some undefined trigger to enter a “spatial navigation mode” in which the specification-defined behavior will apply. This mechanism of navigating content can be an alternative (or addition to) traditional TAB key navigation (or equivalent) for a variety of devices (like TVs).
Spatial navigation occurs in the context of a “spatial navigation container” which defaults to any scroll container (e.g., the viewport). The specification provides an API to enable programmatic navigation, and offers several CSS properties: to enable additional spatial navigation containers, to control the behavior of focus and scroll actions by navigation keys, and to customize the algorithm used for finding the next focusable element in a given direction.
The specification does not presume to use spatial navigation in a limited (e.g., scoped and grouped) way as focusgroup
implies. It appears to assume that arrow key direction navigation will be a primary navigation technique, thus any and all focusable content should participate.
It may be possible for Spatial Navigation and focusgroup
to coexist simultaneously in the same content. In the case that spatial navigation is only enabled via a special mode, it would be likely that its navigation model would take precedence over focusgroup
, as it is meant to make all visual, focusable content navigable, and would provide a super-set of navigational movement for visual content. It is worth noting that accessibility tools would not likely enable spatial navigation mode.
It seems desirable to have a feature to easily enable spatial-navigation-like behavior for a subset of elements in a presentation. While this would not be ideal for an accessibility-view of the content, it is possible that particular spatial navigation metaphors could be omitted by accessibility tools when performing navigation. For example, when an AT interfaces with the user agent, the AT might limit navigation to “forward/backward” content navigation modes, while a user not working with an AT (or ATs designed for visual navigation) would enable the full spatial navigation model. An opt-in for spatial navigation would certainly be a requirement and could be an extension to focusgroup
(e.g., focusgroup=spatial
).
Open Questions
- Issue 995 - It may not make sense to support focusgroup on every HTML element, especially those that already have platform-provided focusgroup-like internal behavior (e.g.,
<select>
). Then again, if the key navigation behavior is explained by the presence of an external attribute on these elements, perhaps the internal behavior should defer to the external specified behavior (usage of the attribute would cancel the element’s preexisting built-in behavior in favor of the new generic behavior). Implementation experience and additional community feedback will be necessary to land a reasonable plan for this case.
See other open focusgroup issues on Github.
Additional Keyboard support
In addition to arrow keys, the focusgroup should also enable other navigation keys such as pageup/down for paginated movement (TBD on how this could be calculated and in what increments), as well as the home/end keys to jump to the beginning and end of groups.
It might also be interesting to add support for typeahead scenarios (though what values to look for when building an index would need to be worked out, and may ultimately prove to be too complicated).
Index of focusgroup values
Focusgroup types:
Description | HTML syntax | CSS syntax |
---|---|---|
no focusgroup | (missing/invalid attribute) | focus-group-type: none |
linear focusgroup | (unspecified value; default value) | focus-group-type: linear |
automatic grid focusgroup | grid | focus-group-type: grid |
manual grid focusgroup root | manual-grid | focus-group-type: manual-grid |
manual grid focusgroup row | grid-row | focus-group-type: grid-row |
manual grid focusgroup cell | grid-cell | focus-group-type: grid-cell |
Focusgroup directions:
Description | HTML syntax | CSS syntax |
---|---|---|
both directions for a linear focusgroup | (unspecified value; default value) | focus-group-direction: both |
inline direction for a linear focusgroup | inline | focus-group-direction: inline |
block direction for a linear focusgroup | block | focus-group-direction: block |
Focusgroup wrapping:
Description | HTML syntax | CSS syntax |
---|---|---|
no wrap | (unspecified value; default value) | focus-group-wrap: none |
wrap | wrap | focus-group-wrap: wrap |
flow (grid focusgroups only) | flow | focus-group-wrap: flow |
column specific | col-wrap , col-flow , col-none | focus-group-wrap: col-wrap or col-flow or col-none |
row specific | row-wrap , row-flow , row-none | focus-group-wrap: row-wrap or row-flow or row-none |
Focusgroup memory:
Description | HTML syntax | CSS syntax |
---|---|---|
enable memory | (unspecified value; default value) | focus-group-memory: auto |
disable memory | no-memory | focus-group-memory: none |
Acknowledgments
Thanks to the members of the OpenUI Community Group for their many contributions. Special thanks to those who have reviewed, commented on, filed issues, and talked with us offline about focusgroup. Your insight and ideas and contributions have been indispensible.