CSS Anchor Positioning: Tooltips Without JavaScript
CSS Anchor Positioning eliminates JavaScript tooltip libraries. One anchor-name, one position-anchor, and the browser handles collision detection.
Positioning tooltips, popovers, and dropdowns has always required JavaScript to calculate coordinates,
handle viewport collisions, and update on scroll. CSS Anchor Positioning (Chrome 125+, Edge 125+)
eliminates all of that. One anchor-name on the trigger, one position-anchor
on the target, and the browser handles the math.
The Basic Pattern
<button style="anchor-name: --tooltip-anchor">Hover me</button>
<div class="tooltip" popover>Tooltip content</div>
<style>
.tooltip {
position: fixed;
position-anchor: --tooltip-anchor;
/* Position relative to anchor using the 3x3 grid */
position-area: top;
/* Offset from anchor edge */
margin-bottom: 8px;
}
</style>
No JavaScript. No getBoundingClientRect(). No scroll listeners. The browser
recalculates position automatically on scroll, resize, and layout changes.
The position-area Grid
position-area (formerly inset-area before Chrome 129) places the positioned
element on a conceptual 3×3 grid centered on the anchor:
/* Grid positions:
top-left | top | top-right
left | center | right
bottom-left | bottom | bottom-right
*/
/* Tooltip above anchor, centered */
position-area: top;
/* Dropdown below anchor, aligned left */
position-area: bottom span-right;
/* Popover to the right, vertically centered */
position-area: right;
The span-* modifiers allow spanning multiple grid cells. bottom span-right
spans from center-bottom to bottom-right, useful for dropdowns that shouldn't overflow the anchor's
left edge.
The anchor() Function for Fine Control
When position-area isn't precise enough, the anchor() function gives
pixel-level control:
.tooltip {
position: fixed;
position-anchor: --trigger;
/* Position top edge at anchor's bottom edge */
top: anchor(bottom);
/* Center horizontally with anchor */
left: anchor(center);
translate: -50% 0;
/* Or use calc() for offsets */
top: calc(anchor(bottom) + 8px);
}
anchor() accepts top, bottom, left,
right, center, and percentage values. It resolves to a length
in the coordinate space of the containing block.
Viewport Collision: position-try-fallbacks
The killer feature. When a tooltip would overflow the viewport, position-try-fallbacks
automatically flips to an alternative position:
.tooltip {
position: fixed;
position-anchor: --trigger;
position-area: top;
/* Flip to bottom if top overflows */
position-try-fallbacks: flip-block;
}
/* Or multiple fallbacks in priority order */
.dropdown {
position-area: bottom;
position-try-fallbacks:
flip-block, /* Try top */
flip-inline, /* Try left/right */
flip-block flip-inline; /* Try opposite corner */
}
Built-in flip options: flip-block (vertical), flip-inline (horizontal),
flip-start, flip-both. The browser tries each in order until one fits.
Custom Fallbacks with @position-try
For complex fallback behavior—different sizing, different offsets—define custom position-try rules:
@position-try --compact-bottom {
position-area: bottom;
width: 150px; /* Narrower to fit */
max-height: 200px;
}
@position-try --side-panel {
position-area: right;
width: 250px;
}
.tooltip {
position-area: top;
width: 300px;
position-try-fallbacks:
flip-block,
--compact-bottom,
--side-panel;
}
Each @position-try block can override any positioning properties. The browser evaluates
overflow for each option in sequence.
Dynamic Anchors and Multiple Tooltips
For dynamically generated content, inline styles are the cleanest approach since each anchor needs a unique name:
// JavaScript generates inline anchor-name
items.forEach((item, i) => {
button.style.anchorName = `--item-${i}`;
tooltip.style.positionAnchor = `--item-${i}`;
});
Alternatively, use anchor-name: auto combined with ID references (still experimental):
<button id="btn-1">...</button>
<div class="tooltip" style="position-anchor: --btn-1">...</div>Integration with Popover API
Anchor positioning pairs naturally with the Popover API. Use popovertarget to create
the implicit association, then add anchor positioning for layout:
<button popovertarget="menu" style="anchor-name: --menu-trigger">
Open Menu
</button>
<div id="menu" popover style="position-anchor: --menu-trigger">
<!-- Menu content -->
</div>
<style>
[popover] {
position: fixed;
margin: 0;
position-area: bottom span-right;
position-try-fallbacks: flip-block;
}
</style>The Popover API handles show/hide, focus management, and light-dismiss. Anchor positioning handles layout. Zero JavaScript for a fully functional dropdown.
Edge Cases and Gotchas
Anchor Must Be in DOM Before Target
The anchor element must exist in the DOM when the positioned element renders. If you're dynamically
inserting both, ensure the anchor is added first or use requestAnimationFrame to defer
positioning.
position: fixed Required
Anchor-positioned elements must use position: fixed or position: absolute.
The anchor() function won't work with static or relative positioning.
Containing Block Matters
With position: absolute, the anchor must share a containing block with the positioned
element. For tooltips in deeply nested components, position: fixed is safer—it always
positions relative to the viewport.
Property Rename: inset-area → position-area
Chrome 125-128 used inset-area. Chrome 129+ uses position-area.
Similarly, position-try-options became position-try-fallbacks.
Support both during the transition:
.tooltip {
inset-area: top; /* Chrome 125-128 */
position-area: top; /* Chrome 129+ */
}Browser Support and Fallbacks
As of early 2026: Chrome/Edge 125+, no Firefox or Safari support yet. For progressive enhancement:
@supports (anchor-name: --test) {
.tooltip {
position: fixed;
position-anchor: --trigger;
position-area: top;
}
}
@supports not (anchor-name: --test) {
/* Fallback: absolute positioning with JS */
.tooltip {
position: absolute;
}
}The CSS Anchor Positioning Polyfill provides partial support for other browsers, though with performance tradeoffs on scroll-heavy pages.
Key Takeaways
anchor-nameon trigger +position-anchoron target creates the relationshipposition-areauses a 3×3 grid for common placements;anchor()for pixel precisionposition-try-fallbackshandles viewport collision automatically—no JS resize handlers- Pairs seamlessly with Popover API for zero-JS dropdowns and tooltips
- Currently Chromium-only; use feature detection and polyfills for broader support
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
MDN: CSS Anchor Positioning
Complete MDN guide covering anchor-name, position-anchor, and position-area properties
Chrome Developers: Anchor Positioning API
Official Chrome blog post with interactive examples and migration notes
CSS Anchor Positioning Spec (W3C)
The W3C working draft specification for CSS Anchor Positioning
Tools & Utilities
Related Insights
Explore related edge cases and patterns
Advertisement