Web Design Research

How to Use CSS Anchor Positioning for Smarter Tooltips and Overlays

Published July 23, 2026 • 11 min read • By WBS Research Team

For years, positioning a user interface element relative to another element—such as aligning a tooltip to a button, a dropdown menu to an input field, or a contextual popover to an icon—has been one of the most notoriously tedious tasks in CSS layout design. Historically, achieving this visual coupling required relying heavily on JavaScript positioning libraries like Popper.js or Floating UI, or writing complex window resize and scroll event observers to manually calculate coordinate offsets. With the introduction of CSS Anchor Positioning, this paradigm shifts entirely. This modern W3C specification enables web developers to anchor positioned elements to target anchors natively within CSS, executing layout calculations directly in the browser's rendering engine. This means positioning calculations run off the main thread, resulting in hardware-accelerated rendering performance, zero layout thrashing, and cleaner, more maintainable codebases.

CSS Anchor Positioning bridges the gap between natural layout flow and absolute positioning. Traditionally, an absolutely positioned element is positioned relative to its closest positioned ancestor. This ancestor-descendant restriction severely limited layout architectures, often forcing developers to nest complex tooltip markup inside buttons, or risk overflow clipping when parent containers utilized overflow: hidden. Anchor positioning completely decouples the DOM hierarchy from the visual connection. The anchor and the positioned element (referred to as the target) can reside in completely different branches of the document object model, yet they can align and size themselves relative to one another seamlessly.

The Historical Pain Points of Floating Elements

To fully appreciate CSS Anchor Positioning, it is helpful to examine the historical workarounds developers had to employ. When building user interfaces, common components like dropdown menus, tooltips, and contextual helper dialogs need to float above other content. They must also follow their trigger element if the page scrolls, the window is resized, or the layout shifts due to dynamic content loading.

To prevent these floating elements from being clipped by containers with overflow: hidden or contain: paint, developers had to append the floating elements directly to the root of the document body. This created a major architectural challenge: because the trigger element and the floating element were in different parts of the DOM, standard CSS absolute positioning could not connect them. JavaScript became the only viable bridge. Scripts had to query the coordinates of the trigger using getBoundingClientRect(), compute the correct offsets, apply them as inline styles to the floating element, and run this logic inside event listeners for scroll, resize, and orientation changes. This approach was highly prone to layout thrashing—a state where the browser is forced to repeatedly recalculate layouts, leading to stuttering animations and poor responsiveness, particularly on low-powered mobile devices. CSS Anchor Positioning eliminates these issues by delegating the entire coordinate tracking and layout updating lifecycle to the browser's native rendering engine.

Core Concepts: Defining Anchors and Targets

Implementing CSS Anchor Positioning requires establishing a clear relationship between the reference element (the anchor) and the positioned element (the target). This is done using CSS properties that identify the anchor and configure the target's relative positioning.

Declaring the Anchor Name

The first step in establishing an anchor-target relationship is identifying the anchor element. This is achieved using the anchor-name property. The name must be a CSS dashed-ident, which is an identifier starting with two dashes, mimicking the syntax of CSS custom properties (variables).

.anchor-element {
  anchor-name: --main-button-anchor;
}

This registers the element within its tree scope under the identifier --main-button-anchor. Once registered, its coordinates, dimensions, and bounding box queries are made available to other styled elements in the document.

Associating the Target with the Anchor

Once an anchor is registered, the target element needs to know which anchor to reference. There are two primary methods to establish this association: the implicit method via the position-anchor property, and the explicit method by passing the anchor name directly into the positioning functions.

The position-anchor property defines the default anchor for a positioned element. This is highly useful when a target only references a single anchor, as it allows you to omit the anchor name in subsequent positioning functions:

.target-element {
  position: absolute;
  position-anchor: --main-button-anchor;
}

By declaring position-anchor: --main-button-anchor;, any subsequent positioning functions like anchor() will automatically refer to --main-button-anchor if no explicit name is provided inside the function calls.

The anchor() Function in Depth

The core positioning mechanism of this specification relies on the anchor() function. This function can be assigned to inset properties (top, bottom, left, right, inset-block-start, inset-inline-start, etc.) to resolve values based on the anchor's edges.

Syntax and Arguments

The formal syntax of the function is: anchor(<anchor-name>? <anchor-side>, <length-percentage>?). Let's examine each of these arguments:

  • Anchor Name (Optional): The dashed-ident name of the anchor. If omitted, the browser falls back to the default anchor defined by the target's position-anchor property.
  • Anchor Side: Defines which edge of the anchor to align with. Valid options include physical edges (top, bottom, left, right), logical edges (start, end, self-start, self-end), or relative percentages (such as center, which is shorthand for 50%).
  • Fallback Value (Optional): A length or percentage used if the anchor is not found or is unavailable. This acts as a safeguard to prevent the target from jumping to the top-left corner of the viewport if the anchor disappears from the DOM.

Physical vs. Logical Alignment

CSS Anchor Positioning supports both physical coordinates and logical coordinates. Logical properties are highly recommended because they adapt automatically to different writing modes (like vertical text or right-to-left languages). Below is a reference table comparing physical and logical target alignments:

Physical Side Logical Side (Horizontal LTR) Logical Side (Vertical RL) Description
top block-start inline-start The upper boundary of the anchor element.
bottom block-end inline-end The lower boundary of the anchor element.
left inline-start block-start The left-hand boundary of the anchor element.
right inline-end block-end The right-hand boundary of the anchor element.
center center center The midpoint of the anchor along the specified axis.

Practical Positioning Examples

To align the top of a tooltip to the bottom of its anchor, you can write the following CSS:

.tooltip {
  position: absolute;
  position-anchor: --main-button-anchor;
  top: anchor(bottom);
  left: anchor(left);
}

This layout places the tooltip directly underneath the anchor, aligning its left edge with the left edge of the anchor. If you want to position the tooltip centered above the anchor, you can combine the anchor() function with CSS transforms or calculate the offset using logical properties:

.tooltip-centered {
  position: absolute;
  position-anchor: --main-button-anchor;
  bottom: anchor(top);
  left: anchor(center);
  transform: translateX(-50%);
}

Alternatively, you can query specific anchors explicitly without relying on the position-anchor property. This is highly useful when a target needs to align itself to multiple different anchors simultaneously. For example, a connective line or a dialog that spans between two elements:

.connective-line {
  position: absolute;
  top: anchor(--header-anchor bottom);
  bottom: anchor(--footer-anchor top);
  left: anchor(--sidebar-anchor right);
}

Dynamic Sizing with the anchor-size() Function

In addition to position offsets, targets can mirror or scale themselves based on the anchor's dimensions using the anchor-size() function. This is particularly useful for dropdown selectors, auto-completes, and combobox menus that must maintain the exact width of the input field triggering them.

The syntax matches the positioning function: anchor-size(<anchor-name>? <anchor-size-dimension>, <length-percentage>?). The size dimensions include:

  • width: The physical width of the anchor.
  • height: The physical height of the anchor.
  • block: The block-axis dimension of the anchor.
  • inline: The inline-axis dimension of the anchor.
  • self-block: The block-axis dimension of the target itself (used for self-referential calculations).
  • self-inline: The inline-axis dimension of the target itself.

For example, to force a dropdown's width to match its input anchor precisely, while setting its height to a percentage of the anchor's height:

.dropdown-menu {
  position: absolute;
  position-anchor: --input-anchor;
  top: anchor(bottom);
  left: anchor(left);
  width: anchor-size(width);
  max-height: calc(anchor-size(height) * 4);
}

This snippet guarantees that regardless of viewport resizing, font-scaling, or dynamic content changes affecting the input element, the dropdown will dynamically adjust its width inline with the input box, maintaining a proportional maximum height.

Intelligent Overflow Control: Position Try Fallbacks

One of the most complex tasks when building floating user interface elements is managing overflow. If a tooltip is positioned at the top of a button, but the button is near the top of the viewport, the tooltip will be cut off. Historically, JavaScript was required to detect this collision and "flip" the tooltip to the bottom.

CSS Anchor Positioning introduces native, hardware-accelerated layout fallbacks using the @position-try rule and position-try-options property.

Defining Custom Position Fallbacks

The @position-try at-rule allows developers to declare alternative positioning schemes that the browser can attempt if the initial position causes the target to overflow its containing block (usually the viewport).

@position-try --top-to-bottom {
  top: anchor(bottom);
  bottom: auto;
}

.tooltip-smart {
  position: absolute;
  position-anchor: --tooltip-anchor;
  bottom: anchor(top);
  left: anchor(center);
  transform: translateX(-50%);
  position-try-options: --top-to-bottom;
}

If the .tooltip-smart overflows the top boundary of the screen, the browser immediately swaps the primary styles (bottom: anchor(top);) with the styles declared in --top-to-bottom, effectively flipping the tooltip below the button.

Built-in Flipping Shorthands

For common layouts, writing custom @position-try declarations is unnecessary. CSS provides built-in flipping options such as flip-block and flip-inline:

.tooltip-simple-fallback {
  position: absolute;
  position-anchor: --tooltip-anchor;
  bottom: anchor(top);
  position-try-options: flip-block;
}

The flip-block keyword automatically switches the block-axis offsets (flipping top to bottom or vice versa) if an overflow occurs. Similarly, flip-inline flips logical inline sides (start to end, or left to right).

Managing Selection Order and Strategy

If you specify multiple potential fallback options, you can guide the browser on how to evaluate and select the best layout using the position-try-order property. Options include:

  • normal: Tries options in the order they are listed. The first option that avoids overflow is selected.
  • most-width: Selects the option that provides the largest inline space for the target.
  • most-height: Selects the option that provides the largest block space for the target.

Advanced Visibility Management with position-visibility

When working with anchored elements, a common issue occurs when the anchor scrolls out of view. In standard layouts, the target element might remain floating on the screen, detached from its anchor, causing a confusing visual state. The position-visibility property provides control over when the target should be displayed based on the visibility of its anchor.

This property accepts several values to control visibility behavior:

  • always: The target is always displayed, regardless of whether the anchor is visible or clipped by an overflow container.
  • anchors-visible: The target is only displayed when the anchor is at least partially visible within its scroll container's clipping bounds. If the anchor scrolls completely out of view, the target is hidden automatically (equivalent to display: none).
  • no-hover: The target is hidden if the user's cursor is not hovering over the anchor or target.

Using position-visibility: anchors-visible; is highly recommended for tooltips and floating menus inside scrollable areas to ensure they don't linger on the screen when their triggering elements are no longer visible.

Interactive Design Patterns and Best Practices

When applying CSS Anchor Positioning to real-world applications, several structural guidelines and design patterns should be followed to ensure robustness, accessibility, and high performance.

Ensuring Scroll and Layout Isolation

Because anchor positioning works across DOM trees, the relative position of the anchor and target is dynamically computed. However, container scroll containers can cause complications. By default, the target will follow its anchor as the page scrolls. To prevent visual stuttering, ensure that your targets have position: absolute or position: fixed, depending on whether their layout container restricts scroll propagation. If the anchor is inside a scroll container with overflow: hidden, using a fixed-position target helps ensure the popover is not clipped by the scroll container's boundary.

Accessibility (a11y) Considerations

While CSS Anchor Positioning visually connects two elements, it does not create an organic relationship for assistive technologies like screen readers. Developers must explicitly define relationships using ARIA attributes:

  • Use aria-describedby on the trigger/anchor linking to the tooltip target.
  • Use aria-haspopup and aria-expanded on trigger buttons to indicate associated menus or dialog panels.
  • Ensure focus order is preserved. If a user tabs to an anchor element, the next tab stop should logically enter the target container if it is interactive (such as a dropdown menu).

Graceful Degradation and Browser Compatibility

As CSS Anchor Positioning is a relatively new specification, it is crucial to handle unsupported browsers. Use feature queries via @supports to ensure a clean fallback layout:

/* Basic absolute positioning fallback */
.tooltip {
  position: absolute;
  top: 100%;
  left: 0;
}

/* Enhancing with anchor positioning */
@supports (anchor-name: --test) {
  .tooltip {
    position-anchor: --tooltip-anchor;
    top: anchor(bottom);
    left: anchor(left);
  }
}

For legacy browsers where visual parity is absolutely non-negotiable, a JavaScript polyfill or a light fallback library (like a minimal Popper.js setup) can be conditionally loaded to handle positioning calculations when CSS.supports('anchor-name', '--test') returns false.

Conclusion

CSS Anchor Positioning marks a major step forward for web layout tools. By migrating complex positioning, size mapping, and overflow avoidance algorithms from JavaScript execution threads to the native browser rendering pipeline, developers can create faster, cleaner, and more resilient user interfaces. From interactive tooltip popovers to highly dynamic dropdown systems, CSS Anchor Positioning allows us to build complex interfaces with clean, standard-compliant CSS, removing the need for heavy scripting dependencies.