You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[focusgroup] Add sections on scrolling and keyboard conflicts (#1289)
* Add key-conflict explanation and escape mechanism. Add small section on how this interacts with scrolling.
Update date.
* Update example
* update author guidance.
* Update site/src/pages/components/scoped-focusgroup.explainer.mdx
Co-authored-by: Philipp Gfeller <[email protected]>
* Add more explaination and examples, fix broken links.
* Add reference to open issue and potential workaround.
---------
Co-authored-by: Philipp Gfeller <[email protected]>
@@ -608,48 +611,122 @@ sure users can "escape" these elements. Built-in elements provide this via the t
608
611
strategies might include requiring an "activation" step before putting focus into the interactive
609
612
control (and an Esc key exit to leave).
610
613
611
-
The focusgroup's [memory](#last-focused-memory) may also cause unexpected user interactions if
612
-
authors are not careful. For example, without any author mitigations, an interactive control inside
613
-
a focusgroup may inadvertently prevent the user from accessing other focusgroup items:
614
+
#### Key conflict elements
615
+
616
+
When there is a conflict between the arrow keys consumed by the interactive element and the focusgroup's navigation, focusgroup will **not** interfere with the interactive element's behavior. This means that the normal way to move focus between focusgroup items (arrow keys) will **not** work when focus is within such an element.
617
+
618
+
Examples:
619
+
-`<input>` elements (most, but not all types use arrow keys)
620
+
-`<textarea>` elements
621
+
-`<select>` elements when the arrow-key axis used by the focusgroup is the same axis used by the select
622
+
- Elements with `contenteditable`
623
+
- Focusable scrollable regions, when the scroll direction is in the same axis as the focusgroup
624
+
- Custom elements with arrow key handlers
625
+
- Elements with `preventDefault()` on arrow keys
626
+
- Audio and video elements with visible controls
627
+
- Iframes and object tags with focusable elements inside
628
+
629
+
For native elements that conflict, the user agent will provide an [escape behavior](#escape-behavior-for-native-key-conflict-elements).
630
+
631
+
For authors that add scripted key handlers that consume arrow keys, they should consider the following:
632
+
- Implementing an escape behavior similar to the [one provided for native key conflict elements](#escape-behavior-for-native-key-conflict-elements)
633
+
- Avoiding conflicts by limiting the focusgroup's axis of movement via [limiting linear focusgroup directionality](#limiting-linear-focusgroup-directionality)
634
+
- Using `focusgroup="none"` to opt-out of focusgroup behavior entirely for conflicting elements
635
+
- Implementing an "activation" step to enter the custom element (and an "escape" step to leave it)
636
+
637
+
#### Escape behavior for native key conflict elements
638
+
639
+
When there is a conflict between the arrow keys consumed by the interactive element and the focusgroup's navigation, focusgroup will **not** interfere with the interactive element's behavior. Instead, focusgroup will provide a way to "escape" the interactive element via the tab key (and Shift+Tab). This means that when focus is within such an element, pressing Tab will move focus to the next focusable element in the focusgroup (if any), and Shift+Tab will move focus to the previous focusable element in the focusgroup (if any).
640
+
641
+
**Important:** These special behaviors only apply when there is an actual conflict between the arrow keys consumed by the interactive element and the focusgroup's navigation. For example, a focusable scroll container that only uses up/down arrows for scrolling in a focusgroup with `inline` restriction (left/right arrows only) would not be considered a key conflict element for up/down arrow keys, since those keys don't conflict with the focusgroup's navigation.
642
+
643
+
There are two categories with different behaviors:
644
+
645
+
**1. Native key conflict elements**
646
+
647
+
For elements with built-in arrow key behaviors, user agents automatically provide tab-escape functionality. When focus is within such elements, the immediate neighboring focusgroup items become available in the normal tab order. Since focus is already within the focusgroup, memory is **not** taken into consideration, though the author has control over which element is focused next via `tabindex` ordering, using the same considerations as [sequential focus navigation](#adjustments-to-sequential-focus-navigation).
- The input field **is** a focusgroup item reachable via arrow keys.
663
+
- Arrow keys are consumed for text cursor navigation once focus is within the input.
664
+
- To ensure the user can still reach the other elements in the focusgroup, pressing Tab moves focus to the "Save" button, and Shift+Tab moves focus to the "Italic" button.
665
+
- The "Go" button is reachable via arrow keys because it is still a focusgroup item, but the `tabindex` of -1 prevents it from being the next candidate when tabbing, matching the behavior used when entering a `focusgroup`.
- If focus was within the "Search" input, tab will invoke the escape behavior, moving focus to "Go".
681
+
682
+
### Scrolling interactions
683
+
684
+
Focusgroups must coexist with scrolling behavior, as arrow keys are commonly used for both focus navigation and scrolling. The priority and interaction between these behaviors depends on the context:
685
+
686
+
#### 1. Focusgroup within a scrollable region
687
+
688
+
This is the most common scenario—a focusgroup contained within a page or region that can scroll.
689
+
690
+
**For focusgroups with wrap behavior:** Focus navigation takes priority over scrolling. Arrow keys will move focus between focusgroup items, wrapping from end to start as configured. Scrolling will only occur as needed to bring the focused element into view.
691
+
692
+
**For focusgroups without wrap behavior:** Focus navigation takes priority until the focus reaches a boundary (first or last item). Once at a boundary, continuing to press arrow keys in the direction will allow normal scrolling behavior to resume.
693
+
694
+
**For focusgroups with axis restrictions:** If a focusgroup limits arrow keys to a specific axis (using `inline` or `block` tokens), then arrow keys in the cross-axis will be available for scrolling.
When the `combobox` input element is focused, it is remembered by the `focusgroup`'s memory.
635
-
The `<input>` element traps nearly all keystrokes by default, including the arrow keys that might
636
-
have been used to reach the "Increase/Decrease Font" buttons. When the user presses tab, focus
637
-
exits the `focusgroup`. Later, when focus re-enters, the `focusgroup` will put focus back on the
638
-
`<input>` element (because of its memory), and the cycle continues with no way to get to the two
639
-
following buttons via keyboard interaction alone.
640
-
641
-
Fortunately, there are several solutions to this problem:
642
-
- Remove `tabindex=-1` from the "Increase/Decrease Font" buttons.
643
-
- Move the "Increase/Decrease Font" buttons before the `combobox`. (Refer to "Avoid including
644
-
controls whose operation requires the pair of arrow keys used for toolbar navigation" in the
645
-
[Toolbar control pattern](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/).) Additionally,
646
-
[opt-out](#opting-out) the `<input>` control from `focusgroup` participation so that
647
-
arrow keys skip it. Alternatively,
648
-
[turn off the `focusgroup`'s memory](#disabling-focusgroup-memory) so that focus isn't
649
-
automatically returned to the `combobox`.
650
-
- Use script to intercept `focusgroup`-related keydown events on the `<input>` and move focus
651
-
manually. Also consider [limiting the `focusgroup`](#limiting-linear-focusgroup-directionality) to
652
-
one axis and reserving the other axis for operating the `<input>`.
711
+
In this example:
712
+
- Up/down arrows navigate between list items in the focusgroup
713
+
- Left/right arrows scroll the container horizontally (if needed) since the focusgroup is limited to `block`
714
+
- Focus is automatically scrolled into view when navigating between items that are off-screen
715
+
- Authors should be aware that since focusgroup navigation takes priority over scrolling, they should take care when constructing large focusgroups to avoid a situation where content can be missed when jumping from one item to another.
716
+
717
+
**Accessibility considerations:** When focusgroup items are separated by large amounts of content, arrow key navigation can skip over intermediate content that users might need to read. See the [open question about scrolling behavior](#open-questions) for potential solutions being considered.
718
+
719
+
#### 2. Scrollable region within a focusgroup
720
+
721
+
When a scrollable element is contained within a focusgroup, the behavior depends on whether there's a conflict between the focusgroup's arrow key handling and the scrollable element's needs.
722
+
723
+
**No conflict scenarios:**
724
+
- Focusgroup limited to one axis, scrollable element scrolls on the cross-axis.
725
+
- Scrollable element that scrolls via different keys (Page Up/Down, etc.)
726
+
727
+
**Conflict scenarios:** When both the focusgroup and the scrollable element want to handle the same arrow keys, the scrollable element is treated as a [key conflict element](#key-conflict-elements). This means:
728
+
- Arrow keys are consumed by the scrollable element for scrolling.
729
+
- If this is a native element, and not custom script, then Tab/Shift+Tab can be used to move to adjacent focusgroup items following the [escape behavior for native key conflict elements](#escape-behavior-for-native-key-conflict-elements).
653
730
654
731
### Restricted elements
655
732
@@ -912,6 +989,10 @@ candidate, then any descendants that precede (or follow) the first (or last) foc
912
989
broad-reaching ancestral `focusgroup`s won't necessarily "steal" focus from descendant `focusgroup`s
913
990
during sequential focus navigation.
914
991
992
+
Additionally, when focus is within [native key conflict elements](#key-conflict-elements), immediate
993
+
neighboring focusgroup items are automatically made available in the tab order to provide an
Some focusable data is structured not as a series of nested linear groups, but as a
@@ -1128,9 +1209,9 @@ too complicated).
1128
1209
### Authoring guidance
1129
1210
1. Put the behavior token first: `focusgroup="tablist wrap"`, `focusgroup="toolbar"`.
1130
1211
2. Omit common child roles unless you need a variant (checkbox / radio menu items, mixed controls, etc.).
1131
-
3. For detailed precedence (mismatches, inference limits, overrides) see [Precedence & Overrides](#precedence--overrides).
1212
+
3. For detailed precedence (mismatches, inference limits, overrides) see [Behavior → role mapping & precedence](#behavior--role-mapping--precedence).
1132
1213
1133
-
###Alternatives considered
1214
+
## Alternatives considered
1134
1215
When considering how to ensure that `focusgroup` usage is scoped to scenarios we want, the following
1135
1216
approaches were considered.
1136
1217
1. Role-required gating (original): only activates when an eligible `role` attribute is present. Rejected: couples behavior activation to ARIA; breaks precedent.
@@ -1153,7 +1234,9 @@ approaches were considered.
1153
1234
- (C) Split into two attrs: `pattern="tablist" focusgroup="wrap"` (clearer separation, extra verbosity & API surface).
1154
1235
- (D) Native elements only (e.g., future `<tabs>`, `<toolbar>`, `<menubar>`); attribute becomes redundant—risk: slower coverage, custom element ecosystems still need declarative navigation.
1155
1236
- Criteria to decide: author error rate, implementation complexity, consistency with existing HTML token patterns, incremental ship path.
1156
-
8.**Keep or drop child role inference (or defer as future consideration):** Should v1 exclude automatic child role assignment entirely to reduce complexity
1237
+
6.**Scrolling behavior when focus target is not in view:** Should focusgroup navigation automatically prioritize scrolling over focus movement when the next focusable item is not currently visible? This addresses accessibility concerns where arrow key navigation can skip over intermediate content that users need to read (see [GitHub issue #1008](https://github.com/openui/open-ui/issues/1008)). Potential solution:
1238
+
- Temporarily disable focusgroup navigation when the target item is out of view, allowing normal scrolling until the item becomes visible.
1239
+
7.**Keep or drop child role inference (or defer as future consideration):** Should v1 exclude automatic child role assignment entirely to reduce complexity
1157
1240
and perceived overreach (keeping only container pattern + navigation)? Rationale for revisiting: reviewer concern about mixing semantics & behavior;
1158
1241
authors can still supply explicit roles; deferring would let us ship navigation sooner and gather data on real author pain before standardizing inference.
1159
1242
@@ -1176,6 +1259,8 @@ Here is a short list to issue discussions that led to the current design of focu
1176
1259
*[focusgroup definitions should not be limited to direct-children](https://github.com/openui/open-ui/issues/989)
1177
1260
*[focusgroup should include a memory](https://github.com/openui/open-ui/issues/537)
1178
1261
*[focusgroup should be restricted to some elements only](https://github.com/openui/open-ui/issues/995)
1262
+
*[Default behaviour for key conflict elements](https://github.com/openui/open-ui/issues/1281)
1263
+
*[Scrolling interactions with focusgroup](https://github.com/whatwg/html/issues/11641)
1179
1264
1180
1265
See other [open `focusgroup` issues on GitHub](https://github.com/openui/open-ui/issues?q=is%3Aissue+is%3Aopen+label%3Afocusgroup).
0 commit comments