EmpireUI
Get Pro
← Blog8 min read#css anchor#positioning#tooltip

CSS Anchor Positioning: Tooltips and Popovers Without JS Libraries

CSS Anchor Positioning is finally here - build tooltips, dropdowns, and popovers that track DOM elements with pure CSS, no JavaScript library required.

CSS code on screen showing positioning and layout properties

What Is CSS Anchor Positioning?

CSS Anchor Positioning is a spec that shipped unflagged in Chrome 125 (May 2024) and has been maturing fast - by mid-2026, Chromium, Firefox Nightly, and Safari Technology Preview all have meaningful support. The core idea is simple: you name a DOM element as an anchor, then position any other element *relative to that anchor*, even if those two elements are nowhere near each other in the DOM tree. That's the part that previously required Floating UI, Popper.js, or a custom scroll/resize listener.

The API centres on two new CSS properties: anchor-name (assigned to the reference element) and position-anchor plus anchor() functions on the floated element. You can describe things like 'put my tooltip 8px above the bottom edge of the button it belongs to' - and the browser handles repaints, scroll events, and overflow detection. No ResizeObserver. No position:fixed hacks.

Worth noting: this isn't just about tooltips. Dropdowns, context menus, date-pickers, autocomplete lists, and custom select popups all share the same underlying problem - keeping a floating layer tethered to a trigger. Anchor Positioning solves all of them at the CSS layer, which means your JavaScript stays focused on logic instead of geometry.

The Syntax: anchor-name, anchor(), and position-try

The minimal setup looks like this: ``css /* 1. Name the anchor */ .btn { anchor-name: --my-button; } /* 2. Position something relative to it */ .tooltip { position: absolute; position-anchor: --my-button; /* anchor() takes: anchor-name, side, fallback */ bottom: calc(anchor(top) + 8px); left: anchor(center); translate: -50% 0; } ` The anchor()` function resolves to a length value - the computed coordinate of the named edge on the anchor element in the containing block's coordinate space. The browser figures out the math for you across scroll, zoom, and dynamic resizes.

Honestly, the part that trips people up most is that the tooltip needs position: absolute or position: fixed, and it should live outside any overflow: hidden ancestor to render without clipping. The Popover API (popover attribute) solves this perfectly: elements with popover render in the top layer, above everything, so you don't have to flatten your DOM to make the layering work.

Then there's @position-try - CSS's answer to Floating UI's flip and shift middleware. You define one or more fallback position sets, and the browser automatically tries them in order if the preferred position would overflow the viewport: ``css @position-try --flip-above { bottom: auto; top: calc(anchor(bottom) + 8px); } .tooltip { position-try-fallbacks: --flip-above; } ` That single block replaces ~40 lines of Popper.js configuration. Quick aside: position-try-fallbacks also accepts keyword shortcuts like flip-block and flip-inline so you often don't even need the custom @position-try` rule.

Building a Real Tooltip Without Any JavaScript

Let's do the full thing - a tooltip that appears on focus and hover, flips when near the viewport edge, and works with keyboard navigation: ``html <button class="anchor-btn" popovertarget="my-tip"> Hover me </button> <div id="my-tip" class="tooltip" popover="hint"> Appears above, flips below if needed </div> ` `css .anchor-btn { anchor-name: --tip-anchor; } .tooltip { position: fixed; position-anchor: --tip-anchor; /* preferred: 8px above the button */ bottom: calc(anchor(top) - 8px); left: anchor(center); translate: -50% 100%; /* flip below if needed */ position-try-fallbacks: flip-block; /* styling */ background: #1a1a2e; color: #e0e0ff; padding: 6px 12px; border-radius: 6px; font-size: 13px; white-space: nowrap; /* popover resets */ margin: 0; border: none; inset: unset; } ` No JavaScript. The popover="hint"` type shows on hover and focus automatically - that's new in 2024 and it pairs with Anchor Positioning beautifully.

Look, that used to take Floating UI as a dependency, a useFloating hook, and at least one effect to sync state. Now it's 20 lines of CSS and a couple of HTML attributes. The browser's layout engine already knows where every element is, so letting *it* compute the position is strictly faster than any JS measurement loop.

In practice, you'll want to add a few polish touches: a transition on opacity and scale for entry animation, an ::after pseudo-element triangle for the tail, and color-scheme awareness so the tooltip doesn't look jarring in dark mode. But the core geometry is fully handled.

Popovers and Dropdowns: Going Beyond Tooltips

Anchor Positioning really shines on dropdown menus and comboboxes - elements where you need a panel to track a button across scroll and resize. The pattern is identical: anchor-name on the trigger, position-anchor on the floating panel: ``css .select-trigger { anchor-name: --select; width: 200px; } .select-dropdown { position: fixed; position-anchor: --select; top: calc(anchor(bottom) + 4px); left: anchor(left); width: anchor-size(width); /* match trigger width exactly */ position-try-fallbacks: flip-block; } ` The anchor-size() function is the sleeper hit here. It returns the computed width or height of the anchor element - so your dropdown stays the same width as the trigger without a single getBoundingClientRect()` call.

One more thing - anchor names are scoped to the element tree, not globally unique, so you can safely reuse --tooltip as the anchor name across hundreds of component instances in a large React or Vue app. Each instance's tooltip will only see its own anchor. That's actually smarter scoping than what JS libraries give you by default.

That said, position-anchor currently requires both elements to share the same containing block for position: absolute, or you need position: fixed to opt into the top layer. The Popover API's top layer is almost always the right choice for floating UI elements - it sidesteps z-index wars too. If you're building a design system, standardise on popover + Anchor Positioning from day one. Empire UI's glassmorphism components and other interactive patterns are already moving in this direction - worth checking out if you want reference implementations to learn from.

Progressive Enhancement and Browser Support in 2026

Chrome 125+ and Edge 125+ ship the full spec. Firefox shipped the anchor-name and anchor() basics in Firefox 132 (late 2024) and has been iterating since. Safari TP has had partial support since 2025; shipping Safari is the blocker if you need production-ready usage across all browsers today.

The progressive enhancement story is clean though. Wrap your anchor styles in @supports (anchor-name: --test) and fall back to a position: fixed + JS-computed top/left for browsers that don't support it yet. Users on unsupported browsers get a working tooltip; users on Chrome get the CSS-native version. That's a fine trade-off.

.tooltip {
  /* baseline: JS-positioned fixed layer */
  position: fixed;
  top: var(--tip-top, 0px);
  left: var(--tip-left, 0px);
}

@supports (anchor-name: --test) {
  .tooltip {
    /* override with native anchor when available */
    position-anchor: --tip-anchor;
    top: unset;
    left: unset;
    bottom: calc(anchor(top) - 8px);
    left: anchor(center);
    translate: -50% 100%;
  }
}

For tools and UI kits, keep an eye on how your component library handles this. If you're using a UI kit that still relies on Popper.js or Floating UI in 2026, it's worth checking their release notes - most major libraries are actively replacing the JS positioning engines with CSS Anchor Positioning fallbacks. If they haven't started yet, that's a sign the maintenance is lagging. You can also use Empire UI's box shadow generator and related CSS tools to experiment with your floating element styles quickly without spinning up a full project.

Performance and Accessibility Notes

The performance argument for CSS Anchor Positioning is real. JS-based positioning runs on the main thread and re-fires on every scroll event unless carefully throttled. CSS positioning runs in the browser's layout engine - the same engine that already knows where every element is. On a page with 50 tooltips, that's 50 fewer scroll listeners. On mobile, it's noticeably smoother.

Accessibility follows standard patterns. Your tooltip element should have role="tooltip" and the trigger should reference it via aria-describedby. The Popover API with popover="hint" handles show/hide focus behaviour automatically, but you still need the ARIA wiring for screen readers: ``html <button aria-describedby="tip-1" class="anchor-btn" popovertarget="tip-1"> Help </button> <div id="tip-1" class="tooltip" popover="hint" role="tooltip"> Saves your work automatically </div> ``

One thing to watch: popover="auto" closes when you click outside, popover="hint" closes on blur/mouseleave, and popover="manual" needs explicit JS control. Match the type to the interaction model. A tooltip triggered by hover wants hint; a dropdown triggered by click wants auto. Getting this wrong is the most common accessibility mistake I've seen with this API.

In practice, Anchor Positioning pairs well with the :has() selector and CSS custom properties for dynamic theming. You can make a context menu that reflects the theme of the element it anchors to without touching JavaScript state - just propagate a custom property down from the anchor and read it on the floating panel. Combine that with tailwind-css-animations patterns for entry transitions and you've got a genuinely modern floating UI stack.

FAQ

Is CSS Anchor Positioning safe to use in production in 2026?

Chrome and Edge 125+ ship it fully, Firefox support is in progress, and Safari TP is partial. Use the @supports feature query to fall back to JS positioning - production is reasonable today with that guard in place.

Do I still need Floating UI or Popper.js?

Not for new projects targeting Chromium. For broad cross-browser support including Safari shipping today, a thin JS fallback layer still helps. But the dependency is shrinking fast.

Can I use Anchor Positioning with React or Vue components?

Absolutely - anchor-name scopes per element tree so you can reuse the same custom property name across thousands of component instances without collisions. Just attach it via a class or style prop.

What's the difference between popover="hint" and popover="auto"?

'hint' shows on hover/focus and closes on blur/mouseleave - ideal for tooltips. 'auto' shows on explicit trigger and closes on outside click - better for dropdowns and menus.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Spatial UI Design in 2026: Vision Pro, Depth and the Glass EraLanding Page Design Patterns in 2026: Above the Fold, Hero, CTAMobile-First UI Design: 48px Touch Targets, Thumb Zones, Safe AreasE-Commerce Product Card Design: 8 Layouts That Actually Convert