Focusgroup V2 (Explainer)
- Authors
- @janewman
- Created
- Last Updated
This explainer builds on the Focusgroup explainer, referred to here as V1, and defines only the V2 additions.
Discussion happens on the Open UI focusgroup issues. The WHATWG issue #11641 and pull request #11723 track the V1 baseline these additions build on.
Introduction
People who navigate by keyboard or another directional input encounter inconsistent focus behavior in composite widgets such as action cards, article feeds, and interactive data grids. Authors currently reproduce these patterns with bespoke script, which makes behavior and accessibility vary across sites.
Like V1, Focusgroup V2 aims to give users consistent directional and sequential focus navigation while reducing the script authors must maintain.
Proposal at a glance
V1 defines focusgroup ownership, items, segments, directional and sequential navigation, entry, memory, nested scopes, opt-out boundaries, key conflicts, and role inference. V2 reuses those rules and adds:
itemcontrols, which retains the active item’s controls in sequential focus navigation and removes inactive items’ controls from it.feed, a block-axis linear behavior for streams of articles with nested controls.grid, a two-dimensional behavior for semantically tabular content.
These additions let authors build action lists, feeds, and data grids with one focusgroup API instead of reproducing focus and mutation handling in script.
Non-goals
- CSS activation. Like V1, V2 defines behavior through the HTML
focusgroupattribute only. - Widget state and data. Navigation moves focus only. Authors still manage selection, activation, expansion, scrolling, and content loading.
- Non-rectangular or virtualized grids. The initial
gridmodel is rectangular and spanless. Ragged rows, spans, indexed topology, virtualization, andtreegridremain future work.
V2 tokens and companion attributes
V2 adds the following tokens to the V1 focusgroup token set. They parse under the same rules: tokens are space-separated, none opts the element and its subtree out, and unknown tokens are ignored.
| Token | Kind | What it does |
|---|---|---|
itemcontrols | Modifier; implied by feed and grid | Removes eligible nested content of inactive focusgroup items from sequential focus navigation. |
noitemcontrols | Modifier | Disables explicit or default itemcontrols behavior. |
feed | Behavior | Block-axis linear navigation for ARIA-feed content streams; defaults to itemcontrols. |
grid | Behavior | Two-dimensional directional navigation over a native HTML table; defaults to itemcontrols. Pair with manual for generic markup. |
manual | grid modifier | Used as focusgroup="grid manual". Enrolls direct child rows with focusgrouprow instead of discovering rows from a native table. |
flow, rowwrap, colwrap, rowflow, colflow | grid modifiers | Configure per-axis grid edges: wrap within a row or column, or flow into the adjacent row or column. |
The grid behavior also specializes V1’s existing wrap and nowrap modifiers: wrap expands to both grid axes, while nowrap forces hard edges on both.
The feed and grid behaviors carry itemcontrols as a default modifier. A redundant explicit itemcontrols remains valid; noitemcontrols overrides the default.
focusgrouprow is a boolean structural companion attribute, not a focusgroup token:
| Attribute | Applies to | What it does |
|---|---|---|
focusgrouprow | Every participating direct child row of a grid manual owner | Enrolls the row in that owner’s manual topology and, on a generic element, contributes a one-way inferred row role. Reflected by the focusGroupRow IDL property. It never creates a focusgroup, segment, memory, or nested scope. |
Controls managed by itemcontrols use the V1 focusgroup="none" value. Compound controls can instead use an independent nested focusgroup. Both follow the same active-item filter.
Supported behaviors and role inference
The feed and grid behaviors extend the V1 supported-behaviors mapping. They infer minimum roles one-way on generic elements under the same behavior → role mapping and precedence rules, and never override native semantics or explicit roles.
| Behavior | APG Pattern | Minimum container role (when applied) | Minimum child role(s) (when applied) | Default modifiers |
|---|---|---|---|---|
feed | Feed | feed | article | block itemcontrols |
grid | Grid | grid | row, gridcell | itemcontrols |
Role inference follows the same per-element constraints as V1 and the applicable ARIA in HTML rules. It never assigns a role that those rules disallow for an element. A generic cell wrapper can receive the inferred gridcell role while a native control inside serves as the cell target.
itemcontrols
Chat messages, cards, and list items often contain controls. Authors can keep those controls out of the outer directional-navigation scope with focusgroup="none" or place a compound control in an independent nested focusgroup. itemcontrols then applies one additional rule:
HTML and V1 establish sequential focus navigation.
itemcontrolsremoves nested controls associated with inactive items.
It does not add, promote, or reorder focusable areas. It can modify any behavior and does not imply focus trapping or ARIA virtual focus.
<div role="toolbar" focusgroup="toolbar block itemcontrols" aria-label="Layers">
<div role="group" tabindex="0" aria-label="Background layer">
<span>Background</span>
<button type="button" focusgroup="none" aria-label="Toggle Background visibility">Hide</button>
<button type="button" focusgroup="none" id="more-background" aria-label="More actions for Background">More</button>
</div>
<div role="group" tabindex="0" aria-label="Foreground layer">
<span>Foreground</span>
<button type="button" focusgroup="none" aria-label="Toggle Foreground visibility">Hide</button>
<button type="button" focusgroup="none" aria-label="More actions for Foreground">More</button>
</div>
</div>
<input type="search" aria-label="Filter layers">
In this example:
- Block-axis commands move between the two layer items.
focusgroup="none"keeps the buttons out of directional navigation.- When Background layer is active, V1’s guaranteed-stop rule removes the Foreground layer item from sequential navigation, and
itemcontrolsremoves that item’s buttons. Tab therefore reaches the Background buttons and then the search field. - Shift+Tab enters at the selected item boundary before reaching its nested controls.
noitemcontrols disables only this filter, including the default supplied by feed and grid. It does not change items, directional navigation, topology, or role inference. Using both modifiers is an author error; noitemcontrols takes precedence.
An independent nested focusgroup follows the same rule as opted-out controls: its sequential boundary is removed while its containing item is inactive. When active, the nested focusgroup retains its own items, directional navigation, entry, memory, and role inference.
Eligible nested content
An owner has effective itemcontrols when itemcontrols is explicit or supplied by default and noitemcontrols is absent.
Association and sequential eligibility are separate. Associated nested content maps focus to a containing item for activity and memory, whether or not that content participates in sequential focus navigation. The user agent uses two association paths:
- Opted-out control. Start at a focusable area inside
focusgroup="none"and stop at its nearest parsed focusgroup owner, or at a top-layer boundary. If that owner has effectiveitemcontrols, associate the area with the first owned item reached for a linear owner, or with the cell target reached without leaving the owned cell for a valid grid. The nearest owner remains a boundary when no association is found; do not repeat this path for further enclosing owners. - Independent nested focusgroup. For focus on its owner or anywhere within its scope, start at the nested focusgroup owner and repeat the lookup independently for each enclosing owner with effective
itemcontrols. Interveningfocusgroup="none"and nested-focusgroup ancestors are transparent for this lookup; a top-layer boundary always stops it. For a linear owner, use the first owned item reached. For a valid grid, use the cell target reached without leaving the owned cell.
Eligible nested content is associated nested content that would already participate in sequential focus navigation under HTML and V1. For an opted-out control, it is the focusable area. For an independent nested focusgroup, it is the boundary through which sequential navigation enters the nested segment; the nested owner need not itself be sequentially focusable.
Disabled, inert, non-rendered, and negative-tabindex content is not eligible nested content because it is already absent from sequential focus navigation. itemcontrols never makes content focusable.
The active focusgroup item is the currently focused owned item, or the item associated with the currently focused nested content. Activity follows focus and is owner-relative. Pointer or script focus on associated negative-tabindex content can therefore make an item active and update memory without adding that content to sequential focus navigation.
Each effective owner removes eligible nested content associated with inactive items. Content associated through multiple nested-focusgroup lookups remains available only when every association is active. Unassociated opted-out content remains an ordinary V1 segment boundary, and top-layer content remains excluded. This preserves the standalone-content case discussed in Open UI issue #1489.
The filter never flattens a nested scope or changes sequential order. When a nested focusgroup remains available, traversal encounters its boundary once and applies its own V1 entry algorithm. After leaving it, traversal continues to later content in HTML order.
Entry, memory, and changes
Forward and reverse entry first use the V1 priorities to select an item in the adjacent segment, then focus that item boundary and end the operation. Entry never descends directly into nested controls. Recursively nested owners are entered one boundary per navigation operation.
When memory is enabled, each effective owner stores its owner-relative active item, never the focused nested control. Memory updates after focus changes and relevant state reevaluation; no active item leaves valid prior memory unchanged. nomemory applies only to its owner.
A remembered item must remain in the relevant shadow-inclusive tree, owned by the same focusgroup, and an item of the selected behavior. It must also remain focusable, being rendered, enabled, and non-inert. Focusability here includes programmatic focusability and does not require sequential focusability. Losing any condition clears the item. Restoration additionally requires the item to belong to the segment being entered and satisfy that behavior’s entry requirements.
Changes to focusability, ownership, scope, rendering, inertness, slot assignment, reading-flow, or behavior state do not move focus merely to repair state. Before the next relevant operation, the user agent clears invalid memory, derives activity from current focus, and reapplies the filter. Native key-conflict elements retain their V1 behavior throughout.
grid focusgroups
The grid behavior provides two-dimensional directional navigation for composite widgets whose rows and columns have semantic meaning. Inline-direction commands move within a row; block-direction commands preserve the current column while moving between rows.
Use grid for the ARIA grid pattern, not a static table or a visually wrapping list. The initial authoring model has four rules:
- Use a native table, or enroll direct-child rows explicitly with
manual. - Keep the grid rectangular and spanless.
- Give every cell exactly one cell target: the focusable area that represents its coordinate.
- Keep the owner, rows, wrappers, and other unexcluded descendants unfocusable.
| Attribute value | Topology source |
|---|---|
focusgroup="grid" | Automatic: discovers rows and cells from a native HTML table structure. |
focusgroup="grid manual" | Manual: participating direct flat-tree child rows carry focusgrouprow, and each marked row’s direct flat-tree element children are its cells. Other direct children do not participate in the manual topology. |
<table role="grid" focusgroup="grid" aria-label="Seats">
<tr><td tabindex="0">A1</td><td tabindex="0">A2</td></tr>
<tr><td tabindex="0">B1</td><td tabindex="0">B2</td></tr>
</table>
What to notice:
- Native table structure supplies the rows and cells.
- Each focusable cell is its coordinate’s cell target.
- The explicit
role="grid"exposes composite semantics; authored ARIA does not configure focus topology.
Grid edges are hard by default. The APG grid pattern warns that wrapping can disorient users in data grids; reserve wrap or flow for layout grids where continuation helps.
Topology details
Ownership and order
All grid relationships and paths use the CSS flat tree. User-agent traversal treats open and closed shadow roots alike. Assigned nodes and fallback content participate; unassigned light-DOM children do not. The nearest parsed focusgroup owner owns a candidate structure. Independent nested focusgroups, nested grids, focusgroup="none" subtrees, and top-layer subtrees delimit its scope. Top-layer exclusion applies after flat-tree ownership.
Rows form one sequence and are ordered first by order-modified document order adjusted by reading-flow; native row groups do not add a coordinate level. Cells are then ordered independently within each row by the same rule. Ordering never changes ownership or moves a cell to another row. Coordinates are assigned after ordering, while rectangularity depends only on each row’s cell count.
A structural row or cell participates only if it and its flat-tree ancestors are being rendered and are not inert. Participation is resolved before coordinates and rectangularity. Hiding the same column in every row can therefore produce a narrower valid grid, while hiding cells inconsistently can make row lengths unequal. A fully excluded row contributes nothing; a rendered row with no participating cells makes the grid invalid.
A supported grid is rectangular and spanless: every participating row contains the same nonzero number of cells, and each cell occupies one coordinate. An effective rowspan or colspan greater than one, an empty participating row, or unequal row lengths makes the grid invalid. Place spanning headers, summaries, and full-width messages outside the owner or divide the content into separate grids.
A row or cell cannot belong to more than one grid. A row cannot contain another row owned by the same grid, and a cell cannot contain another cell owned by the same grid. A nested focusgroup inside a cell remains an independent scope under the existing nesting rules.
Automatic table topology
focusgroup="grid" uses native HTML table structure: <thead>, <tbody>, and <tfoot> group rows; <tr> elements are rows; and <td> and <th> elements are cells. Each row must be a direct flat-tree child of the owner or a direct native row group, and each cell must be a direct child of its row. CSS table layout and arbitrary wrappers do not qualify; non-native structure uses manual.
An automatic grid owner without native table structure is invalid.
The automatic behavior does not use structural row enrollment. A focusgrouprow attribute under an automatic grid is ignored for enrollment and role inference and should produce an author diagnostic when grid state is evaluated; it does not change topology or invalidate an otherwise valid grid.
Manual row enrollment
focusgroup="grid manual" supports generic and custom markup by requiring explicit row enrollment. focusgrouprow is the HTML topology signal: every participating row is a direct flat-tree element child of the manual grid owner and carries the attribute, and each marked row’s direct flat-tree element children are its cells. Authored ARIA roles do not enroll rows or cells because they do not configure focus behavior.
focusgrouprow is structural: it never establishes a focusgroup owner or definition, nested scope, focusgroup segment, or memory. On a generic element it contributes a one-way inferred row role under the shared minimum-role precedence. Cells are not marked with a focusgroupcell attribute: each direct element child of a marked row is a cell coordinate, and a generic cell is exposed one-way as gridcell.
Other direct children may provide supporting structure or content. They do not participate in manual topology and do not make the grid invalid solely because they lack focusgrouprow; they remain subject to the grid’s other focusability, ownership, and scope requirements. A marked row with a malformed cell structure is invalid, and the user agent does not fall back to automatic discovery.
A focusgrouprow marker within a manual grid’s owned scope makes that grid invalid if it is not a direct child. A marker inside a nested owner, focusgroup="none" subtree, or top-layer subtree is outside the outer grid’s scope and neither enrolls a row nor invalidates the outer grid. The user agent should diagnose either misuse when it evaluates the relevant grid state.
<div focusgroup="grid manual" aria-label="Sensor readings">
<div focusgrouprow>
<div tabindex="0">A1</div>
<div tabindex="0">A2</div>
</div>
<div focusgrouprow>
<div tabindex="0">B1</div>
<div tabindex="0">B2</div>
</div>
</div>
What to notice:
- The generic owner is exposed one-way as
grid, eachfocusgrouprowelement asrow, and each row’s direct child asgridcell. - Manual mode makes row enrollment explicit for markup that has no native table structure.
Cell targets
Every cell occupies one coordinate and must supply exactly one cell target. The user agent forms the candidates as follows:
- The cell itself is a candidate if it is sequentially focusable.
- Each sequentially focusable descendant is a candidate unless it crosses one of the boundaries below.
- If there is exactly one candidate, it is the target. If there are none or more than one, the structure is malformed; the user agent does not infer a target or silently choose the first candidate.
A candidate must be within the cell’s owned flat-tree subtree. It cannot cross focusgroup="none", an independent nested focusgroup, or a top-layer boundary. HTML sequential focusability already accounts for disabled, inert, and rendering state; authored ARIA states and properties do not affect target eligibility.
Every participating cell must retain exactly one eligible cell target. If its only target becomes disabled, non-rendered, inert, or otherwise ineligible, the grid becomes invalid until every cell again supplies one target. When actions can become unavailable, use a stable focusable cell boundary and mark the actions with focusgroup="none"; disabling an action then leaves the coordinate valid.
A negative-tabindex cell or descendant is ignored as a destination. Its programmatic focusability does not itself invalidate the grid, but its cell must still have another eligible target. When such an element has focus, the user agent resolves its source coordinate from the nearest owned cell without crossing an opt-out, nested-focusgroup, or top-layer boundary. That source can initiate navigation but is never a destination or remembered item. Sequential navigation follows the V1 segment-exit rule clarified in Open UI issue #1434.
Every focusable descendant owned by a valid grid must be a cell target, a managed negative-tabindex source, or behind an opt-out, nested-focusgroup, or top-layer boundary. A focusable row, wrapper, or other unexcluded descendant makes the grid invalid.
A focusable cell target does not implicitly opt out its descendants. Each unexcluded sequentially focusable descendant remains another target candidate, so the cell supplies multiple candidates and the grid is invalid. Authors currently must use focusgroup="none" for individual nested controls or an independent nested focusgroup for a compound control.
Multi-control cells and itemcontrols
By default, grid implies itemcontrols, and each cell target is a grid focusgroup item — either a focusable cell boundary or one selected descendant. Grid association is coordinate-relative: nested controls map to their cell target even when it is a sibling rather than an ancestor. The cell target remains the coordinate’s sole directional target.
The active grid item is the cell target whose coordinate contains current focus. itemcontrols removes nested controls from every inactive cell and leaves them available in the active cell.
noitemcontrols leaves grid topology, targets, and directional navigation unchanged. Opted-out controls and independent nested focusgroups instead follow V1 segment and sequential-navigation rules, without active-cell filtering.
For a complex cell, prefer a focusable cell boundary and mark its controls as nested content. This provides a stable labeled target without privileging one control over its siblings.
The following native data grid uses table structure for automatic topology:
<table role="grid" focusgroup="grid" aria-label="Project files">
<thead>
<tr>
<th tabindex="0">Name</th>
<th tabindex="0">Status</th>
<th tabindex="0">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td tabindex="0">Design specification</td>
<td tabindex="0">Ready</td>
<td tabindex="0" aria-label="Actions for Design specification">
<a href="/files/design-spec" focusgroup="none">Open</a>
<button type="button" focusgroup="none" aria-label="More actions for Design specification">More</button>
</td>
</tr>
<tr>
<td tabindex="0">Research notes</td>
<td tabindex="0">Draft</td>
<td tabindex="0" aria-label="Actions for Research notes">
<a href="/files/research-notes" focusgroup="none">Open</a>
<button type="button" focusgroup="none" aria-label="More actions for Research notes">More</button>
</td>
</tr>
</tbody>
</table>
What to notice:
- The same cell and control structure works with manual topology when each participating direct-child row carries
focusgrouprow. - Each focusable cell is its coordinate’s cell target. The links and buttons use
focusgroup="none"so they do not become additional targets. - Directional navigation moves between cell boundaries. With the default
itemcontrols, controls in inactive cells are removed from sequential focus navigation, so Tab from an active Actions cell can enter its first nested control. - Shift+Tab from the first nested control returns to the active cell boundary. The model does not require an Enter or Escape drill-in mode.
A nonfocusable cell may instead use one descendant as its cell target and mark sibling controls with focusgroup="none". Use this pattern only when one control clearly represents the cell and preserves its row and column context. Prefer a focusable boundary when controls are peers or consume directional input.
Many existing grids use Enter or F2 to enter a multi-control cell and Escape to return. itemcontrols provides a non-modal alternative: directional navigation stays on the cell target, while sequential navigation can reach controls in the active cell.
grid wrapping and flow
A grid edge is the boundary reached when a directional operation would move before the first or after the last coordinate in the current row or column. A grid focusgroup configures each edge independently. The inline-axis modifiers (rowwrap, rowflow) control what happens when inline movement passes a row’s first or last cell. The block-axis modifiers (colwrap, colflow) control what happens when block movement passes a column’s first or last row. The broad wrap and flow tokens set both axes at once, and nowrap forces hard edges.
For a grid, wrap applies independently within the current row and current column. It does not continue through the row-major coordinate sequence as linear focusgroup wrapping does. flow supplies continuation into the next row or column, including outer-edge continuation under the selected flow rules.
| HTML (attribute value) | Inline edges (within a row) | Block edges (within a column) |
|---|---|---|
focusgroup="grid" (default) | Hard edge | Hard edge |
focusgroup="grid wrap" (= rowwrap colwrap) | Wrap to the opposite end of the same row | Wrap to the opposite end of the same column |
focusgroup="grid flow" (= rowflow colflow) | Cross a row edge into the next or previous row | Cross a column edge into the next or previous column |
focusgroup="grid rowwrap" | Wrap to the opposite end of the same row | Hard edge |
focusgroup="grid colwrap" | Hard edge | Wrap to the opposite end of the same column |
focusgroup="grid rowflow" | Cross a row edge into the next or previous row | Hard edge |
focusgroup="grid colflow" | Hard edge | Cross a column edge into the next or previous column |
focusgroup="grid nowrap" | Hard edge (explicit) | Hard edge (explicit) |
At the outermost edge, flow continues from the final cell or column to the first, and in reverse from the first to the final.
If wrapping or flow resolves to the starting target, focus does not move and the operation is not consumed.
The parser expands wrap to rowwrap colwrap and flow to rowflow colflow, then resolves each axis:
- If
nowrapappears with any wrap or flow modifier, the combination is an author error and both axes use hard edges. - If one axis has both wrap and flow, that axis uses a hard edge; the other axis is resolved independently.
- Otherwise, the axis uses its specified wrap or flow behavior, or a hard edge when neither is present.
A conflict does not invalidate the grid. For example, wrap flow and nowrap flow produce two hard axes.
grid directional navigation
grid navigation is defined as a set of abstract directional operations, following the same input-modality abstraction as linear focusgroups: moving focus by spatial direction is the core operation, and mapping a specific input device onto it is the user agent’s responsibility. The grid owner’s computed writing mode and direction determine the inline and block axes.
| Directional operation | Result |
|---|---|
| Inline-start or inline-end | Moves to the target in the previous or next cell in the current row; at a row edge, applies the inline wrap or flow setting. |
| Block-start or block-end | Moves to the target in the previous or next cell in the current column; at a column edge, applies the block wrap or flow setting. |
| Row start or row end | Moves to the target in the first or last cell of the current row. |
| Grid start or grid end | Moves to the target in the first or last cell in row-major order. |
User agents MAY map keyboard commands to these operations: for example, Arrow keys for inline and block movement, Home and End for row boundaries, and Ctrl+Home and Ctrl+End for grid boundaries. These bindings are optional; the abstract operations are the normative core, and other input devices map their own controls. grid does not define Page Up or Page Down.
grid navigation moves focus only; like the linear behaviors it does not change selection, activation, expansion, or content loading.
grid navigation runs only when the relevant input event is not canceled, focus remains on the original focused area, and no native action claims the command. The user agent resolves the nearest parsed owner, source coordinate, and current eligible destination; invalid owners remain boundaries. It consumes the command only when the destination receives focus, leaving unsuccessful commands available to ordinary behavior.
Native key conflicts in grids
A native key-conflict cell target retains each directional input claimed by its native behavior. The user agent suppresses only the grid operation mapped to a claimed input; another mapped operation remains available when the event is not canceled and no native action claims it. This is the operation-specific conflict rule inherited from V1.
Tab and Shift+Tab use HTML sequential focus navigation, not spatial grid relationships. Under the V1 escape algorithm, focusgroup makes the next or previous eligible cell target available in that order. itemcontrols can still remove nested controls from inactive cells, but it does not replace HTML ordering.
Sequential entry, memory, and mutations
A grid owner has one focusgroup memory; rows and focusgrouprow markers do not have separate memory. A valid grid uses the same cell targets for directional navigation, sequential entry, focusgroupstart, and memory:
- The shared segment rules determine the grid segment being entered.
- When memory is enabled, a remembered eligible cell target in that segment has priority.
- Otherwise, an eligible cell target with
focusgroupstarthas priority. - Otherwise, the first eligible cell target in sequential focus navigation order becomes the entry target.
The selected cell target is the grid segment’s single sequential entry target. nomemory disables restoration. The owner’s grid memory stores the target for the active coordinate, never a nested focused control. Focus outside every grid item does not replace valid prior memory.
focusgroupstart changes only entry priority. It does not alter coordinates, directional order, wrapping, or flow. A marker on a row, wrapper, negative-tabindex element, or other ineligible descendant is ignored.
grid state and invalid structures
Each parsed grid owner has one conceptual grid state. Before any observable operation that depends on that state — directional navigation, a sequential-focus query or entry, focusgroupstart or memory resolution, or inferred-role computation — the user agent builds or reuses one snapshot:
- Select automatic or manual topology and apply flat-tree ownership and scope boundaries.
- Collect participating rendered, non-inert rows and cells; order rows, then cells within each row; and assign coordinates.
- Validate the owner, row and cell relationships, rectangular spanless shape, manual row markers, target candidates, and other owned focusable descendants.
- If every check succeeds, record a valid state with its coordinates and targets. Otherwise, record an invalid state.
The state is invalid if the owner is focusable; a manual-row marker, row, or cell is misplaced; the shape is unsupported; a cell has zero or multiple target candidates; or a non-target descendant is focusable. Directional navigation, memory, sequential-entry collapse, segment behavior, and inferred roles all use this state and do not run while it is invalid.
A relevant DOM, attribute, style, slot-assignment, or reading-flow change marks the snapshot stale. A change that might make the remembered target ineligible conservatively clears that memory without requiring an immediate topology or layout update. Merely restoring eligibility later does not restore the cleared memory. The user agent rebuilds the snapshot before the next observable focusgroup operation. State repair alone does not move focus; normal HTML focus disposition still applies.
After a valid rebuild, an owner with effective itemcontrols updates grid memory from current focus under the owner-relative rule.
Ignoring grid focusgroup behavior never makes content inert. Normal DOM focus, sequential focus navigation, element interaction, authored roles, native semantics, and custom-element default semantics remain in effect.
When a user agent evaluates an invalid grid state, it should report a developer diagnostic that identifies the grid owner and invalid-state category and provides category-specific remediation. When practical, it may also identify an offending row, cell, marker, or target. User agents may deduplicate a diagnostic while the same state version remains invalid. Diagnostics are emitted when state is evaluated, not synchronously for every mutation; the console or developer-tools surface remains implementation-defined.
An invalid parsed focusgroup owner remains an ownership and dispatch boundary. Descendants do not leak into an ancestor focusgroup, and dispatch does not search past the invalid owner. The boundary disappears only when the attribute no longer parses as a focusgroup owner.
feed focusgroups
feed is a V1 linear focusgroup behavior with block and itemcontrols as defaults. noitemcontrols disables the latter. V1 item discovery, ownership, boundaries, directional navigation, and key-conflict rules apply unchanged.
feed maps to the ARIA feed role on a generic container and infers article on eligible generic items, following the same minimum-role precedence rules as other behaviors.
Social feed example
<div focusgroup="feed" aria-label="Posts">
<div tabindex="0" aria-labelledby="post1-title" aria-describedby="post1-body"
aria-posinset="1" aria-setsize="-1">
<h2 id="post1-title">Foo Bar</h2>
<p id="post1-body">Post content…</p>
<button type="button" focusgroup="none">Like</button>
<button type="button" focusgroup="none" id="comment-foo-bar">Comment</button>
</div>
<div tabindex="0" aria-labelledby="post2-title" aria-describedby="post2-body"
aria-posinset="2" aria-setsize="-1">
<h2 id="post2-title">Baz Bo</h2>
<p id="post2-body">Post content…</p>
<button type="button" focusgroup="none">Like</button>
<button type="button" focusgroup="none">Comment</button>
</div>
</div>
<input type="search" aria-label="Search posts" />
What to notice:
- The generic container and its top-level items receive inferred
feedandarticleroles. Each article includes the labeling and set-position metadata recommended by the APG feed pattern;aria-setsize="-1"means the total is unknown. - Block-axis commands move between articles. Nested feeds remain independent.
itemcontrolsremoves the inactive article’s buttons from sequential focus navigation. Tab enters the active article’s opted-out buttons, Shift+Tab returns to the article, and Tab from#comment-foo-barmoves to the search field.- With
focusgroup="feed noitemcontrols", block-axis navigation remains, but the opted-out buttons follow V1 segment and sequential-navigation rules.
Relationship to ARIA feeds
The APG feed pattern establishes an interoperability contract. Assistive technologies keep users in reading mode and focus the article containing the reading cursor; the page manages scrolling and content loading.
The APG recommends Page Down and Page Up as one interface for scripted feed navigation. focusgroup="feed" uses the V1 linear model instead. Authors who need a different keyboard model can omit focusgroup while retaining role="feed".
Dynamic content
When feed items change, the focusgroup updates its item set before the next directional or sequential operation. Virtualized feeds expose only rendered items.
Authoring guidance
- Use
itemcontrolswhen controls should be sequentially reachable only within the active item. Usefocusgroup="none"for individual controls and a nested focusgroup for a compound control. - Use
focusgroup="feed"for block-axis article navigation and include the metadata recommended by the APG feed pattern. - Use
gridonly for semantic rows and columns. Native tables userole="grid" focusgroup="grid"; generic markup usesfocusgroup="grid manual"and direct-childfocusgrouprowrows. - Keep grids rectangular and spanless with one cell target per cell. Prefer a stable focusable cell boundary for multiple or conditionally available controls.
- Use
wraporflowonly when continuation at grid edges helps users. Usefeed, a linear focusgroup, or script when rows rather than cells are the navigation unit.
Feature detection
V2 extends V1 feature detection:
const probe = document.createElement('div')
const hasFocusgroup = 'focusGroup' in HTMLElement.prototype
const supportsGrid = hasFocusgroup && probe.focusGroup.supports('grid')
const supportsManualGrid = supportsGrid && probe.focusGroup.supports('manual')
const supportsFeed = hasFocusgroup && probe.focusGroup.supports('feed')
const supportsItemcontrols = hasFocusgroup && probe.focusGroup.supports('itemcontrols')
const supportsNoItemcontrols = hasFocusgroup && probe.focusGroup.supports('noitemcontrols')
const hasFocusgroupRow = 'focusGroupRow' in HTMLElement.prototype
DOMTokenList.supports() reports whether the user agent recognizes a token. It does not validate an owner’s roles, descendants, topology, or targets.
Alternatives considered
Continue author-scripted widgets
Authors can continue implementing roving focus, active-item filtering, and grid navigation in script. This remains necessary for interaction models outside V2, but it reproduces complex focus and mutation handling across sites and produces inconsistent behavior for users.
Require an explicit cell-interaction mode
Many existing grids use a modal cell-interaction model. The APG grid pattern describes using Enter or F2 to enter a cell’s controls and Escape to return. That pattern remains available to scripted grids. V2 instead uses itemcontrols to remove eligible nested content from inactive coordinates without adding mode-switch commands.
Require a focusable boundary for every grid cell
Requiring a focusable cell boundary would simplify target selection and remains the recommended pattern for complex cells. V2 also permits one eligible descendant when a single primary control can represent the coordinate without an extra focus stop.
Derive focus topology from authored ARIA
Authored grid, row, gridcell, and index properties could describe topology, including virtualized content. V2 does not use them as behavior configuration because ARIA describes accessibility semantics and should not silently change focus behavior. The initial model derives topology from HTML structure and defers indexed or split-grid topology.
Open questions
itemcontrols
What should the itemcontrols and noitemcontrols modifiers be called so their active-item sequential-navigation behavior is clear from their names? See Open UI issue #1489.
Should a linear focusgroup support a mode in which peer items remain reachable by both sequential and directional navigation? See Open UI issue #1435.
How does outer directional navigation resume while focus is inside a nested control, especially when that control consumes the same directional input? See Open UI issues #1396 and #1404.
grid
How should a future grid extension represent ragged rows, spans, and gaps while preserving a stable coordinate model for directional navigation, wrapping, flow, memory, and focusgroupstart? See Open UI issue #1495.
How much grid topology can user agents infer safely from HTML structure, and where should authors provide explicit signals to avoid ambiguous or fragile behavior? See Open UI issue #1496.
Accessibility and internationalization considerations
Accessibility
V2 changes which focusable areas participate in directional and sequential navigation, but it does not supply widget names, states, selection, or activation. Authors remain responsible for appropriate HTML and ARIA semantics, accessible names, and state updates. The one-target-per-cell rule, stable-boundary guidance, native key-conflict precedence, and invalid-state fallback prevent ambiguous targets or interference with native control input.
A descendant cell target can omit context that a focusable cell boundary would expose. Authors should test both patterns with keyboard users and assistive technologies, preserve row and column context in accessible names, and prefer a boundary when controls are peers or consume directional input.
Internationalization
The grid owner’s computed writing mode and direction define inline and block operations, so directional navigation follows the page’s writing system. HTML sequential focus navigation continues to govern Tab and Shift+Tab. V2 introduces no locale-sensitive text processing, and authors localize labels and descriptions through existing HTML and ARIA mechanisms.
Privacy and security considerations
The V2 additions introduce no new privacy or security concerns beyond V1. See the V1 privacy and security considerations. grid, feed, and itemcontrols navigation move focus within already-rendered, author-provided content and expose no new information to script or other origins.
Design decisions
- HTML structure and platform focusability define grid topology and targets. Inferred roles describe accessibility semantics one-way; authored ARIA never configures focus behavior. The boundary between inferred topology and explicit author signals remains under discussion in Open UI issue #1496.
- An independent nested focusgroup retains its own scope. Its sequential boundary uses the same active-item filter as opted-out controls, while its separate association path can map it to multiple enclosing owners. This resolves the composition case discussed in Open UI issue #1489.
Acknowledgments
Thanks to the Open UI Community Group for its contributions to the focusgroup proposal.
Open UI