Skip to content

Add x-anchor.noflip modifier#4382

Open
aguingand wants to merge 3 commits intoalpinejs:mainfrom
aguingand:anchor-noflip
Open

Add x-anchor.noflip modifier#4382
aguingand wants to merge 3 commits intoalpinejs:mainfrom
aguingand:anchor-noflip

Conversation

@aguingand
Copy link

This PR adds ability to disable auto flipping for anchor. In some cases you want to force the anchored element to stay at his position and not flipping to the top if there is no enough room below. For example with large menus or popovers teleported to body.

Usage :

<div x-anchor.noflip>
</div>

<!-- with position -->
<div x-anchor.bottom-start.noflip>
</div>

@ekwoka
Copy link
Contributor

ekwoka commented Sep 30, 2024

Is there a way to add a test for this?

@aguingand
Copy link
Author

@ekwoka I see no easy way to test this, the logic is almost entirely in @floating-ui/dom. Current e2e test is minimalist.

calebporzio and others added 2 commits February 8, 2026 21:49
Adds a Cypress test that verifies .noflip prevents automatic flipping
by positioning an anchor at the bottom of the viewport with .bottom.noflip
and asserting the element stays below the reference button.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@calebporzio
Copy link
Collaborator

PR Review: #4382 — Add x-anchor.noflip modifier

Type: Feature
Verdict: Merge

What's happening (plain English)

Right now, x-anchor always includes floating-ui's flip() middleware. This means if you anchor a dropdown below a button but there's not enough viewport space below, floating-ui automatically flips it to the top. That's usually what you want — but not always.

For large menus or teleported popovers, flipping can be disorienting or cause layout issues. This PR adds a .noflip modifier so you can opt out:

<!-- Before: always flips when space runs out -->
<div x-anchor.bottom="$refs.button">...</div>

<!-- After: stays at bottom placement no matter what -->
<div x-anchor.bottom.noflip="$refs.button">...</div>

The implementation is 3 lines:

  1. Parse noflip from modifiers → allowFlip = !modifiers.includes('noflip')
  2. Conditionally include flip middleware → allowFlip && flip() (floating-ui's types explicitly accept false in the middleware array)
  3. Return allowFlip from getOptions

Other approaches considered

  1. Remove flip entirely and let users add it back — Wrong direction. Flip-by-default is the right UX for most cases.
  2. Use .no-flip (hyphenated) instead of .noflip — The focus plugin already uses .noscroll, .noreturn, .noautofocus — all unhyphenated. .noflip follows this precedent correctly.
  3. Filter the middleware array with .filter(Boolean) — Marginally more defensive, but floating-ui's TypeScript types explicitly allow Array<Middleware | null | undefined | false>, so the allowFlip && flip() pattern is idiomatic and supported.

Changes Made

  • Added a Cypress test that positions a button at the bottom of the viewport with .bottom.noflip, then asserts the anchored element stays below the button (doesn't flip to top). Verified the test fails on main and passes with the fix — it properly targets the actual behavior change.

Test Results

  • anchor.spec.js: 2/2 passing (both "can anchor an element" and "noflip modifier prevents automatic flipping")
  • No CI checks configured on contributor's branch

Code Review

  • packages/anchor/src/index.js:30allowFlip && flip() is clean. floating-ui handles falsy middleware entries by design.
  • packages/anchor/src/index.js:75! modifiers.includes('noflip') follows the same pattern as modifiers.includes('no-style') on line 74.
  • Style is correct: let not const, no semicolons.
  • Docs are clear and include both a code example and a live demo verbatim block.

Security

No security concerns identified. This only affects which floating-ui middleware is included — no expression evaluation, no user input handling.

Verdict

This is a small, clean, well-scoped feature addition. The use case is real (large menus/popovers where flipping is undesirable), the implementation is minimal (3 lines), the naming follows established precedent (.noscroll, .noreturn, .noautofocus), and floating-ui explicitly supports the false-in-middleware pattern. The only thing missing was a test — I've added one and pushed it. Recommend merge.


Reviewed by Claude

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants