Skip to content

[CSS Anchor Positioning] Ensure AnchoredOverlay remains in the viewport#7802

Open
TylerJDev wants to merge 31 commits intomainfrom
tylerjdev/fix-css-anchor-position-overflow
Open

[CSS Anchor Positioning] Ensure AnchoredOverlay remains in the viewport#7802
TylerJDev wants to merge 31 commits intomainfrom
tylerjdev/fix-css-anchor-position-overflow

Conversation

@TylerJDev
Copy link
Copy Markdown
Member

@TylerJDev TylerJDev commented May 1, 2026

Closes https://github.com/github/primer/issues/6511

Adds behavior to handle overflow in y-axis when CSS anchor positioning does not have enough space to reposition. We handled this before in #7725, but this only applied to the x-axis (left | right).

This PR also implements behavior to ensure that the overlay fits in the viewport if its height expands past the viewport height.

Changelog

Changed

  • Add functionality to handle overflowing overlays in y-axis.
  • Ensure overlay remains in the viewport, even if initial height is larger than the viewport's set height.

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 1, 2026

🦋 Changeset detected

Latest commit: 00afccf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@TylerJDev TylerJDev added the Canary Release Apply this label when you want CI to create a canary release of the current PR label May 1, 2026
@github-actions github-actions Bot added the staff Author is a staff member label May 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

⚠️ Action required

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Check the integration testing docs for step-by-step instructions. Or, apply the integration-tests: skipped manually label to skip these checks.

To publish a canary release for integration testing, apply the Canary Release label to this PR.

@github-actions github-actions Bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label May 1, 2026
@github-actions github-actions Bot temporarily deployed to storybook-preview-7802 May 1, 2026 19:21 Inactive
@github-actions github-actions Bot temporarily deployed to storybook-preview-7802 May 4, 2026 18:03 Inactive
@TylerJDev TylerJDev added Canary Release Apply this label when you want CI to create a canary release of the current PR and removed Canary Release Apply this label when you want CI to create a canary release of the current PR labels May 4, 2026
@TylerJDev TylerJDev changed the title Tylerjdev/fix css anchor position overflow [CSS anchor positioning] Ensure AnchoredOverlay remains in the viewport May 4, 2026
Comment on lines +449 to +451
// Suggest a flip when the overlay is currently overflowing both axes:
// prefer the side of the anchor with enough room, otherwise fall back to
// outside-bottom and let the offsets keep it inside the viewport.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The summary of the changes here are:

  • We try to account for content overflow both horizontally and vertically. CSS anchor positioning will handle this automatically IF there's at least one fallback with enough space to fit the overlay, but if not, it'll default the the default position, which is where this implementation comes in.
  • This should only kick in only when CSS anchor positioning isn't able to correctly position the overlay (lack of space).
  • This logic only runs on opening of the overlay, so it won't affect performance.

Comment on lines +272 to +274
// Manage `anchor-name` on the anchor independently of `open`/`width` so a
// parent re-render that re-runs the positioning effect below doesn't
// briefly flicker the anchor link off and back on.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The comment explains it, but we add an additional useEffect mainly to handle when we add/remove the anchor-overlay CSS anchor positioning link.

It helps us ensure that unrelated dependency changes don't accidentally add or remove position-anchor or anchor-name.

Comment on lines +57 to +61
/* Falls back to `anchor(top)` when JS hasn't overridden it. JS sets the
override when the overlay's bottom would overflow the viewport so we
can lift it up to keep the bottom edge on screen, mirroring the JS
anchored-positioning code path. */
top: var(--anchored-overlay-top-override, anchor(top));
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is some new logic to handle overflows in the y-axis. Mainly to fix https://github.com/github/primer/issues/6648 gracefully.

@TylerJDev TylerJDev added Canary Release Apply this label when you want CI to create a canary release of the current PR and removed Canary Release Apply this label when you want CI to create a canary release of the current PR labels May 4, 2026
@github-actions github-actions Bot temporarily deployed to storybook-preview-7802 May 4, 2026 21:55 Inactive
@primer-integration
Copy link
Copy Markdown

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/19900

@TylerJDev TylerJDev marked this pull request as ready for review May 4, 2026 22:03
@TylerJDev TylerJDev requested a review from a team as a code owner May 4, 2026 22:03
@TylerJDev TylerJDev requested review from Copilot and siddharthkp May 4, 2026 22:03
@primer-integration
Copy link
Copy Markdown

Integration test results from github/github-ui:

Passed  CI   Passed
Running  VRT   Running
Passed  Projects   Passed

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the CSS-anchor-positioning path for AnchoredOverlay so overlays and nested ActionMenu submenus stay within the viewport more reliably when there is limited horizontal and vertical space. It fits into the existing feature-flagged native anchor-positioning rollout in @primer/react.

Changes:

  • Split anchor-name management from the positioning effect and added new JS fallback logic for side suggestions and viewport clamping in AnchoredOverlay.
  • Added CSS support for a JS-controlled top override on left/right anchored overlays.
  • Added a new ActionMenu dev story to manually exercise large right-aligned submenus, plus a patch changeset entry.
Show a summary per file
File Description
packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx Adds native anchor-positioning fallback logic for horizontal alignment, suggested side changes, and vertical clamping.
packages/react/src/AnchoredOverlay/AnchoredOverlay.module.css Wires left/right anchored overlays to a JS-provided top override custom property.
packages/react/src/ActionMenu/ActionMenu.dev.stories.tsx Adds a dev-only preview scenario for large nested submenus near the viewport edge.
.changeset/social-deer-laugh.md Records the patch release note for the viewport-fitting behavior change.

Copilot's findings

Comments suppressed due to low confidence (2)

packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx:318

  • This only corrects bottom overflow. If a left/right overlay gets flipped above its anchor and ends up with a negative top but no bottom overflow, we now leave it partially off-screen at the top of the viewport. The new viewport-clamping logic needs to handle top overflow as well.
        // Set y-axis offset to prevent overflow if needed.
        const settledRect = currentOverlay.getBoundingClientRect()
        const overflowBottom = settledRect.bottom - window.innerHeight
        if (overflowBottom > 0) {
          const clampedTop = Math.max(0, settledRect.top - overflowBottom - 8)
          currentOverlay.style.setProperty('--anchored-overlay-top-override', `${clampedTop}px`)
        } else {
          currentOverlay.style.removeProperty('--anchored-overlay-top-override')

packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx:305

  • If a previous measurement set data-side to a suggested fallback, a later run that no longer needs a flip never restores the requested side. Because this is a direct DOM mutation, React will not reconcile it back to the prop value on a rerender with the same props, so the overlay can stay stuck on the old side after width/content changes while it is open.
        if (result.suggestedSide) {
          currentOverlay.setAttribute('data-side', result.suggestedSide)
  • Files reviewed: 4/4 changed files
  • Comments generated: 3

Comment thread packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
Comment on lines +300 to +318
const fallbackWidth = width ? parseInt(widthMap[width]) : parseInt(widthMap.small)
const result = getDefaultPosition(anchorElement, currentOverlay, fallbackWidth)

currentOverlay.setAttribute('data-align', result.horizontal)
if (result.suggestedSide) {
currentOverlay.setAttribute('data-side', result.suggestedSide)
}

// Apply offset only when viewport is too narrow
const offset = result.horizontal === 'left' ? result.leftOffset : result.rightOffset
currentOverlay.style.setProperty(`--anchored-overlay-anchor-offset-${result.horizontal}`, `${offset || 0}px`)

// Set y-axis offset to prevent overflow if needed.
const settledRect = currentOverlay.getBoundingClientRect()
const overflowBottom = settledRect.bottom - window.innerHeight
if (overflowBottom > 0) {
const clampedTop = Math.max(0, settledRect.top - overflowBottom - 8)
currentOverlay.style.setProperty('--anchored-overlay-top-override', `${clampedTop}px`)
} else {
currentOverlay.style.removeProperty('--anchored-overlay-top-override')
Comment thread packages/react/src/ActionMenu/ActionMenu.dev.stories.tsx
@TylerJDev TylerJDev changed the title [CSS anchor positioning] Ensure AnchoredOverlay remains in the viewport [CSS Anchor Positioning] Ensure AnchoredOverlay remains in the viewport May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary Release Apply this label when you want CI to create a canary release of the current PR integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm staff Author is a staff member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants