Tooltips and Overlays Without JavaScript
Tooltips, dropdowns and contextual overlays were solved for years either with JavaScript or with fragile CSS hacks. CSS Anchor Positioning changes that fundamentally: anchor(), position-anchor and @position-try enable precise, context-aware positioning directly in the stylesheet, without a single line of JavaScript.
Table of Contents
- 1. The Problem With Tooltip Positioning Before Anchor Positioning
- 2. Basics: anchor-name and position-anchor
- 3. The anchor() Function: Precise Edge References
- 4. @position-try: Automatic Fallback Positioning
- 5. inset-area: Grid-Based Positioning
- 6. Practical Example: A Tooltip Component Entirely in CSS
- 7. Dropdown Menu With CSS Anchor Positioning
- 8. Browser Support and Progressive Enhancement
- 9. Anchor Positioning in Direct Comparison
- 10. Summary
- 11. FAQ
1. The Problem With Tooltip Positioning Before Anchor Positioning
Positioning tooltips, dropdowns and contextual overlays has been one of the most unsatisfying tasks in frontend development for years. The core problem: an overlay needs to be positioned relative to its trigger, an element that sits somewhere in the DOM, while the overlay itself often lives in a different place in the document tree, for example right before the closing <body> tag, to avoid stacking context problems. CSS Anchor Positioning addresses exactly this conflict between DOM position and visual positioning.
Earlier solutions relied either on JavaScript libraries such as Popper.js or Floating UI, which position the overlay dynamically via getBoundingClientRect(), or on CSS hacks using position: relative on the parent element, which ties the overlay to the trigger in the DOM and creates stacking problems. Both approaches have drawbacks: JavaScript solutions add extra bytes and do not always react synchronously to layout changes. CSS hacks break down in complex layouts with nested stacking contexts. CSS Anchor Positioning solves both natively in the browser.
2. Basics: anchor-name and position-anchor
The heart of CSS Anchor Positioning is two new CSS properties. The anchor-name property marks any element as an anchor and assigns it a custom-property-like identifier, for example anchor-name: --my-button. The overlay that should align to this anchor references that name via position-anchor: --my-button. This logically links the two elements, regardless of where they sit in the DOM tree. The connection exists purely in CSS, with no JavaScript and no DOM mutation.
The overlay must have position: absolute or position: fixed set for CSS Anchor Positioning to take effect. With position: fixed, the overlay stays correctly positioned relative to the anchor while scrolling, because the browser updates the offset automatically. That is a significant advantage over JavaScript solutions, which have to listen for scroll events. The anchor positioning system runs in the same layout pass as every other CSS property, so there is no asynchronous repositioning and no layout thrashing.
/* Step 1: Mark the trigger element as an anchor */
.tooltip-trigger {
anchor-name: --tooltip-anchor;
/* Works on any element: button, span, div, img */
}
/* Step 2: Link the overlay to the anchor */
.tooltip {
position: fixed; /* or absolute, fixed survives scrolling */
position-anchor: --tooltip-anchor;
/* Step 3: Position relative to anchor edges */
bottom: anchor(top); /* overlay bottom = anchor top */
left: anchor(center); /* overlay left = anchor horizontal center */
translate: -50% -0.5rem; /* center and add gap */
/* Visibility: linked to :popover-open or :hover chain */
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
}
.tooltip-trigger:hover .tooltip,
.tooltip-trigger:focus .tooltip {
opacity: 1;
pointer-events: auto;
}
3. The anchor() Function: Precise Edge References
The anchor() function is the tool used to read concrete coordinates from the anchor element. It can be used in the overlay's inset properties: top, right, bottom, left, inset-block-start, inset-inline-end and similar. As an argument, anchor() takes an edge keyword: top, bottom, left, right, center, start, end or a percentage value. An optional second argument is a fallback value in case the anchor does not exist or cannot be resolved.
The grammar is intentionally logical: bottom: anchor(top) means the bottom edge of the overlay aligns with the top edge of the anchor. left: anchor(right) places the overlay to the right of the anchor. Combined with margin or translate, fine-grained spacing becomes easy to achieve. Particularly elegant is anchor(center), which returns the anchor's centerline, horizontal or vertical depending on context. CSS Anchor Positioning also knows the anchor-size() function, which returns the width or height of the anchor and allows the overlay to be set to exactly the same width as its trigger.
4. @position-try: Automatic Fallback Positioning
One of the most powerful features of CSS Anchor Positioning is the @position-try at-rule. It solves the classic viewport overflow problem: if a tooltip does not have enough room above, it should appear below instead. If a dropdown would extend past the right edge of the viewport, it should align to the left instead. Until now, this logic required JavaScript to check viewport boundaries and swap CSS classes.
With @position-try --fallback-name { … } you define alternative positioning rules. The position-try-fallbacks property on the overlay takes an ordered list of these fallbacks. The browser tries the options in order and picks the first one where the overlay fits entirely within the viewport, automatically, with no JavaScript, on every layout reflow. CSS Anchor Positioning combines this with the position-try-order property, which controls whether the browser prefers the option with the most available space instead of strictly working through the list in order.
/* Define fallback positioning strategies */
@position-try --above {
bottom: anchor(top);
left: anchor(center);
translate: -50% -0.5rem;
}
@position-try --below {
top: anchor(bottom);
left: anchor(center);
translate: -50% 0.5rem;
}
@position-try --right {
top: anchor(center);
left: anchor(right);
translate: 0.5rem -50%;
}
@position-try --left {
top: anchor(center);
right: anchor(left);
translate: -0.5rem -50%;
}
/* Apply: browser picks first fitting option */
.tooltip {
position: fixed;
position-anchor: --tooltip-anchor;
/* Primary position */
bottom: anchor(top);
left: anchor(center);
translate: -50% -0.5rem;
/* Automatic overflow handling */
position-try-fallbacks: --above, --below, --right, --left;
position-try-order: most-space; /* prefer option with most available space */
}
5. inset-area: Grid-Based Positioning
Besides the anchor() function, inset-area offers a second, conceptually different way to position elements in CSS Anchor Positioning. The property thinks in terms of an imaginary 3x3 grid around the anchor element: top left, top center, top right, center left, center, center right, bottom left, bottom center, bottom right. These are referred to as regions. With inset-area: top the overlay is placed in the top center, with inset-area: end start in the logical bottom left corner.
The advantage of inset-area over the anchor() function lies in readability: you describe the desired region rather than concrete edge values. The overlay adapts automatically to the anchor size. CSS Anchor Positioning also allows two-value inset-area declarations such as inset-area: bottom span-right, which covers the entire bottom row from the anchor's midpoint to the right, ideal for context-menu-style overlays. Combining inset-area with position-try-fallbacks makes it trivial to write overlays that automatically adapt to the available viewport space.
6. Practical Example: A Tooltip Component Entirely in CSS
A complete tooltip component built with CSS Anchor Positioning needs not a single line of JavaScript. The browser's Popover API handles visibility control: the tooltip element gets the popover attribute, the trigger button gets popovertarget. The browser links the two automatically and takes care of accessibility attributes such as aria-expanded and focus management. CSS is left to handle only positioning and animation.
The interplay of the Popover API and CSS Anchor Positioning is particularly strong: because the popover element renders in the browser's top layer, above all normal stacking contexts, the classic z-index problems disappear entirely. At the same time, CSS Anchor Positioning still allows the overlay to be placed precisely relative to its trigger, even if the trigger sits deep inside a nested stacking context. The combination of the two APIs is the real breakthrough for JavaScript-free overlays.
/* Complete CSS-only tooltip using Popover API + Anchor Positioning */
/* Anchor: the trigger button */
[popovertarget="my-tooltip"] {
anchor-name: --tooltip-btn;
}
/* The tooltip popover */
#my-tooltip {
/* Popover resets to display:none by default */
position: fixed;
position-anchor: --tooltip-btn;
inset: auto; /* reset default popover insets */
/* Position above the anchor */
bottom: calc(anchor(top) + 0.5rem);
left: anchor(center);
translate: -50% 0;
/* Size and style */
max-width: 20rem;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
background: #1e1b4b;
color: #ede9fe;
font-size: 0.875rem;
border: none;
box-shadow: 0 4px 24px rgba(74,29,150,0.35);
/* Automatic flip if not enough space above */
position-try-fallbacks: flip-block;
/* Smooth entry animation (uses @starting-style) */
transition: opacity 0.15s ease, scale 0.15s ease, display 0.15s ease allow-discrete;
opacity: 1;
scale: 1;
}
/* Before opening (entry animation start state) */
@starting-style {
#my-tooltip:popover-open {
opacity: 0;
scale: 0.95;
}
}
/* Hidden state */
#my-tooltip:not(:popover-open) {
opacity: 0;
scale: 0.95;
}
7. Dropdown Menu With CSS Anchor Positioning
Dropdown menus are another central use case for CSS Anchor Positioning. The classic problem: a navigation dropdown must begin below its trigger and align with its left edge, no matter how wide the menu is. With CSS Anchor Positioning you write top: anchor(bottom) and left: anchor(left). If the menu would exceed the right edge of the viewport, a @position-try fallback automatically kicks in, aligning the right edge of the menu with the right edge of the trigger.
A common dropdown pattern is width matching: the dropdown should be at least as wide as its trigger. With min-width: anchor-size(width) this is done in a single line of CSS. Besides anchor-size(width), CSS Anchor Positioning also knows anchor-size(height), anchor-size(inline) and anchor-size(block) for writing-direction-independent sizing. This replaces entire JavaScript functions that previously read the trigger width via getBoundingClientRect() and applied it to the dropdown via style.minWidth.
8. Browser Support and Progressive Enhancement
Browser support for CSS Anchor Positioning has become pleasingly broad since 2025, though it is not yet universal. Chrome and Edge support the full specification starting with version 125. Firefox delivered support starting with version 131. Safari implemented the core functionality starting with version 18, though some advanced features such as position-try-order: most-space are still missing. For production deployments, feature detection with @supports (anchor-name: --x) is recommended.
Progressive enhancement works well with CSS Anchor Positioning: the fallback for unsupporting browsers is classic positioning with position: absolute relative to the parent element. The overlay may not work perfectly at every viewport size in that case, but it remains visible and usable. You then add the anchor positioning rules inside a @supports block. That way modern browsers benefit from precise positioning while older browsers get a functional fallback, with no JavaScript required in either case.
9. Anchor Positioning in Direct Comparison
Comparing classic tooltip techniques with CSS Anchor Positioning shows the advantages across several dimensions: bundle size, accessibility, maintainability and correctness in complex layouts.
| Criterion | Popper.js / Floating UI | CSS Anchor Positioning | Advantage |
|---|---|---|---|
| Bundle size | ~12 KB (Floating UI core) | 0 KB (browser native) | No third-party package |
| Viewport flip | JS logic, scroll events | @position-try, automatic | No event listener |
| Stacking context | Z-index management needed | Top layer via Popover API | No z-index conflicts |
| Accessibility | Manual ARIA maintenance | Popover API, browser managed | aria-expanded, focus automatic |
| Overlay width | getBoundingClientRect() + JS | anchor-size(width) | One line of CSS |
The table shows that CSS Anchor Positioning is not just a technical gimmick but a serious replacement for JavaScript-based positioning libraries in modern projects. The one real remaining drawback is browser support that is not yet fully complete, which calls for progressive enhancement. Once all relevant browsers have fully implemented the specification, there will be no remaining case for Popper.js in new projects.
Mironsoft
Modern CSS, frontend architecture and performance optimization
JavaScript-free UI components for your project?
We bring modern CSS APIs such as Anchor Positioning, the Popover API and View Transitions into your codebase, for leaner bundles, better accessibility and fewer JavaScript dependencies.
CSS audit
Analyzing and modernizing existing tooltip and overlay implementations
Component development
Tooltips, dropdowns and popovers built as progressively enhanced CSS components
Bundle optimization
Replacing Popper.js and similar libraries with native CSS solutions
10. Summary
CSS Anchor Positioning solves a fundamental problem in web development: positioning overlays relative to arbitrary elements in the DOM, independent of their position in the document tree. The three core concepts, anchor-name and position-anchor for linking, the anchor() function for precise edge references and @position-try for automatic overflow handling, fully replace JavaScript libraries such as Popper.js for most use cases. Combined with the browser's Popover API, z-index problems and manual accessibility implementations also disappear.
Browser support reached a level of maturity in 2025 that justifies using CSS Anchor Positioning in new projects with progressive enhancement. For projects that still need to support older browsers, wrapping usage in a @supports block is the right approach. The long-term direction is clear: overlay positioning belongs in CSS, not in JavaScript. Teams working with modern CSS APIs today build leaner, more maintainable and more accessible interfaces.
CSS Anchor Positioning: The Essentials at a Glance
Linking
anchor-name: --name on the trigger, position-anchor: --name on the overlay. DOM-independent linking purely in CSS.
Positioning
anchor(top/bottom/left/right/center) in inset properties. anchor-size(width) for width matching.
Overflow handling
@position-try defines alternatives. position-try-fallbacks sets the order. The browser chooses automatically.
Browser support
Chrome 125+, Firefox 131+, Safari 18+. Progressive enhancement via @supports (anchor-name: --x).