diff --git a/site/src/pages/components/scoped-focusgroup.explainer.mdx b/site/src/pages/components/scoped-focusgroup.explainer.mdx index f1d098a34..4ef23e66a 100644 --- a/site/src/pages/components/scoped-focusgroup.explainer.mdx +++ b/site/src/pages/components/scoped-focusgroup.explainer.mdx @@ -8,7 +8,7 @@ layout: ../../layouts/ComponentLayout.astro - Authors: [Jacques Newman](https://github.com/janewman) - Prior Authors from the earlier, broader [focusgroup explainer](/components/focusgroup.explainer): [Travis Leithead](https://github.com/travisleithead), [David Zearing](https://github.com/dzearing), [Chris Holt](https://github.com/chrisdholt) - WHATWG issue: https://github.com/whatwg/html/issues/11641 - - Last updated: 2025-10-7 + - Last updated: 2026-1-15 ## Table of Contents
@@ -50,6 +50,7 @@ layout: ../../layouts/ComponentLayout.astro - [Restricted elements](#restricted-elements) - [Feature detection](#feature-detection) - [Additional features](#additional-features) + - [The focusgroupstart Attribute](#the-focusgroupstart-attribute) - [Enabling wrapping behaviors](#enabling-wrapping-behaviors) - [Limiting linear focusgroup directionality](#limiting-linear-focusgroup-directionality) - [Opting-out](#opting-out) @@ -87,7 +88,7 @@ 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,D-pads, remote controls, gesture +to work) equally well for `focusgroup` navigation behavior (e.g., game controllers, D-pads, remote controls, gesture recognizers, touch-based assistive technologies (AT), etc.). Benefits over ad-hoc scripts (FocusZone, Tabster, bespoke roving tabindex): less boilerplate, standardized axis + wrap behavior (including RTL / vertical), reduced misapplication, and a consistent, testable baseline for AT and UA interoperability. @@ -151,11 +152,11 @@ Opting an element and its subtree out of an ancestor focusgroup, see: [Opting-ou
``` -Specifying a specific item as the [entry point](#entry-point) for focus when entering a focusgroup: +Specifying a specific item as the [start element](#the-focusgroupstart-attribute) for focus when entering a focusgroup: ```html
- +
``` @@ -207,12 +208,12 @@ Current behavior tokens (APG alignment & minimum roles). All listed (except the | `grid` (future) | Grid (2-D) | [`grid`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/grid_role) | (TBD) | [APG Grid](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) | #### Behavior → role mapping & precedence -2. No explicit container `role`: UA maps behavior token to its minimum role (subject to minimum-role conditions described above). -3. Explicit container `role` identical to minimum role: keep as-is. -4. Explicit compatible composite `role`: keep explicit role; behavior navigation still activates. -5. Explicit incompatible `role` (e.g., `button`): UA MAY activate a developer warning. -6. Child role inference only runs when: (a) container role came from behavior mapping, (b) descendant is managed (not opted-out), (c) behavior defines an inferred child role, (d) descendant lacks explicit `role`, (e) inference would not replace richer native semantics. This endures an inferred, minimum role will never override an explicit role. -7. Inference never upgrades variant types (e.g., `menuitemcheckbox` vs `menuitemradio`)—authors must specify those explicitly. +1. No explicit container `role`: UA maps behavior token to its minimum role (subject to minimum-role conditions described above). +2. Explicit container `role` identical to minimum role: keep as-is. +3. Explicit compatible composite `role`: keep explicit role; behavior navigation still activates. +4. Explicit incompatible `role` (e.g., `button`): UA MAY activate a developer warning. +5. Child role inference only runs when: (a) container role came from behavior mapping, (b) descendant is managed (not opted-out), (c) behavior defines an inferred child role, (d) descendant lacks explicit `role`, (e) inference would not replace richer native semantics. This ensures an inferred, minimum role will never override an explicit role. +6. Inference never upgrades variant types (e.g., `menuitemcheckbox` vs `menuitemradio`)—authors must specify those explicitly. This approach separates behavioral intent (token) from accessibility semantics (minimum role mapping) and addresses concerns about reusing ARIA role strings directly as activation tokens. @@ -226,14 +227,14 @@ Reasons to pursue `focusgroup` in parallel: 2. Incremental flexibility: Adding a new eligible behavior token (and optional child inference) is a far smaller spec and implementation change than introducing a new HTML element with parsing, styling, accessibility mapping, and legacy considerations. 3. Low friction upgrade path: Existing ARIA patterns (toolbar of buttons, listbox of options) become declarative with a single attribute rather than refactoring to new tags. 4. Progressive expansion: We can start with a minimal, consensus set (toolbar, tablist, radiogroup, listbox, menu/menubar) and add more patterns as needed. -6. Minimal surface risk: An attribute opt-in is easier to ship, iterate, or adjust (including potential deprecation of an unused token) than an element baked into the content model. -7. Immediate boilerplate win: Solves today's repetitive roving tabindex logic without waiting for multiple element proposals to mature and achieve cross-browser implementation. +5. Minimal surface risk: An attribute opt-in is easier to ship, iterate, or adjust (including potential deprecation of an unused token) than an element baked into the content model. +6. Immediate boilerplate win: Solves today's repetitive roving tabindex logic without waiting for multiple element proposals to mature and achieve cross-browser implementation. `focusgroup` doesn't replace, but complements native elements. It standardizes a widely re-implemented behavioral layer and leaves room for richer, purpose-built elements to integrate or rely on it later. ## Quickstart -The following examples start with the most common composite: a formatting toolbar. +The following examples demonstrate common focusgroup patterns. In this example, the author is using a [tab control pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) where the tab activation @@ -242,7 +243,7 @@ behavior is decoupled from selection ("manual tab activation"): ```html
- +
@@ -251,9 +252,9 @@ behavior is decoupled from selection ("manual tab activation"): ``` What to notice: -* The `focusgroup-entry-priority` attribute on the selected tab determines which tab receives focus when entering the focusgroup. +* The `focusgroupstart` attribute on the selected tab determines which tab receives focus when entering the focusgroup. The `no-memory` value [prevents the focusgroup from remembering the last focused tab](#disabling-focusgroup-memory) so - that focus will always go to the tab with `focusgroup-entry-priority` on re-entry regardless of which element was + that focus will always go to the tab with `focusgroupstart` on re-entry 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 `tabpanel-2` which is next in sequential focus navigation order (because the other @@ -265,7 +266,7 @@ What to notice: **inline** direction (assuming the `
`'s `writing-mode` is `horizontal-tb`). * The author code required to manage the selection of a tab is omitted for brevity. Such code on tab selection change would update `aria-selected` values, the `hidden` state of the controlled `role="tabpanel"` - and move the `focusgroup-entry-priority` attribute to the newly selected tab. + and move the `focusgroupstart` attribute to the newly selected tab. In a third example, the author is creating a [navigation menubar](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/). @@ -337,12 +338,12 @@ Empirical misuse in uncontrolled contexts; correct usage clusters around APG-bac Interactions with explicit `role` vs behavior token mapping; also see [open question around child role inference](#open-questions) ```html @@ -464,9 +465,9 @@ focusgroup candidate and (in this case) the single focusgroup item.
``` -Focusgroup candidates become focusgroup items if they are focusable and do not have a negative `tabindex` value, e.g., implicitly focusable -elements like ` - + ``` -When multiple elements within the same [focusgroup segment](#focusgroup-segments) have the `focusgroup-entry-priority` attribute, the first element with the attribute in DOM order will be prioritized, or if the items are also being managed by reading-flow, the first such item in reading order. The direction of navigation (Tab vs Shift+Tab) does not affect which element is chosen - the same element will always receive focus when entering the segment, promoting consistent user experience. +When multiple elements within the same [focusgroup segment](#focusgroup-segments) have the `focusgroupstart` attribute, the first element with the attribute in DOM order will be prioritized, or if the items are also being managed by reading-flow, the first such item in reading order. The direction of navigation (Tab vs Shift+Tab) does not affect which element is chosen - the same element will always receive focus when entering the segment, promoting consistent user experience. **Key behaviors:** -- Focusgroup items with `focusgroup-entry-priority` are prioritized. -- If no focusgroup item has `focusgroup-entry-priority`, the first focusgroup item in the segment is chosen (in DOM order, or if the items are also being managed by reading-flow, in reading order). -- Memory (when enabled) takes precedence over entry priority - a previously focused element will be restored even if other elements have `focusgroup-entry-priority` +- Focusgroup items with `focusgroupstart` are prioritized. +- If no focusgroup item has `focusgroupstart`, the first focusgroup item in the segment is chosen (in DOM order, or if the items are also being managed by reading-flow, in reading order). +- Memory (when enabled) takes precedence over `focusgroupstart` - a previously focused element will be restored even if other elements have `focusgroupstart` -#### Memory vs Entry Priority Example +#### Memory vs focusgroupstart Example -This example demonstrates how memory takes precedence over entry priority: +This example demonstrates how memory takes precedence over `focusgroupstart`: ```html
- +
``` **Behavior sequence:** -1. **First time entering the focusgroup:** Focus goes to "Italic" (entry priority applies since no memory exists) +1. **First time entering the focusgroup:** Focus goes to "Italic" (`focusgroupstart` applies since no memory exists) 2. **User arrows to "Underline":** Focus moves to "Underline" and memory is updated -3. **User tabs away and then back:** Focus goes to "Underline" (memory restored, entry priority ignored) +3. **User tabs away and then back:** Focus goes to "Underline" (memory restored, `focusgroupstart` ignored) 4. **Using `no-memory`:** With `focusgroup="toolbar no-memory"`, focus would always go to "Italic" regardless of previous focus -#### Multiple Items with the Entry Priority Attribute (Not Recommended) -Focusgroup will always prioritize the first element with the `focusgroup-entry-priority` attribute in DOM order when entering a focusgroup segment, or if the items are also being managed by reading-flow, the first such item in reading order, and will not consider any subsequent elements with the same attribute. +#### Multiple Items with focusgroupstart (Not Recommended) +Focusgroup will always prioritize the first element with the `focusgroupstart` attribute in DOM order when entering a focusgroup segment, or if the items are also being managed by reading-flow, the first such item in reading order, and will not consider any subsequent elements with the same attribute. ```html
- - + +
``` -In this example, "Italic" will receive focus when entering the focusgroup because it appears first in DOM order among elements with `focusgroup-entry-priority`, or if the items are also being managed by reading-flow, it would be the first in reading order among elements with `focusgroup-entry-priority`. +In this example, "Italic" will receive focus when entering the focusgroup because it appears first in DOM order among elements with `focusgroupstart`, or if the items are also being managed by reading-flow, it would be the first in reading order among elements with `focusgroupstart`. #### Interaction with Nested Focusgroups -When focusgroups are nested, each focusgroup manages its own entry priority independently. The `focusgroup-entry-priority` attribute only affects the focusgroup that directly contains the element. +When focusgroups are nested, each focusgroup manages its own `focusgroupstart` independently. The `focusgroupstart` attribute only affects the focusgroup that directly contains the element. ```html
- + - +
- +
- +
``` @@ -890,32 +892,32 @@ When focusgroups are nested, each focusgroup manages its own entry priority inde In this example, there are two distinct focusgroups, and three focusgroup segments: **Main toolbar**: `[Save, Print]`, `[Close, Exit]` -- Entry priority: "Print" (has `focusgroup-entry-priority`) +- Start element: "Print" (has `focusgroupstart`) **Nested toolbar**: `[Bold, Italic, Underline]` -- Entry priority: "Italic" (has `focusgroup-entry-priority`) +- Start element: "Italic" (has `focusgroupstart`) **Sequential navigation behavior:** -1. **Tab into main toolbar:** Focus goes to "Print" (entry priority) -2. **Tab from "Print":** Focus moves to "Italic" (entry priority in nested toolbar) +1. **Tab into main toolbar:** Focus goes to "Print" (`focusgroupstart`) +2. **Tab from "Print":** Focus moves to "Italic" (`focusgroupstart` in nested toolbar) 3. **Tab from nested toolbar:** Focus moves to "Close" (continuing in main toolbar) -4. **Shift+Tab into main toolbar from "After":** Focus goes to "Exit" (entry priority) +4. **Shift+Tab into main toolbar from "After":** Focus goes to "Exit" (`focusgroupstart`) **Directional navigation behavior:** - Arrow key navigation within the main toolbar moves between "Save", "Print", "Close", and "Exit", but skips over the nested toolbar - Arrow key navigation within the nested toolbar moves between "Bold", "Italic", and "Underline" - Each focusgroup maintains independent arrow navigation -This demonstrates how entry priority works independently for each nested focusgroup and how exiting and entering nested focusgroups respects each segment's entry priority. +This demonstrates how `focusgroupstart` works independently for each nested focusgroup and how exiting and entering nested focusgroups respects each segment's start element. #### Interaction with Opted-Out Subtrees -When elements opt out of a focusgroup using `focusgroup="none"`, they can create multiple [focusgroup segments](#focusgroup-segments), each with their own entry priority behavior. +When elements opt out of a focusgroup using `focusgroup="none"`, they can create multiple [focusgroup segments](#focusgroup-segments), each with their own `focusgroupstart` behavior. ```html
- + @@ -925,28 +927,28 @@ When elements opt out of a focusgroup using `focusgroup="none"`, they can create
- + ``` In this example, the opted-out help section creates two focusgroup segments: **Segment 1** (before the opted-out section): `[New, Open, Save]` -- Entry priority: "Open" (has `focusgroup-entry-priority`) +- Start element: "Open" (has `focusgroupstart`) **Segment 2** (after the opted-out section): `[Close, Exit]` -- Entry priority: "Exit" (has `focusgroup-entry-priority`) +- Start element: "Exit" (has `focusgroupstart`) **Sequential navigation behavior:** 1. **Tab into Segment 1:** Focus goes to "Open" 2. **Tab from "Save":** Focus moves to "Help" (first opted-out element) -3. **Tab from "Shortcuts":** Focus moves to "Exit" (entry priority in Segment 2) -4. **Shift+Tab from "Help":** Focus returns to Segment 1, going to "Open" (entry priority) +3. **Tab from "Shortcuts":** Focus moves to "Exit" (`focusgroupstart` in Segment 2) +4. **Shift+Tab from "Help":** Focus returns to Segment 1, going to "Open" (`focusgroupstart`) -** Directional navigation behavior:** +**Directional navigation behavior:** - Arrow key navigation can move freely across both segments, skipping over the opted-out help section. -This demonstrates how entry priority works independently within each segment created by opted-out elements. +This demonstrates how `focusgroupstart` works independently within each segment created by opted-out elements. ## Enabling wrapping behaviors @@ -1012,7 +1014,7 @@ Example: ```html - + ⚠️This `focusgroup` configuration is an error--neither constraint will be applied (which is actually what the author intended). @@ -1041,7 +1043,7 @@ To opt-out: ### Impact on sequential focus navigation -When elements opt-out of a `focusgroup` using `focusgroup="none"`, they can effectively divide the focusgroup for sequential focus navigation purposes, creating multiple tab stops where the focusgroup would normally have only one. This happens because opted-out elements remain in the normal sequential focus navigation order, necessitating the splitting the focusgroup into separate segments to ensure content is not skipped. +When elements opt-out of a `focusgroup` using `focusgroup="none"`, they can effectively divide the focusgroup for sequential focus navigation purposes, creating multiple tab stops where the focusgroup would normally have only one. This happens because opted-out elements remain in the normal sequential focus navigation order, necessitating splitting the focusgroup into separate segments to ensure content is not skipped. Sequential focus navigation within each resulting segment follows the visual reading order established by CSS [`reading-flow`](#reading-flow) when applied to the container or relevant ancestors. @@ -1143,7 +1145,7 @@ change: * The currently remembered element stops being focusable (e.g., a `
` with a `tabindex` has its `tabindex` attribute removed). * The currently remembered element is changed to become excluded from the `focusgroup` (through - `focusgroup="none" on itself or a shadow-inclusive ancestor, or by changing `focusgroup`s: if a new + `focusgroup="none"` on itself or a shadow-inclusive ancestor, or by changing `focusgroup`s: if a new `focusgroup` definition appears on itself or one of its shadow-inclusive ancestor elements). ## Adjustments to sequential focus navigation @@ -1160,14 +1162,14 @@ When a user navigates into a focusgroup using Tab or Shift+Tab, the user agent m #### 2. **Apply focus priority (first match wins):** - If the focusgroup has a **last focused item** and the [`no-memory`](#disabling-focusgroup-memory) token is not present, and the last focused item is within the current segment, focus that item. - - Otherwise, if an item within the segment has the `focusgroup-entry-priority` attribute, focus the first such item in tree order, or if the items are also being managed by reading-flow, focus the first such item in reading order. + - Otherwise, if an item within the segment has the `focusgroupstart` attribute, focus the first such item in tree order, or if the items are also being managed by reading-flow, focus the first such item in reading order. - Otherwise, if an item within the segment is **sequentially focusable** (e.g., has `tabindex="0"` or is a natively focusable element like `