CSS Anchor Positioning for Advanced Popover Systems

Table of contents
- Building Modern UI Components Without JavaScript Gymnastics
- Why Anchor Positioning Matters
- The Power of Anchor Positioning + Popover API
- Core Concepts of Anchor Positioning
- Building a Real-World Advanced Dropdown Menu
- Fallback Positioning for Smart Edge Detection
- Real-World Considerations and Best Practices
- Conclusion

Building Modern UI Components Without JavaScript Gymnastics
As front-end developers, we've all been there: trying to position tooltips, popovers, and dropdown menus that need to appear relative to a specific element on the page. For years, our options were limited, either perform HTML/CSS gymnastics or resort to JavaScript for precise positioning. The result? Complex, hard-to-maintain code that often breaks accessibility.
But now, we have CSS Anchor Positioning, a powerful native solution that works beautifully with the new Popover API to create truly sophisticated UI components with minimal code.
Why Anchor Positioning Matters
Before anchor positioning, associating an element with another element and dynamically changing its location based on an anchor's position required JavaScript, which added complexity and performance issues. It also wasn't guaranteed to work in all situations. This was particularly frustrating when building components that needed to adapt to scrolling, resizing, or other layout shifts.
As a developer who's built countless tooltips and dropdown menus over the years, I can tell you firsthand that the fragility of JavaScript positioning solutions has been a constant source of headaches. Elements appearing offscreen, flickering during position calculations, or breaking on mobile were all common issues.
The Power of Anchor Positioning + Popover API
The real magic happens when you combine these two modern web APIs:
CSS Anchor Positioning: Positions elements relative to another element (anchor)
Popover API: Handles UI states, accessibility, focus management, and layer stacking
When building components where content needs to become visible upon user interaction, it can be a challenge to ensure they are fully accessible to users of assistive technologies, and often require additional JS to get this right. Using web platform features like the Popover API can help us build more accessible websites, as much of the necessary functionality comes already baked in.
Core Concepts of Anchor Positioning
Let's break down the key concepts you need to understand:
1. Explicit Anchor Association
To associate an anchor with a positioned element, you need two primary properties:
/* Designate the button as an anchor */
.trigger-button {
anchor-name: --tooltip-anchor;
}
/* Position the tooltip relative to this anchor */
.tooltip {
position: absolute; /* or fixed */
position-anchor: --tooltip-anchor;
}
2. Positioning with Anchor Functions
CSS anchor positioning enables anchor-positioned elements to be placed relative to the edges of their associated anchor(s). The module defines the anchor() function, which is a valid value for each of the inset properties.
Let's say you want a tooltip to appear above a button:
.tooltip {
position: absolute;
position-anchor: --tooltip-anchor;
bottom: anchor(top);
left: anchor(center);
translate: -50% -10px; /* Center horizontally and offset from button */
}
This sets the tooltip's bottom edge to align with the button's top edge, and centers it horizontally.
3. Implicit Anchor Association with Popovers
Associating any kind of popover with its invoker creates an implicit anchor reference between the two. This causes the invoker to become the popover's anchor element, meaning that you can position the popover relative to it using CSS anchor positioning.
<button popovertarget="my-popover">Open Menu</button>
<div id="my-popover" popover>Popover content</div>
With the above markup, the popover already has an implicit reference to the button as its anchor, even without using anchor-name
or position-anchor
CSS properties.
Building a Real-World Advanced Dropdown Menu
Let's create a multi-level dropdown navigation menu that:
Opens on click
Positions submenus intelligently
Animates smoothly
Works without JavaScript
Is fully accessible
HTML Structure Breakdown
Main Navigation Container:
The
<nav class="main-nav">
serves as the wrapper for the entire navigation menu.This contains the top-level unordered list that houses all menu items.
Top-Level Menu Button:
<button popovertarget="dropdown-1">Products</button>
creates a button that when clicked will show/hide the dropdown menu.The
popovertarget
attribute connects this button to the dropdown element with the ID "dropdown-1".
Primary Dropdown Container:
<div popover id="dropdown-1" class="dropdown">
creates the first-level dropdown.The
popover
attribute makes this element a popover that can be shown/hidden by its associated button.The
id="dropdown-1"
matches thepopovertarget
value from the button, establishing their connection.
Nested Submenu Structure:
Inside the first dropdown, there's another button with
popovertarget="subdropdown-1"
.This button is connected to another popover with
id="subdropdown-1"
.This creates a nested submenu that appears when hovering over or clicking "Product B".
CSS Explanation
Popover Base Styling
[popover] {
position: fixed;
min-width: 180px;
margin: 0;
}
The
[popover]
attribute selector targets all elements with the popover attributeposition: fixed
is essential for anchor positioning to work - without this, the positioning functions would have no effectSetting
margin: 0
removes the default spacing that browsers apply to popovers
Anchor Positioning for First-Level Dropdown
#dropdown-1 {
top: anchor(bottom);
left: anchor(start);
margin-top: 0.5rem;
}
top: anchor(bottom)
positions the dropdown's top edge at the bottom edge of its implicit anchor (the button that triggered it)left: anchor(start)
aligns the left edge of the dropdown with the start edge of the buttonThe
margin-top: 0.5rem
creates visual spacing between the dropdown and its trigger
Intelligent Subdropdown Positioning
.subdropdown {
top: anchor(top);
left: anchor(end);
margin-left: 0.5rem;
}
top: anchor(top)
aligns the top of the submenu with the top of its parent menu itemleft: anchor(end)
positions the submenu to start at the right edge of its parent itemmargin-left: 0.5rem
creates spacing between the parent menu and submenu
Popover Animation System
[popover] {
display: none;
opacity: 0;
transform: translateY(5px);
transition:
opacity 200ms ease,
transform 200ms ease,
display 200ms allow-discrete,
overlay 200ms allow-discrete;
}
The
transition
property creates smooth animations for both opacity and transform changesdisplay 200ms allow-discrete
is critical - it enables transitions for the display property which normally cannot be animatedoverlay 200ms allow-discrete
ensures the popover stays in the top layer during animationtransform: translateY(5px)
creates a starting position slightly below where the dropdown will end up
[popover]:popover-open {
display: block;
opacity: 1;
transform: translateY(0);
@starting-style {
opacity: 0;
transform: translateY(5px);
}
}
The
:popover-open
pseudo-class targets the popover when it's visible@starting-style
is a new CSS feature that sets the initial values for the opening animationWithout
@starting-style
, the popover would appear immediately and only animate when closing
Dropdown Arrow Indicator
#dropdown-1::before {
content: "";
position: absolute;
top: -6px;
left: 1rem;
width: 12px;
height: 12px;
background: var(--menu-background);
transform: rotate(45deg);
border-left: 1px solid var(--menu-border);
border-top: 1px solid var(--menu-border);
}
Creates a small rotated square that appears as a triangular arrow pointing up toward the trigger button
Positioned just above the dropdown to create a visual connection between the dropdown and its trigger
The borders match the dropdown border to create a seamless visual connection
Popover Trigger Indicators
.main-nav button[popovertarget]::after {
content: "";
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid currentColor;
margin-left: 0.5rem;
}
.main-nav li button[popovertarget="subdropdown-1"]::after {
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 4px solid currentColor;
border-right: none;
}
Uses the attribute selector
[popovertarget]
to target only buttons that control popoversCreates different arrow indicators: downward for top-level and rightward for nested dropdowns
These provide a visual cue that clicking will reveal additional content
Fallback Positioning for Smart Edge Detection
One of the most powerful features of anchor positioning is fallback positioning to handle edge cases, like when a dropdown would render offscreen.
CSS anchor positioning also provides CSS-only mechanisms for specifying multiple alternative positions for an anchor-positioned element. For example, if a tooltip is anchored to a form field but the tooltip would otherwise be rendered offscreen in its default position settings, the browser can try rendering it in a different suggested position so it is placed onscreen.
Here's a practical implementation:
Real-World Considerations and Best Practices
Progressive Enhancement
Always build with fallbacks in mind. Test your components with anchor positioning disabled to ensure they remain functional. A good approach is to use feature detection:
if (CSS.supports('anchor-name: --anchor')) {
// Use anchor positioning
} else {
// Use JavaScript positioning fallback
}
Animation Techniques
The combination of three new CSS features enables smooth animations without JavaScript:
transition-behavior: allow-discrete
makes thedisplay
property animatable@starting-style
defines initial animation states for elements that are appearingInclude
overlay
in your transitions to keep popovers in the top layer during animations
[popover] {
transition: opacity 200ms, display 200ms allow-discrete;
}
[popover]:popover-open {
@starting-style { opacity: 0; }
}
Troubleshooting Checklist
If your anchor positioning isn't working:
Verify the anchor element is visible (hidden elements can't be anchors)
Confirm you're using
position: absolute
orposition: fixed
Check for typos in your anchor name values
Remember that popovers automatically create implicit anchor references
Browser Support Strategy
As of mid-2025, implement a layered approach:
Use feature detection rather than browser detection
Provide JavaScript positioning fallbacks for critical components
Consider lightweight polyfills for consistent behavior
Monitor caniuse.com for support updates
Conclusion
CSS Anchor Positioning fundamentally transforms how we build interactive UI components on the web. By eliminating complex JavaScript positioning logic and replacing it with declarative CSS, it delivers three key benefits:
Simpler codebase: Replace hundreds of lines of positioning JavaScript with a few lines of CSS
Better accessibility: The Popover API handles focus management, keyboard interaction, and proper ARIA attributes automatically
Superior performance: Native browser implementations are inherently more efficient than JavaScript calculations
As you implement these features in your projects, remember that the web is fundamentally progressive. Start with solid, accessible foundations and layer on these enhancements where supported.
Subscribe to my newsletter
Read articles from Peter Bamigboye directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Peter Bamigboye
Peter Bamigboye
I am Peter, a front-end web developer who writes on current technologies that I'm learning or technologies that I feel the need to simplify.