Skip to content

Commit 512c101

Browse files
committed
UI: Fix focus-trap broken by ThemeProvider's display:contents
Base UI ≥1.4.0 switched to an internal tabbable implementation that uses checkVisibility() for visibility detection. Per spec, checkVisibility() returns false for display:contents elements (no generated box). Since ThemeProvider wraps popup elements and uses display:contents, every element inside the popup was classified as non-tabbable, completely breaking focus-trap cycling. Override display:contents to display:block on the ThemeProvider div when it directly wraps a popup. The popup is position:fixed, so an extra block wrapper is layout-inert. The rule is unlayered to win over ThemeProvider's unlayered display:contents. Affects: Dialog, AlertDialog, Popover, Tooltip, Select. Made-with: Cursor
1 parent 5ceadb9 commit 512c101

5 files changed

Lines changed: 52 additions & 0 deletions

File tree

packages/ui/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Bug Fixes
6+
7+
- `Dialog`, `AlertDialog`, `Popover`, `Tooltip`, `Select`: Fix broken focus-trap caused by ThemeProvider's `display: contents` and Base UI's `checkVisibility()` ([#XXXXX](https://github.com/WordPress/gutenberg/pull/XXXXX)).
8+
59
## 0.11.0 (2026-04-15)
610

711
### Breaking Changes

packages/ui/src/dialog/style.module.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
22

3+
/*
4+
* Base UI ≥1.4.0 uses checkVisibility() to detect tabbable elements.
5+
* checkVisibility() returns false for display:contents (no generated box),
6+
* which breaks the focus-trap when ThemeProvider wraps the popup.
7+
*
8+
* This rule must be unlayered to override ThemeProvider's unlayered
9+
* display:contents.
10+
*/
11+
[data-wpds-theme-provider-id]:has(> .popup) {
12+
display: block;
13+
}
14+
315
@layer wp-ui-components {
416
.backdrop {
517
position: fixed;

packages/ui/src/popover/style.module.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
22

3+
/*
4+
* Base UI ≥1.4.0 uses checkVisibility() to detect tabbable elements.
5+
* checkVisibility() returns false for display:contents (no generated box),
6+
* which breaks the focus-trap when ThemeProvider wraps the popup.
7+
*
8+
* This rule must be unlayered to override ThemeProvider's unlayered
9+
* display:contents.
10+
*/
11+
[data-wpds-theme-provider-id]:has(> .popup) {
12+
display: block;
13+
}
14+
315
@layer wp-ui-components {
416
.positioner {
517
z-index: var(--wp-ui-popover-z-index, initial);

packages/ui/src/tooltip/style.module.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
22

3+
/*
4+
* Base UI ≥1.4.0 uses checkVisibility() to detect tabbable elements.
5+
* checkVisibility() returns false for display:contents (no generated box),
6+
* which breaks the focus-trap when ThemeProvider wraps the popup.
7+
*
8+
* This rule must be unlayered to override ThemeProvider's unlayered
9+
* display:contents.
10+
*/
11+
[data-wpds-theme-provider-id]:has(> .popup) {
12+
display: block;
13+
}
14+
315
@layer wp-ui-components {
416
.positioner {
517
z-index: var(--wp-ui-tooltip-z-index, initial);

packages/ui/src/utils/css/item-popup.module.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
22

3+
/*
4+
* Base UI ≥1.4.0 uses checkVisibility() to detect tabbable elements.
5+
* checkVisibility() returns false for display:contents (no generated box),
6+
* which breaks the focus-trap when ThemeProvider wraps the popup.
7+
*
8+
* This rule must be unlayered to override ThemeProvider's unlayered
9+
* display:contents.
10+
*/
11+
[data-wpds-theme-provider-id]:has(> .popup) {
12+
display: block;
13+
}
14+
315
@layer wp-ui-utilities {
416
.popup {
517
composes: dropdown-motion from "./dropdown-motion.module.css";

0 commit comments

Comments
 (0)