CSS position-try-fallbacks for Popover Collisions
The CSS position-try-fallbacks property, a core
component of the CSS Anchor Positioning API, provides a native way to
prevent popovers and tooltips from overflowing the screen. By
automatically testing alternative layout configurations when a popover
collides with viewport boundaries, it eliminates the historical reliance
on heavy JavaScript libraries like Popper or Floating UI. This article
explores how browser engines detect viewport collisions, the mechanics
of fallback evaluations, and how to configure automatic flipping and
custom positioning states.
The Viewport Collision Problem
Popovers and dropdown menus are anchored to specific trigger elements, such as buttons or links. If an anchor sits near the edge of the screen, displaying a popover in its default position (such as directly below or to the right of the anchor) can cause the popover to render off-screen or trigger unwanted scrollbars.
Historically, solving this required JavaScript to calculate
getBoundingClientRect() on scroll or resize events and
dynamically flip the element. With CSS Anchor Positioning, the browser
performs these spatial checks natively on the compositor thread.
How
position-try-fallbacks Works
The position-try-fallbacks property defines a
prioritized list of alternative position states that the browser should
attempt if the default position causes the popover to overflow its
containing block or the visible viewport.
When rendering the anchored popover, the browser follows a structured resolution process:
- Default Position Check: The browser first attempts
to render the popover using its default positioning styles (e.g.,
placing it directly below the anchor via
position-anchorand standard inset properties). - Collision Detection: The browser calculates the popover's geometry relative to the viewport. If any part of the element intersects the boundaries or overflows the available space, a collision is triggered.
- Sequential Evaluation: The browser evaluates each
fallback option listed in
position-try-fallbacksin order from left to right. - Application: The first fallback that allows the popover to fit entirely within the viewport without clipping is applied. If none of the fallbacks fit perfectly, the browser selects the one that results in the least amount of overflow or falls back to the original placement, depending on standard constraints.
Using Predefined Flip Keywords
For common use cases, CSS provides predefined keywords that automatically mirror the element's position across various axes without requiring custom rules:
flip-block: Inverts the position along the block axis (swappingtopandbottom).flip-inline: Inverts the position along the inline axis (swappingleftandright, accounting for writing modes).flip-start: Mirrors properties toward the start of the opposite axis.
.anchor-button {
anchor-name: --menu-button;
}
.popover-menu {
position: fixed;
position-anchor: --menu-button;
top: anchor(bottom);
left: anchor(start);
margin-top: 8px;
/* Try flipping to the top if bottom overflows */
position-try-fallbacks: flip-block;
}If the button is positioned at the bottom of the viewport, the
browser detects the collision and automatically recalculates the rules
as bottom: anchor(top), rendering the menu above the button
instead.
Defining Custom
Fallbacks with @position-try
When layouts require more than simple axis flipping, custom fallback
positions can be defined using the @position-try at-rule.
This allows precise control over properties such as margins, insets, and
transforms for different collision scenarios.
/* Define custom fallback states */
@position-try --top {
top: auto;
bottom: anchor(top);
margin-bottom: 8px;
}
@position-try --right {
top: anchor(top);
bottom: auto;
left: anchor(end);
margin-left: 8px;
}
/* Apply fallbacks in order of preference */
.popover-menu {
position: fixed;
position-anchor: --menu-button;
top: anchor(bottom);
left: anchor(start);
margin-top: 8px;
position-try-fallbacks: --top, --right, flip-inline;
}In this setup, the browser first attempts to render the popover below
the anchor. If it collides with the bottom edge, it tries the
--top rule. If the top edge also causes an overflow, it
attempts the --right rule, and finally attempts
flip-inline before choosing the best fit.
Performance and Behavior Benefits
Handling collisions natively through
position-try-fallbacks offers two key technical
advantages:
- Zero Layout Thrashing: Because the browser engine handles geometry evaluation internally, there are no forced synchronous layouts caused by reading DOM properties via JavaScript.
- Scroll Synchronization: Fallback adjustments occur seamlessly during scroll and resize events, preventing the visible position "jumping" or lagging often observed with JavaScript-driven positioning solutions.