diff --git a/site/src/pages/components/focusgroup.explainer.mdx b/site/src/pages/components/focusgroup.explainer.mdx index 66c3b40d..fed61a22 100644 --- a/site/src/pages/components/focusgroup.explainer.mdx +++ b/site/src/pages/components/focusgroup.explainer.mdx @@ -1,5 +1,5 @@ --- -menu: Non-active Proposals +menu: Active Proposals name: focusgroup (Explainer) layout: ../../layouts/ComponentLayout.astro --- @@ -12,103 +12,309 @@ Authors: [Travis Leithead](https://github.com/travisleithead), {/* DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE */} {/* END doctoc generated TOC please keep comment here to allow auto update */} -## 1. Introduction +## Introduction -When writing custom controls, authors need to implement the semantics of various known -controls (see [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)) to -enable proper (and expected) keyboard support. Control examples include tab widgets, -comboboxes, accordion panels, carousels, focusable grid tables, etc. Many of these -patterns expect or can benefit from arrow-key navigation, as well as support for -page down/up, home/end, even "type ahead" behavior. +Authors create many interactive control [patterns](https://www.w3.org/WAI/ARIA/apg/) that expect +or can benefit from keyboard-accessible behavior such as arrow-key navigation. -The native web platform supports _some_ of these linear navigation behaviors in native -controls like radio button groups (wrap-around arrow key movement that changes both focus -and linked selection), ` + + + +``` + +A custom radio group using `focusgroup`: + +```html +

Choose your pet:

+
+ Dog + Cat + Turtle +
+``` + +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 `` elements focusable (the `focusgroup` attribute + doesn't make any elements focusable by default, including the `
` 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 via `tabindex="-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 setting `tabindex=-1`). +* The `onfocusin` event handler is present because `focusgroup` **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](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) where the tab activation +behavior is decoupled from selection ("manual tab activation"): + +```html +
+ + + +
+ +
+ +``` + +What to notice: +* Sequential focus navigation within a `focusgroup` is respected. When entering the `focusgroup`, + focus will always go to the first selected tab (with `tabindex=0`). The `no-memory` value + [prevents the focusgroup from remembering the last focused tab](#disabling-focusgroup-memory) 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 to + `tabpanel-2` which is next in sequential focus navigation order (because the other + `role=tabpanel`s are `hidden`). +* focus will `wrap` from one end of the tablist to the other because of `focusgroup=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 the `role=tablist`'s + **inline** direction (assuming the `
`'s `writing-mode` is `horizontal-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, the `hidden` state of the controlled `role=tabpanel` + and the `tabindex` values of tabs such that the newly selected `role=tab` element is `tabindex=0` + while all others are `tabindex=-1`. + +In a third example, the author is creating a +[navigation menubar](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/). +Both `menuitem`s in the `menubar` ("About" and "Admissions") have popover `menu`s. The "Admissions" +menu has an additional submenu under "Tuition". + +```html + +``` + +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](#opting-out) of its + ancestor focusgroup. +- menuitems in `role=menubar` are limited to inline-direction arrow keys (e.g., left and right), + while menuitems in `role=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-in `popover` and `autofocus` attribute behaviors. +- the "memory" of the nested `focusgroup`s is reset when the content is hidden/shown--this allows + the `autofocus` 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](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/). The arrow keys move the +focus between the headings, but skip anything in the accordion bodies. + +```html +
+

+
+ … accordion panel w/form controls that are Tab focusable … +
+

+
+ … 2nd panel … +
+

+
+ … 3rd panel … +
+
+``` + +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 a `writing-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-out `focusgroup=none`. +* Content in the accordion's `role=region` is still available via sequential focus navigation + (e.g., tab order). The tab order is unaffected by `focusgroup=none` declarations. + +Finally, the author is creating a data grid following the +[data grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/examples/data-grids/) where each +of the cells in the table are focusable. + +```html + + + + + + + + + + + + + + + + + + + + + + +
DateTypeDescriptionAmountBalance
01-Jan-16DepositCash Deposit$1,000,000.00$1,000,000.00
02-Jan-16DebitDown Town Grocery$250.00$999,750.00
+``` + +What to notice: +- `focusgroup=grid` understands table layout and will provide logical cell navigation with arrow + keys around all the focusable grid cells. +- `` header cells are not made focusable in this example, and so are not navigable by the + `focusgroup`. +- all focusable elements are declared with `tabindex=-1` to take them out of sequential focus + navigation. The `focusgroup` ensures that at least one of these focusable elements participates + in the sequential focus navigation order regardless. The `focusgroup` 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". Here's a few sources that explain roving tabindex in more detail -as well as a selection of common libraries implementing the pattern: - -- [What's 'roving tabindex'?](https://www.stefanjudis.com/today-i-learned/roving-tabindex/) -- [Rob Dodson explains roving tabindex (YouTube)](https://www.youtube.com/watch?v=uCIC2LNt0bk) -- [ARIA Authoring Practices using roving tabindex in radiogroup example](https://www.w3.org/WAI/ARIA/apg/example-index/radio/radio.html) -- [React-roving-tabindex](https://www.npmjs.com/package/react-roving-tabindex) -- [Angular's ListKeyManager in the Component Dev Kit](https://material.angular.io/cdk/a11y/overview#listkeymanager) -- [FocusZone in Fluent UI](https://developer.microsoft.com/en-us/fluentui#/controls/web/focuszone) -- [Elix component library's KeyboardDirectionMixin](https://component.kitchen/elix/KeyboardDirectionMixin) - -To achieve this goal, we believe that the solution must be done with declarative markup or CSS. +[roving tabindex](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets#technique_1_roving_tabindex) +[[react](https://www.npmjs.com/package/react-roving-tabindex), +[angular](https://material.angular.io/cdk/a11y/overview#listkeymanager), +[fluent](https://developer.microsoft.com/en-us/fluentui#/controls/web/focuszone), +[elix](https://component.kitchen/elix/KeyboardDirectionMixin)]]. +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](https://docs.google.com/document/d/1cOxjjZdvFfpexiN1x-MpNJLXrmbrR0xp1sRCTbaQ29c/) +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 group +the key signal that allows the platform's accessibility infrastructure to make the `focusgroup` accessible by default: -- Signaling to the AT that arrow-key navigation can be used on the focused element. -- Understanding the bounds/extent of the group, providing list context like "X in Y items" - (posinset/setsize) -- Providing a consistent and reliable navigation usage pattern for users with no extra author code +- signaling to the AT to switch to a + ["Focus mode"](https://www.accessibility-developer-guide.com/knowledge/screen-readers/desktop/browse-focus-modes/) + 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. -## 3. Non-Goals +## Non-Goals -In most roving tabindex implementations, the notion of moving _focus_ is -tightly coupled with the notion of _selection_. In some cases, it makes sense to have selection follow -the focus, but in other scenarios these need to be decoupled. For this proposed standard, we decouple -selection from focus and only focus (cough) on the focus aspect: moving focus using the -keyboard among eligible focus targets. Adding selection state, e.g., tracking the currently selected -option in a list, will be a separate feature. To handle selection-based scenarios, we defer to the -CSS proposal [**Toggles**](https://tabatkins.github.io/css-toggle/) which can work nicely with focusgroups. +In some [control patterns](https://www.w3.org/WAI/ARIA/apg/) (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**](https://tabatkins.github.io/css-toggle/), is +[no longer being pursued](https://docs.google.com/document/d/10JxXgeDAJ5zerOdxoWM9ZU9d5iMOdzGJt0EhlhkOkxM/). -No additional UI (e.g., a "focus ring") is associated with focusgroups. A focusgroup -is wholly intended as a logical (transparent) grouping mechanism for a set of already focusable child elements, -providing arrow key navigation among them. All the pre-existing styling/ native UI affordances for -focus tracking are unchanged with this proposal. +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. -## 4. Principles +## Principles 1. Intuitive use in declarative scenarios. Focusgroups - are easy to reason about (self-documenting) in the source markup or CSS. - - provide an intuitive interaction between focusgroups. - - integrate well with other related platform semantics. -2. Focusgroups are easy to maintain and configure. + - provide a rational behavior when nested. + - integrate well with other related platform semantics (e.g., `tabindex`). +1. 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. -3. Complimentary declarative representations in HTML and CSS +1. 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 to conditionally apply focusgroup - behavior under changing layouts. Enables some advanced use cases where selector-based - matching is advantageous. - -## 5. Use Cases - -1. (Child opt-in) Group a set of focusable child elements under a single focusgroup. -2. (Descendent opt-in) Focusable elements deeply nested can participate in a single focusgroup. -3. (Cross Shadow DOM) Focusable elements nested inside a Shadow DOM are discoverable and focusable - when their Shadow Host or an ancestor element declares a focusgroup. -3. (Wrap) Focusgroup can be configured to have wrap-around focus semantics. -4. (Limit directional arrow keys) A focusgroup can be configured to respond to either the logical + - CSS properties allow for responsive design patterns and can avoid redundant markup in some + scenarios. + +## Use Cases + +1. (Element and subtree opt-in) A focusable element and its entire subtree can participate in a + single focusgroup. +1. (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. +1. (Wrap) Focusgroup can be configured to have wrap-around focus semantics. +1. (Limit directional arrow keys) A focusgroup can be configured to respond to either the logical [inline-axis](https://drafts.csswg.org/css-writing-modes-4/#inline-axis) navigation keys (e.g., left and right arrow keys when the focusgroup is in a `horizontal-tb` [writing mode](https://drafts.csswg.org/css-writing-modes/#block-flow)) or @@ -117,180 +323,168 @@ focus tracking are unchanged with this proposal. nodes in a tree view control). See [CSS Logical Properties and Values](https://drafts.csswg.org/css-logical/) for more about logical directions. -5. (Focus movement arrow keys follow content direction) The user's arrow key presses move the focus +1. (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. -6. (Extend same direction) Focusgroups can be nested to provide arrow navigation into multiple - composed widgets (such as lists within lists). -7. (Extend opposite direction) Focusgroups can be nested to provide arrow navigation into composed - widgets with orthogonal navigation semantics (such as horizontal-inside-vertical menus). -8. (Multiple focusgroups) Multiple focusgroups can be established on a single element (advanced CSS - scenario). -9. (Opt-out) Individual elements can opt-out of focusgroup participation (advanced CSS scenario) -10. (Grid) Focusgroups can be used for grid-type navigation (``-structured content or other - grid-like structured content (advanced CSS scenario), but not "presentation" grids). - -## 6. Focusgroup Concepts - -A focusgroup is a group of elements that are related by arrow-key navigation and for which the web -platform provides the arrow key navigation behavior by default (no JavaScript event handlers needed)! -Navigation of focusgroup items happens according to order or structure of the DOM (not how it is -visually rendered). The arrow keys controlling the navigation are mapped +1. (Opt-out) Individual elements can opt-out of focusgroup participation +1. (Grid) Focusgroups can be used for grid-type navigation (`
`-structured content or other + grid-like structured content). + +A use case we are evaluating: + +1. (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 (and are based on the element declaring the focusgroup). +"block/inline-start" directions, respectively (based on the element's directionality that declares +the focusgroup). -This document describes two kinds of focusgroups: **linear focusgroups** and **grid focusgroups**. +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. -Multiple linear focusgroups can be combined together into one logical focusgroup, but linear focusgroups -cannot be combined with grid focusgroups and vice-versa (and grid focusgroups cannot be combined with -each other). - -Focusgroups consist of a **focusgroup definition** that establish **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 (among the possible -focusgroup candidates). +| 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](#grid-focusgroups) | -When a linear focusgroup definition is associated with an element, all of that element's shadow-inclusive direct children -become focusgroup candidates. Focusgroup candidates become focusgroup items if they are focusable, e.g., -implicitly focusable elements like ` + + + + ``` -From outside of the above markup, assume a Tab key press lands focus on the first `
  • `. From -this point, the user can use the arrow keys to move from the beginning of the menubar to the end, or press -Tab again to move outside of this grouping. Alternatively, maybe you have another instance of where -support both Tab _and_ Arrow key navigation may be necessary. No problem, just change the setup so -that all the focusable elements participate in the tab order: +When pressing tab to enter this "toolbar" focusgroup from an element _before_ it, focus will go to +the first ` + + + + ``` -The focusgroup doesn't "remember" the last element that was focused when focus leaves the group -and returns later. Instead, the "leaving" and "returning" logic (for keyboard scenarios) depends on the -preexisting _tabindex sequential focus navigation_ only (those with `tabindex=0` for example). The -[CSS Toggles](https://tabatkins.github.io/css-toggle/) proposal could be used in this same scenario to -track the "toggle state" among this group of toggle-able elements, allowing focus changes to be decoupled -from "selection/toggle" state. - -A future extension of this proposal may allow focus entering a focusgroup to jump to the currently -toggled (or the toggle with the highest value) if any toggles are set in the focusgroup. +### 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 according to Section 6.7.) +developers to support focusgroup behavior across component boundaries. (Component authors that want +to [opt-out of this behavior](#opting-out) can do so.) -Example 4: +Example: ```html - +