Repository navigation
Conversation
Popover backdrops need a supported surface for custom styles. Expose a wrapper that customizes the existing underlay through every Popover export. Preserve default behavior while coordinating backdrop and content exit animations.
Custom backdrops must preserve existing popover interactions. Cover state ownership, rendering, dismissal, focus, portals, Select, ComboBox, and nested menus. Document the expected composition behavior through unit regressions.
The public wrapper must support server-rendered applications. Add a StrictMode SSR hydration case using the public Popover entry point. Verify that the dialog and custom backdrop appear after hydration.
Backdrop layering and animations need real browser coverage. Exercise entry, exit ordering, transitions, reopening, pointer hit testing, and skipped animations. Capture the browser behavior expected from a custom PopoverOverlay.
Applications need an example of a styled popover backdrop. Show a custom stacking order with independent backdrop entry and exit animations. Provide a Storybook composition for manual interaction review.
The backdrop API needs guidance alongside Popover documentation. Describe styling, sibling portal elements, state ownership, animations, and the exported props. Help applications customize modal backdrops without changing Popover positioning.
The skipped-exit test can observe DOM before React commits dismissal in Firefox. Wait up to 1000 ms for each removal and assert that focus returns to the trigger. Keep the long animation negative control effective without changing runtime behavior.
Author
|
Updated in Reviewed patch validation: Chromium 8/8, oxfmt and oxlint passed. The negative control (removing |
The CustomOverlay popover could reappear after its exit animation while the longer backdrop animation was still running. Retain the final keyframe while exiting and document fill mode for CSS keyframe exits with different durations. Keep the example hidden until both exits finish without changing runtime behavior.
Keep the padded popover inside the viewport and scroll overflowing content within its own surface. Add an actual-story browser regression for short, narrow viewports and keyboard dismissal/focus restoration.
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.
Closes #5231
Why
Issue #5231 describes positioned outside controls remaining clickable above the default transparent underlay. Applications also need a supported way to style that underlay. This follows the maintainer-proposed PopoverOverlay wrapper API, while keeping positioning and dismissal on Popover.
Summary
Add an optional PopoverOverlay wrapper so applications can style the existing popover backdrop, including its stacking order and independent entry/exit animations. An ordinary Popover keeps its default backdrop.
The wrapper shares the trigger state and passes props/ref to Popover's existing underlay. Popover retains ownership of positioning, focus, dismissal, and submenu grouping. The backdrop stays outside the existing group container, and the overlay remains mounted until both backdrop and content exit animations finish. Non-modal popovers and submenus do not gain extra backdrops.
The latest commit,
08b7ce68, also retains the CustomOverlay example's final popover exit appearance while the longer backdrop exit finishes, preventing the content from flashing back. The documentation explainsanimation-fill-mode: forwardsfor unequal keyframe durations.This change was AI-assisted. The assistant was given AGENTS.md and the contribution/testing guidance. The contributor's own review and ownership remain required before marking this draft ready.
Scope and maintainer feedback
This draft contains the wrapper implementation, its seven existing Popover export surfaces, unit/SSR/browser coverage, one Storybook example, and Popover documentation. It does not change dependencies or add a new overlay interaction hook.
Please review the wrapper API and trigger-versus-standalone state ownership, the sibling-underlay DOM arrangement, and the rule that both backdrop and content exit animations must finish before unmounting.
✅ Pull Request Checklist:
📝 Test Instructions:
Current head:
08b7ce68a760ab32a8afde702bf15fc5f1b60396.Exact-head upstream CircleCI passed its unit and SSR jobs for the default React version and React 16/17/18, aggregate lint (including repository TypeScript), package build, API/type comparison, documentation checks, and both Storybook builds.
The browser job passed 441 tests, with 78 skipped across Chromium, Firefox, and WebKit. All 8 Popover browser cases passed in each browser: independent entry animations; both exit completion orders and focus restoration; CSS transitions; reopening during exit; elevated outside-control hit testing; and skipped entry/exit animations. These are automated browser results, not manual acceptance.
The former local TypeScript and browser-startup gaps are superseded by the successful upstream jobs. The CI Storybook URL currently returns BlobNotFound. Supplemental desktop Chrome checks therefore used an isolated Parcel page that imports the unchanged component sources and the unchanged CustomOverlay story/CSS from this exact head, with React 19.3.0. This was not a run of the full Storybook manager.
Supplemental interaction results:
To reproduce, start
yarn start-parcel3, open React Aria Components/Popover/CustomOverlay, and exercise Close/Escape/outside dismissal while observing the unequal exit durations. Also compare an ordinary Popover and nested menus in a Modal. Select/ComboBox were subsequently checked in the isolated exact-source page using installed Chrome 155.0.8059.85 and Playwright 1.62.1:Two limits remain: 200% CSS
zoomdisplaced the popup in both this PR and a control using the unchanged Popover implementation; the positioning source files are identical. That simulation is not counted as a passing native browser-zoom check. ComboBox touch-emulated option tapping was inconclusive: the PR fixture reported an intercepted tap, while the unchanged control returned from the tap without selecting the value. Neither result is claimed as touch acceptance or proof of a defect introduced by this wrapper.No real-device touch/virtual-keyboard, screen-reader announcement, native desktop browser-zoom, or full Spectrum/OS theme matrix verification is claimed. The RTL, light/dark, viewport, forced-colors, and pinch results above are narrowly scoped to the supplemental page and explicitly identified emulation. Chromatic remains with maintainers. PR Title Lint is currently
action_requiredwith no job; it has not passed, even though the existing title matches the workflow's regex.🧢 Your Project:
Community contribution for #5231.