1195 content planner improve inline banner accessibility#23231
Open
vraja-pro wants to merge 15 commits intorelease/27.6from
Open
1195 content planner improve inline banner accessibility#23231vraja-pro wants to merge 15 commits intorelease/27.6from
vraja-pro wants to merge 15 commits intorelease/27.6from
Conversation
…lity Adds role="group" and aria-label to the InlineBanner container so screen readers announce the banner as a distinct region when navigating with the virtual cursor.
Gutenberg's useTabNav hook runs in the bubble phase and redirects Tab to sentinel divs when the next tabbable is not inside a [data-block] wrapper.
The banner sits outside any real block, so it was always skipped.
Two changes fix this:
- Add data-block="yoast-content-planner-banner" to the wrapper div so that intra-banner Tab works via Gutenberg's existing null === null path in isInSameBlock.
- Attach a capture-phase keydown listener on the editor document that fires before Gutenberg's bubble handler. When Tab crosses the banner boundary
(entering or leaving), it calls preventDefault() and manually moves focus; Gutenberg's early-return guard then fires and leaves focus alone.
The navigation logic is extracted to a standalone helper so it can be unit tested independently. @wordpress/dom is added as an explicit direct dependency since it is now imported directly.
Covers all branches: early returns (defaultPrevented, non-Tab key, Shift+Tab, null banner, no next tabbable), Tab into banner, Tab out of banner, intra-banner Tab, and Tab between two elements both outside the banner.
Coverage Report for CI Build 0Coverage increased (+0.03%) to 56.671%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats💛 - Coveralls |
Contributor
There was a problem hiding this comment.
Pull request overview
Improves the accessibility of the Content Planner inline banner in the Gutenberg editor by ensuring its controls are reachable via keyboard tab navigation and correctly announced by assistive technologies.
Changes:
- Adds a capture-phase Tab handler (plus unit tests) to manage focus when tabbing across the inline banner boundary.
- Marks the banner wrapper with a
data-blockattribute to satisfy Gutenberg’s writing-flow tabbing expectations. - Enhances screen reader semantics for the inline banner and adds
@wordpress/domas a direct dependency.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/js/tests/ai-content-planner/helpers/handle-banner-tab-navigation.test.js | Adds unit tests for the new Tab/Shift+Tab boundary navigation helper. |
| packages/js/src/ai-content-planner/helpers/handle-banner-tab-navigation.js | Introduces a focus-management helper for Tab navigation into/out of the banner. |
| packages/js/src/ai-content-planner/components/with-inline-banner.js | Wires the capture-phase keydown listener and adds data-block to the banner wrapper. |
| packages/js/src/ai-content-planner/components/inline-banner.js | Adds role="group" and an accessible label for screen readers. |
| packages/js/package.json | Adds @wordpress/dom as an explicit dependency used by the new helper. |
JorPV
reviewed
May 5, 2026
This fix enables consistent styling when using Link from ui library, also it avoids translation with placeholder.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
The inline content planner banner was made non-block in PR #23221 (it no longer lives in Gutenberg's block data model). As a side-effect, its buttons became unreachable via keyboard Tab and Shift+Tab navigation and invisible to screen readers.
The root cause is Gutenberg's
useTabNavhook, which intercepts all Tab events inside the writing flow in the bubble phase. It only allows natural tabbing when the next tabbable element is inside the same[data-block]wrapper as the current focus. Because the banner sits outside any real block, Tab was always redirected to the sentinel divs — skipping the banner entirely.Summary
This PR can be summarized in the following changelog entry:
Relevant technical choices:
data-block="yoast-content-planner-banner"to the banner wrapper div. This satisfies Gutenberg'starget.closest('[data-block]')check for intra-banner Tab:isInSameBlockcompares.block-editor-block-list__blockancestors, both of which arenullfor elements inside the banner (since it is not inside a real block) —null === nullevaluates totrue, so Gutenberg allows Tab between the banner's own buttons without intervention.keydownlistener on the editor'sownerDocumentto handle Tab and Shift+Tab crossing the banner boundary (entering from a preceding block, leaving from the last button, entering from a following block via Shift+Tab, or leaving from the first button via Shift+Tab). Capture fires before Gutenberg's bubble-phase handler; callingpreventDefault()there triggers Gutenberg's early-return guard (if (event.defaultPrevented) return) and leaves focus management to our handler. Intra-banner navigation is intentionally not intercepted — it is already handled correctly by Gutenberg via thedata-blockattribute above.handle-banner-tab-navigation.jsso it can be unit-tested independently of the component. The helper picksfindPreviousorfindNextbased onshiftKeyand applies the same boundary-crossing XOR check for both directions.role="group"andaria-labelto theInlineBannercontainer so screen readers announce the banner as a distinct region when navigating with the virtual cursor.@wordpress/domas an explicit direct dependency inpackages/js/package.json(previously only available transitively via@wordpress/block-editor).Test instructions
Test instructions for the acceptance test before the PR gets merged
This PR can be acceptance tested by following these steps:
RTL:
Relevant test scenarios
Test instructions for QA when the code is in the RC
QA can test this PR by following these steps:
Impact check
This PR affects the following parts of the plugin, which may require extra testing:
Other environments
[shopify-seo], added test instructions for Shopify and attached theShopifylabel to this PR.[yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached theGoogle Docs Add-onlabel to this PR.Documentation
Quality assurance
grunt build:imagesand commited the results, if my PR introduces new images or SVGs.Innovation
innovationlabel.Fixes https://github.com/Yoast/reserved-tasks/issues/1195