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:

  1. 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-anchor and standard inset properties).
  2. 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.
  3. Sequential Evaluation: The browser evaluates each fallback option listed in position-try-fallbacks in order from left to right.
  4. 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 (swapping top and bottom).
  • flip-inline: Inverts the position along the inline axis (swapping left and right, 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.