Skip to content

[focusgroup] Add the focusgroup attribute #11723

Open
janewman wants to merge 127 commits intowhatwg:mainfrom
janewman:focusgroup
Open

[focusgroup] Add the focusgroup attribute #11723
janewman wants to merge 127 commits intowhatwg:mainfrom
janewman:focusgroup

Conversation

@janewman
Copy link
Copy Markdown

@janewman janewman commented Sep 29, 2025

#11641

This adds the focusgroup attributes needed to allow for declarative focus navigation using the directional navigation (arrow keys, d-pad).
This specs the behavior detailed in the explainer on OpenUI.


/dom.html ( diff )
/index.html ( diff )
/infrastructure.html ( diff )
/interaction.html ( diff )

@janewman janewman marked this pull request as ready for review November 24, 2025 17:33
@janewman
Copy link
Copy Markdown
Author

Opening up for review, but please keep in mind this is an early draft.

@janewman
Copy link
Copy Markdown
Author

@annevk, during TPAC you expressed interest in taking a look at this draft spec change - I would really appreciate any feedback you had as I work to move this towards something we think accurately captures the behavior and fits in with the existing logic.
Thanks!

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
@smaug----
Copy link
Copy Markdown

It is unclear to me what happens if the page has keydown, or keyup event listener and calls preventDefault() when arrow key is used. Which event listener has some affect and which doesn't?
What if some modifier is used with the key event?

…ng section, improve segment definition with example, and simplify key conflict element handling
@janewman
Copy link
Copy Markdown
Author

janewman commented Feb 2, 2026

It is unclear to me what happens if the page has keydown, or keyup event listener and calls preventDefault() when arrow key is used. Which event listener has some affect and which doesn't? What if some modifier is used with the key event?

I've added a new section that should answer all of these questions, please let me know what you thank and if it still isn't clear.
Thanks for all of the feedback, it is very much appreciated!!

@janewman janewman requested a review from smaug---- February 2, 2026 23:39
alastor0325 and others added 15 commits March 27, 2026 21:07
This change allows select elements with the multiple attribute to be rendered as a drop-down box when the author explicitly sets the size attribute to 1.

The related issue should be resolved by suggesting that mobile browsers and desktop browsers both follow these rules in order to improve cross-platform compatibility.

Fixes whatwg#8189.
This aligns `ancestorOrigins` exposure with referrer policy when using the `iframe referrerpolicy` attribute, so an embedder can prevent revealing its own origin to embedded documents. If an `<iframe>` uses `referrerpolicy="no-referrer"` or `same-origin` (and the parent and child are cross-origin), the parent’s origin and any same-origin ancestors are replaced with opaque origins (until reaching an ancestor that is cross-origin). Other policies continue to expose full origins.

This approach keeps existing behavior by default (for web compat) while addressing privacy concerns with an opt-out.

The algorithm reuses the parent's existing list of ancestor origins, avoiding synchronous cross-process lookups and ensuring a stable snapshot even if ancestors mutate their `referrerpolicy` attributes later.

Fixes whatwg#1918. Closes whatwg#2480.
Instead of having a "scripting flag" and then "fixing" scripts in createContextualFragment(), change "scripting flag" to an enum for clarity.
Currently per spec, #update-the-navigation-api-entries-for-a-same-document-navigation
will be called in different order to the #wait-for-all navigation handler
list depending on if the navigation is intercepted or not. This is not
ideal, but even more so, in case of a "traverse" navigation, the
callback passed to the #wait-for-all and the might be called before
the call to #update-the-navigation-api-entries-for-a-same-document-navigation
has happened.

This means that we'll run the finish steps with the wrong session
history entry.

Since all same document navigations, intercepted or otherwise, eventually
call #update-the-navigation-api-entries-for-a-same-document-navigation
we can ensure that we never run the navigation handlers with bad state.

This patch also reverts the patch that introduced always having a
method tracker, since that was intended to solve this issue but
failed doing so.

Fixes whatwg#12222, fixes whatwg#12165. Reverts whatwg#11814.
* Markup: For some `data-x="attr-input-{max,min}"`, change `attr` to `concept`
* Punctuation: Insert a couple periods
* Punctuation: Change colon to period
…attribute

Specify support for the loading attribute for the video element and audio element using similar conventions to img and iframe, where the attribute already has precedent. Loading has possible values of eager and lazy. When a media element's loading attribute value is lazy, loading of any video or poster image data, as well as autoplay playback, will defer until layout is known and the video is in the viewport.

Fixes whatwg#10376
Fixes whatwg#6636
Key changes:
- Rename 'no-memory' token to 'nomemory' (unhyphenated, matching explainer)
- Add 'nowrap' modifier token for explicitly disabling wrapping
- Add default modifiers per behavior token (toolbar→inline,
  tablist→inline wrap, menu→block wrap, menubar→inline wrap)
- Add default modifiers column to behavior tokens table
- Add text explaining how default modifiers work and interact
  with explicit modifiers
- Update ARIA Role Inference to document <button> child role inference
  eligibility (buttons are the most common composite widget item)
- Add note that aria-orientation is not inferred from inline/block
- Add note about reading-flow CSS property interaction with both
  sequential and directional navigation
- Update directional navigation algorithm to reference effective
  modifiers (including defaults)
- Update wrap check in navigation algorithm to account for nowrap
  and default modifiers
- Replace toolbar example with proper tablist example showing
  default modifiers, focusgroupstart, child role inference, and
  tab panel association
- Add menubar/menu nested focusgroup example demonstrating
  independent nested focusgroups with default axis/wrap behavior

Co-authored-by: Copilot <[email protected]>
- Reword key conflict element definition to emphasize native/built-in
  arrow key behavior (UA-determined), not author-scripted handlers
- Add note with author guidance for scripted key conflicts: use
  focusgroup='none', limit axis, or activation/escape pattern
- Fix scrolling conflict scenario to reference key conflict escape
  behavior and clarify that only focusable scrollable regions
  (UA-determined) are affected

Co-authored-by: Copilot <[email protected]>
@janewman
Copy link
Copy Markdown
Author

@smaug----, @annevk, I would love to get your thoughts on the latest iteration, friendly ping for review, I appreciate your time.

janewman and others added 2 commits April 23, 2026 22:47
When an element enters the document's top layer (e.g., a popover shown
via showPopover(), a modal dialog, or a fullscreen element), exclude it
and its shadow-including descendants from any ancestor focusgroup scope.

Changes:
- focusgroup scope: exclude top-layer elements and their shadow-including
  descendants (qualified so the owner's own focusgroup is unaffected)
- focusgroup segment: top-layer subtrees create segment boundaries
- entry element algorithm: last-focused-item is ineligible while its
  subtree is in the top layer
- clearing conditions: exception preserves last-focused-item when scope
  exit is solely due to top-layer membership
- new "Top layer interaction" section with normative prose and examples

Co-authored-by: Copilot <[email protected]>
Align the top-layer segment boundary clause with the focusgroup="none"
clause by requiring the top-layer element to be sequentially focusable.
This matches the Chromium implementation, which only creates segment
boundaries for excluded subtrees containing focusable content.

Co-authored-by: Copilot <[email protected]>
Copy link
Copy Markdown
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also missing the WebDriver integration piece.

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated

[<span>CEReactions</span>, <span data-x="xattr-Reflect">Reflect</span>] attribute boolean <dfn attribute for="HTMLOrSVGElement" data-x="dom-fe-autofocus">autofocus</dfn>;
[<span>CEReactions</span>, <span data-x="xattr-ReflectSetter">ReflectSetter</span>] attribute long <span data-x="dom-tabindex">tabIndex</span>;
[<span>CEReactions</span>, <span data-x="xattr-Reflect">Reflect</span>] attribute DOMString <dfn attribute for="HTMLOrSVGElement" data-x="dom-focusgroup">focusgroup</dfn>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use DOMTokenList?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I considered, but felt that the behavior-first ordering and modifier pattern felt like it might not fit into the patterns that other attributes that use DOMTokenList have today.

This is not a strong opinion, so if you feel that DOMTokenList is a much better fit for this use case, I'd be OK with changing this.

janewman and others added 4 commits April 24, 2026 20:24
- Rename IDL properties: focusgroup → focusGroup, focusgroupStart → focusGroupStart
- Rename concept terms: 'focusgroup item/owner/scope/segment' → 'focus group ...'
- Revert wrong punctuation change (. not ;) in focusable area conditions
- Replace 'store' with 'set' per spec conventions
- Remove duplicate note about memory/focusgroupstart
- Add cross-reference for 'set of space-separated tokens'
- Add terminal punctuation on list items
- Fix 'effective modifiers' cross-reference
- Fix 'focus group owner of' data-x slug in algorithms
- Move last focused item dfn to concepts section
- Move domintro entries after their attribute dfns
- Move focusGroupStart domintro to focusgroupstart section

Co-authored-by: Copilot <[email protected]>
- Fix radiogroup/listbox default modifiers (wrap, block)
- Add token case-sensitivity statement (ASCII case-insensitive)
- Fix entry element top-layer check with owner-descendant qualifier
- Disambiguate tabindex-ordered scope conjunction
- Add clearing condition for focusgroup attribute removal
- Fix tree order → shadow-including tree order
- Add empty items guard in directional navigation
- Fix toplayer parenthetical and Tab reachability explanation
- Add AT browse mode note and aria-orientation example
- Add missing 'then' in If-conditionals
- Change ul to ul class=brief for key conflict list
- Fix redundant key conflict element re-definition
- Tighten focus group owner definition
- Fix scope definition wording precision

Co-authored-by: Copilot <[email protected]>
On macOS, Safari does not include button elements in the tab order by
default without Full Keyboard Access enabled. Adding explicit
tabindex="0" ensures the examples work cross-platform and matches
the approach taken in the WPT tests.

Co-authored-by: Copilot <[email protected]>
@janewman
Copy link
Copy Markdown
Author

I'm also missing the WebDriver integration piece.

I'm still looking into what we can do here. Regarding having WebDriver toggle the MacOS keyboard setting, I've reached out to James Craig who I've worked with in aria and WPT regarding our options here, waiting to hear a response. In the meantime, I've adjusted all WPTs to accommodate, with all buttons having a tabindex of 0 manually set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.