CSSAWWWARDS
+ Submit tool
layoutsIntermediate · Updated July 2026

CSS Anchor Positioning: Complete Guide to Native Popover & Tooltip Layouts (2026)

CSS Anchor Positioning is the most significant layout addition to CSS in years. It lets any positioned element tether itself to any other element on the page — enabling tooltips, dropdowns, popovers, and context menus that automatically flip when they hit the viewport edge. No JavaScript required.

By Adil Badshah21 July 202615 min read
CSS Anchor Positioning Complete Guide 2026

What Is CSS Anchor Positioning?

Quick Answer

CSS Anchor Positioning is a CSS feature that lets a position: absolute or position: fixed element bind itself to any other element on the page (the “anchor”) and align its edges to the anchor’s edges — using the anchor() function in inset properties. Combined with @position-try, it automatically flips direction when the preferred position overflows the viewport. This replaces JavaScript positioning libraries for most tooltip, dropdown, and popover use cases.

Before CSS Anchor Positioning, creating a tooltip that appears above a button, flips below it when near the top of the screen, and stays centred horizontally required either a JavaScript library (Floating UI, Popper.js, Tippy.js) or several hundred lines of manual calculation with getBoundingClientRect(). The CSS spec had no native primitive for “position this element relative to that element” beyond the limited position: relative/absolute parent–child relationship.

CSS Anchor Positioning changes this entirely. It introduces a cross-element tethering mechanism: register any element as an anchor with anchor-name, then any positioned element anywhere in the document can bind to it via position-anchor and use the anchor() function to align to its edges. The anchor and the positioned element do not need to share a parent. They do not need to be siblings. The only requirement is that the anchor element is rendered before or alongside the positioned element in the document.

The spec shipped in Chrome 125 (May 2024), Firefox 131 (October 2024), and Safari 18.2 (December 2024). As of July 2026, global browser coverage sits at approximately 88% — fully viable for production with a progressive enhancement fallback.

Key concept: CSS Anchor Positioning works across the entire document, not just within a parent–child relationship. The anchor element and the tooltip/dropdown can be completely unrelated in the DOM hierarchy. This solves the long-standing problem of tooltips clipping inside overflow: hidden containers.

anchor-name and position-anchor — Registering Anchors

Quick Answer

anchor-name: --my-anchor on an element registers it as an anchor with that name. position-anchor: --my-anchor on a positioned element binds it to that anchor. The name must be a CSS dashed-ident (start with --).

The two foundational properties are anchor-name and position-anchor. Think of them as a named connection between two elements — one broadcasts its identity, the other subscribes to it.

/* 1. Register the anchor */.trigger {  anchor-name: --my-anchor;} /* 2. Attach and position the tooltip */.tooltip {  position: absolute;  position-anchor: --my-anchor;   /* Place bottom of tooltip at top of anchor */  bottom: anchor(top);   /* Horizontally centre over the anchor */  left: anchor(center);  translate: -50% 0;   margin-bottom: 8px;}

The name is a CSS custom property ident: it starts with -- and can be any descriptive string. Dashes are allowed. Multiple elements can reference the same anchor, and the same element can be referenced by multiple positioned children.

Implicit anchors

When working with the HTML popover attribute, you can set anchor as an HTML attribute on the popover element pointing to the ID of its trigger button. The browser then implicitly sets the CSS relationship between them, meaning you don’t need to write anchor-name or position-anchor in CSS — the HTML attribute establishes the connection. This is the most ergonomic approach for popover-based tooltips and menus.

Anchor scope

Anchor names are scoped to the nearest containing block that creates a new stacking context. In practice this means anchor names are globally available across a document unless you have overlapping names. Namespacing your anchor names descriptively (e.g., --header-nav-dropdown) prevents collisions in large component trees.

The anchor() Function — Positioning to an Edge

Quick Answer

anchor(anchor-name, edge) returns the pixel coordinate of a specific edge of the named anchor element. You use it inside top, right, bottom, or left on the positioned element. For example, bottom: anchor(top) aligns the bottom of your element to the top of the anchor — placing it directly above.

The anchor() function is where the actual positioning happens. It accepts an anchor name (optional if you’ve set position-anchor) and an edge keyword, then returns the computed length of that edge in the coordinate system of the containing block.

/* All anchor() edge values */.popup {  position: absolute;  position-anchor: --btn;   /* Vertical edges */  top:    anchor(top);     /* aligns to anchor's top edge    */  bottom: anchor(bottom);  /* aligns to anchor's bottom edge */  top:    anchor(bottom);  /* stacks below the anchor        */  bottom: anchor(top);     /* stacks above the anchor        */  top:    anchor(center);  /* centres vertically             */   /* Horizontal edges */  left:   anchor(left);    /* aligns to anchor's left edge   */  right:  anchor(right);   /* aligns to anchor's right edge  */  left:   anchor(right);   /* stacks to the right            */  right:  anchor(left);    /* stacks to the left             */  left:   anchor(center);  /* centres horizontally           */}

The edge keywords are: top, bottom, left, right, center, start, end, self-start, and self-end. The start/end keywords are writing-mode aware — they map to left/right in a left-to-right document and flip in right-to-left, making your tooltips automatically correct for Arabic, Hebrew, and other RTL languages.

Combining anchor() with calc()

Since anchor() returns a length value, you can use it inside calc(). This is useful when you want to offset a positioned element slightly from an edge:

/* Place the tooltip 16px above the anchor instead of flush */.tooltip {  bottom: calc(anchor(top) + 16px);  left: anchor(center);  translate: -50% 0;}

You can also cross-reference multiple anchors in one element by passing different anchor names to different anchor() calls — though this is an advanced pattern useful for elements that need to span between two anchors (e.g., a tooltip arrow that must align with two different reference points).

@position-try — Automatic Flip Fallbacks

Quick Answer

@position-try --name { ... } defines an alternate set of positioning declarations. Add position-try-fallbacks: --name to the positioned element. If the primary position overflows the viewport, the browser automatically tries the fallback — repositioning the tooltip below, or flipping a dropdown above — with no JavaScript.

The ability to automatically flip a tooltip or dropdown when it runs out of space is one of the main reasons developers reach for JavaScript libraries. @position-try brings this capability natively to CSS.

/* Define an alternate position to try when the preferred overflows */@position-try --flip-below {  bottom: unset;  top: anchor(bottom);  margin-bottom: 0;  margin-top: 8px;} .tooltip {  position: absolute;  position-anchor: --my-anchor;   /* Primary: above the anchor */  bottom: anchor(top);  left: anchor(center);  translate: -50% 0;  margin-bottom: 8px;   /* Fallback: flip below if no room above */  position-try-fallbacks: --flip-below;}

The browser tries the primary position first. If the positioned element overflows the viewport (or a containing scroll container), it tries each fallback in the position-try-fallbacks list in order, picking the first one that fits. If no fallback fits, it reverts to the primary position.

Built-in flip keywords

Rather than defining @position-try rules manually, you can use the built-in flip-block, flip-inline, and flip-start keywords in position-try-fallbacks:

/* Built-in keyword: flip on the block (vertical) axis */.tooltip {  position-try-fallbacks: flip-block;  /* Automatically tries the mirrored vertical position */} /* Multiple fallbacks in priority order */.dropdown {  position-try-fallbacks: flip-block, flip-inline, flip-start;}

flip-block mirrors the element across the block axis (flips top to bottom in a horizontal writing mode). flip-inline mirrors across the inline axis (flips left to right). flip-start swaps both. These keywords cover the majority of real-world cases without any @position-try boilerplate.

position-try-order

By default the browser tries fallbacks in the order you list them. You can override this with position-try-order: most-width or position-try-order: most-height, which instructs the browser to pick the fallback that provides the most available width or height for the element, regardless of list order. This is useful for dropdowns in constrained UI regions.

anchor-size() — Match the Anchor's Dimensions

Quick Answer

anchor-size(width) and anchor-size(height)return the corresponding dimension of the anchor element. Use them in sizing properties on the positioned element to make a dropdown exactly as wide as its trigger button, or to constrain a tooltip’s maximum size relative to the anchor.

A common requirement for select-style dropdowns is that the dropdown panel should match the width of the trigger button exactly. Previously this required JavaScript to read offsetWidth and apply it dynamically. With anchor-size() you express this entirely in CSS:

/* anchor-size() matches the positioned element to the anchor's dimensions */.dropdown {  position: absolute;  position-anchor: --select-btn;   top: anchor(bottom);  left: anchor(left);  margin-top: 4px;   /* Match exact width of the trigger button */  width: anchor-size(width);   /* Or cap at double the anchor's height */  max-height: calc(anchor-size(height) * 6);}

The anchor-size() function accepts width, height, inline, block, self-inline, and self-block keywords (the last two are relative to the positioned element’s own writing mode). You can use it inside calc() — for instance, max-height: calc(anchor-size(height) * 5)limits a dropdown to five times the anchor’s height.

Build a Tooltip Without JavaScript

Here is a complete, production-ready tooltip implementation using CSS Anchor Positioning. It shows on hover and focus, appears above the trigger by default, flips below when near the top of the viewport, and uses no JavaScript at all.

/* ── Complete tooltip example ── */ /* Anchor */.btn-tooltip {  anchor-name: --tooltip-anchor;  position: relative;  /* not required, just for context */} /* Tooltip */.tooltip {  position: absolute;  position-anchor: --tooltip-anchor;   bottom: anchor(top);  left: anchor(center);  translate: -50% 0;  margin-bottom: 10px;   /* Flip below when near top of viewport */  @position-try --flip-below {    bottom: unset;    top: anchor(bottom);    margin-bottom: 0;    margin-top: 10px;  }  position-try-fallbacks: --flip-below;   /* Appearance */  background: #111;  color: #fff;  font-size: 12px;  padding: 5px 10px;  border-radius: 4px;  white-space: nowrap;   /* Hidden by default */  opacity: 0;  pointer-events: none;  transition: opacity 0.15s;} /* Show on hover */.btn-tooltip:hover + .tooltip,.btn-tooltip:focus-visible + .tooltip {  opacity: 1;  pointer-events: auto;}

A few details worth noting in this implementation. The pointer-events: none on the hidden state prevents the tooltip from interfering with mouse events when invisible. The transition: opacity gives a smooth fade — if you want a custom easing curve or a slide-in effect, the CSS Transition Generator lets you build and preview the exact transition shorthand before you copy it in. The adjacent sibling selector (.btn-tooltip:hover + .tooltip) requires the button and tooltip to be siblings in the HTML — a minor structural constraint that is usually easy to satisfy.

Accessibility note: For screen readers, add role="tooltip" and aria-describedby on the trigger pointing to the tooltip’s ID. The CSS opacity approach is visually-only — the tooltip content is still accessible in the DOM. Use the HTML popover attribute (section 9) if you need full keyboard-accessible dismiss behaviour.

Tooltip arrow

Adding a directional arrow requires a pseudo-element that also uses anchor(). Here’s the pattern:

/* Arrow pseudo-element that flips with the tooltip */.tooltip::after {  content: "";  position: absolute;   /* Arrow pointing down (tooltip is above anchor) */  bottom: -6px;  left: 50%;  translate: -50% 0;  border: 6px solid transparent;  border-top-color: #111;  border-bottom: none;} /* When @position-try flips the tooltip below, flip the arrow too */@position-try --flip-below {  bottom: unset;  top: anchor(bottom);  margin-top: 10px;} /* Reverse the arrow direction in the flipped state */@position-try --flip-below {  .tooltip::after {    bottom: unset;    top: -6px;    border-top-color: transparent;    border-bottom: 6px solid #111;  }}

Build a Dropdown Menu With CSS Only

Navigation dropdowns are one of the most common UI patterns that historically required JavaScript to position correctly. With CSS Anchor Positioning, the layout is entirely declarative.

/* ── Dropdown menu with anchor positioning ── */ /* Trigger */.nav-btn {  anchor-name: --nav-dropdown;} /* Dropdown panel */.dropdown-menu {  position: absolute;  position-anchor: --nav-dropdown;   /* Align left edge to trigger's left, top to trigger's bottom */  top:  anchor(bottom);  left: anchor(left);  margin-top: 4px;   /* Match trigger width (or set min-width) */  min-width: anchor-size(width);   /* Flip above when near bottom of viewport */  @position-try --flip-above {    top: unset;    bottom: anchor(top);    margin-top: 0;    margin-bottom: 4px;  }  position-try-fallbacks: --flip-above;   /* Styles */  background: var(--color-surface);  border: 1px solid var(--color-border);  border-radius: 6px;  box-shadow: 0 8px 24px rgba(0,0,0,0.12);  padding: 4px;  list-style: none;   /* Hide by default */  display: none;} /* Show when button is aria-expanded */.nav-btn[aria-expanded="true"] + .dropdown-menu {  display: block;}

The min-width: anchor-size(width) line ensures the dropdown is never narrower than the trigger — important for single-word trigger buttons where the dropdown items are longer. If you need to style the trigger button itself, the CSS Button Generator produces clean, copy-paste button CSS with hover and focus states included. The aria-expanded toggle still requires a small amount of JavaScript (or the HTML popover attribute — see the next section) to add/remove the attribute, but all positioning logic is handled in CSS.

One thing to watch: dropdown menus create a new stacking context that can cause z-index conflicts with other overlapping elements. The CSS Z-Index Visualizer helps you map and debug stacking layers across your UI before they become a production problem.

Multi-level dropdowns

Each menu item can itself be an anchor, with a nested submenu positioned relative to it. Nest @position-try rules accordingly, using flip-inline to switch the submenu from opening right to opening left when near the viewport edge.

/* Nested submenu opens to the right of the parent item */.has-submenu {  anchor-name: --submenu-trigger;} .submenu {  position: absolute;  position-anchor: --submenu-trigger;  top: anchor(top);  left: anchor(right);  margin-left: 2px;  position-try-fallbacks: flip-inline;}

Build a Context Menu / Right-Click Popover

Context menus need to appear near the cursor, which requires JavaScript for the cursor position. However, if your context menu should appear anchored to the element that was right-clicked (rather than the exact cursor position), CSS Anchor Positioning handles it completely:

/* ── Right-click context menu ── */ /* Context menu positioned relative to the clicked element */.card {  anchor-name: --card-anchor;  position: relative;} .context-menu {  position: absolute;  position-anchor: --card-anchor;   /* Default: top-right of the card */  top:  anchor(top);  left: anchor(right);  margin-left: 4px;   /* Try left if no room on right */  @position-try --flip-left {    top:  anchor(top);    left: unset;    right: anchor(left);    margin-left: 0;    margin-right: 4px;  }   /* Try below-right if not enough vertical space */  @position-try --flip-below {    top:  anchor(bottom);    left: anchor(right);    margin-left: 4px;    margin-top: 4px;  }   position-try-fallbacks: --flip-left, --flip-below;}

This pattern uses multiple @position-try fallbacks listed in priority order. The browser tries them in sequence: right of the card first, then left of the card, then below-right. This ensures the menu is always visible regardless of where in the viewport the card sits.

Using the HTML popover Attribute

Quick Answer

The HTML popoverattribute handles show/hide, focus management, and “light dismiss” (click outside to close) with zero JavaScript. Combined with CSS Anchor Positioning for placement, you get fully accessible popovers with no JavaScript at all.

The HTML popover attribute is a browser-native popover mechanism that works synergistically with CSS Anchor Positioning. Add popover to any element, and it is hidden by default. Add popovertarget="element-id" to a button, and clicking the button shows/hides the popover automatically — including moving focus into it for keyboard accessibility and closing it when the user clicks outside.

<!-- HTML popover attribute + CSS anchor positioning --><button  id="menu-btn"  popovertarget="menu-popup"  style="anchor-name: --menu-btn">  Open Menu</button> <ul  id="menu-popup"  popover  style="    position: absolute;    position-anchor: --menu-btn;    top: anchor(bottom);    left: anchor(left);    min-width: anchor-size(width);    margin-top: 4px;  ">  <li>Item 1</li>  <li>Item 2</li>  <li>Item 3</li></ul> <!-- No JavaScript needed — the popover attribute handles     show/hide, focus management, and light-dismiss -->

The :popover-open pseudo-class lets you style the popover when it is open. You can use it to animate the entrance with @starting-style (a related new CSS feature that defines the before-open state for transitions):

/* Animate popover entrance */[popover] {  opacity: 0;  transform: translateY(-4px);  transition: opacity 0.2s, transform 0.2s,              display 0.2s allow-discrete,              overlay 0.2s allow-discrete;} [popover]:popover-open {  opacity: 1;  transform: translateY(0);} /* Starting state for the opening transition */@starting-style {  [popover]:popover-open {    opacity: 0;    transform: translateY(-4px);  }}

The combination of popover + CSS Anchor Positioning + @starting-style gives you an animated, keyboard-accessible, light-dismiss popover with zero lines of JavaScript. The entrance easing matters here — a linear fade feels flat while a well-tuned ease-out feels polished. Use the Cubic Bezier Builder to craft and preview the exact curve before plugging it into your transition property. This is the recommended approach for any new tooltip or dropdown in 2026.

CSS Anchor Positioning vs JavaScript (Floating UI)

Floating UI (formerly Popper.js) is the dominant JavaScript library for tooltip and popover positioning. Here is an honest comparison of when to use each approach:

/* ── Before: JavaScript-based positioning (Floating UI) ── */import { computePosition, flip, shift } from "@floating-ui/dom"; const btn = document.getElementById("btn");const tooltip = document.getElementById("tooltip"); computePosition(btn, tooltip, {  placement: "top",  middleware: [flip(), shift({ padding: 8 })],}).then(({ x, y }) => {  Object.assign(tooltip.style, {    left: x + "px",    top: y + "px",  });}); /* ── After: Pure CSS Anchor Positioning ── *//* No JavaScript. No library. No event listeners. */.btn        { anchor-name: --btn; }.tooltip    {  position: absolute;  position-anchor: --btn;  bottom: anchor(top);  left: anchor(center);  translate: -50% 0;  margin-bottom: 8px;  position-try-fallbacks: --flip-below;}@position-try --flip-below {  bottom: unset;  top: anchor(bottom);  margin-bottom: 0;  margin-top: 8px;}
FeatureCSS Anchor PositioningFloating UI
Basic positioning✓ Native CSS✓ JS runtime
Auto flip / overflow detection✓ @position-try✓ flip() middleware
Match anchor size✓ anchor-size()✓ JS measurement
Arrow pointing to anchor✓ Pseudo-element + anchor()✓ arrow() middleware
Virtual anchors (cursor-based)✗ Requires JS for cursor coords✓ virtualElement API
Auto-reposition on scroll✓ Browser-native✓ autoUpdate()
Overflow container clipping fix✓ position: fixed on tooltip✓ strategy: 'fixed'
Bundle size0 KB (CSS only)~12 KB minified+gzip
Legacy browser support88% (mid-2026)~99%

The verdict: use CSS Anchor Positioning for new projects targeting modern browsers, and use @supports for a Floating UI fallback if you need to support older environments. For cursor-positioned context menus or any scenario where the anchor is not a DOM element, you still need JavaScript — but for the vast majority of tooltip and dropdown use cases, CSS is now the correct first choice.

Browser Support & Progressive Enhancement

BrowserMin versionRelease dateNotes
Chrome / Edge125May 2024Full support including @position-try
Firefox131Oct 2024Full support
Safari18.2Dec 2024Full support
Global (mid-2026)~88%Production-ready with fallback

The recommended progressive enhancement pattern uses @supports (anchor-name: --test)to gate the anchor positioning rules, with a traditional absolute-positioned fallback for browsers that don’t support the feature:

/* Progressive enhancement with @supports */@supports (anchor-name: --test) {  .trigger      { anchor-name: --trigger; }  .tooltip      {    position: absolute;    position-anchor: --trigger;    bottom: anchor(top);    left: anchor(center);    translate: -50% 0;    margin-bottom: 8px;    position-try-fallbacks: --flip-below;  }  @position-try --flip-below {    bottom: unset;    top: anchor(bottom);    margin-top: 8px;  }} /* Fallback for unsupported browsers */@supports not (anchor-name: --test) {  .tooltip {    position: absolute;    bottom: 100%;    left: 50%;    transform: translateX(-50%);    margin-bottom: 8px;  }}

For most new projects in 2026, you can write anchor positioning CSS directly without the @supports wrapper. The 12% of browsers without support will receive the default browser rendering of the positioned element — typically appearing at position 0,0 of the containing block. Since tooltips and dropdowns are typically hidden by default (opacity: 0 or display: none), unsupported browsers simply won’t see the popup — a reasonable graceful degradation. If your project also uses native page transitions, the CSS View Transitions Generator pairs naturally with anchor-positioned elements to create seamless between-page animations.

Frequently Asked Questions

What is CSS Anchor Positioning?

CSS Anchor Positioning is a CSS feature that lets a positioned element (position: absolute or fixed) tether itself to any other element on the page and align its edges using the anchor() function. It enables tooltips, dropdowns, and popovers that automatically flip direction when they overflow the viewport — without JavaScript.

What browsers support CSS Anchor Positioning in 2026?

Chrome 125+, Edge 125+, Firefox 131+, and Safari 18.2+. Global browser support sits around 88% as of mid-2026. It is production-ready with a @supports fallback for remaining unsupported browsers.

What is anchor-name in CSS?

anchor-name is a CSS property that registers an element as an anchor. The value is a dashed-ident (e.g., --my-anchor). Positioned elements reference it via position-anchor: --my-anchor to bind their position to the named anchor.

What does the anchor() function do in CSS?

The anchor() function returns the pixel coordinate of a specific edge (top, bottom, left, right, center, start, end) of the anchor element. Used inside inset properties like bottom: anchor(top), it positions the element relative to that exact edge.

What is @position-try in CSS?

@position-try defines an alternative set of positioning declarations. When a positioned element overflows the viewport, the browser automatically applies the fallback rules defined in the named @position-try block — flipping a tooltip from above to below, for example, with no JavaScript.

Can I replace Floating UI or Popper.js with CSS Anchor Positioning?

For most tooltip and dropdown use cases targeting modern browsers, yes. CSS handles positioning, flip fallbacks, and size matching natively at 0 KB. Libraries like Floating UI remain useful for cursor-based virtual anchors, legacy browser support, and more complex multi-axis overflow scenarios.

What is anchor-size() in CSS?

anchor-size(width) and anchor-size(height) return the dimensions of the anchor element. They are used in sizing properties on the positioned element — for example, width: anchor-size(width) makes a dropdown match its trigger’s exact width.

Does CSS Anchor Positioning work with the HTML popover attribute?

Yes, and the combination is the recommended approach for 2026. The popover attribute handles show/hide, focus management, and light dismiss. CSS Anchor Positioning handles where the popover appears. Together they produce accessible, animated popovers with zero JavaScript.