CSS Anchor Positioning & Popover API: No More JS for Popups in 2026
前端工程Sarah Miller
CSS Anchor Positioning: The "Emancipation Proclamation" for Frontend Devs
Every frontend developer has experienced this pain: using JS to calculate Tooltip positions, handling boundary overflow, listening to scroll and resize events... Now, CSS can handle all of this natively.
Historical review: In 2016 we used Popper.js, in 2020 Floating UI, and in 2026 we finally have native CSS anchor positioning. After 10 years, popup positioning is no longer a JS job.
Evolution from JS Positioning to CSS Positioning
2016 jQuery offset() + manual calculation
Recalculate on every scroll/resize
2020 Floating UI / Popper.js
Auto-flip, offset, boundary detection
But still requires JS init and destroy
2024 CSS Anchor Positioning (Chrome 125+)
Pure CSS declarative positioning
Browser handles boundaries and scrolling natively
2026 Anchor Positioning + Popover API
Pure CSS popups: zero-JS toggle, zero-JS positioning
Complete declarative popup solution
anchor-name and position-anchor
Basic Usage
<div class="card">
<button class="trigger">More Actions</button>
<div class="dropdown">Dropdown content</div>
</div>
.trigger {
anchor-name: --my-anchor;
}
.dropdown {
position: fixed;
position-anchor: --my-anchor;
top: anchor(bottom);
left: anchor(left);
}
anchor() Function Reference
.tooltip {
position: fixed;
position-anchor: --trigger-anchor;
/* Relative to anchor edges */
top: anchor(bottom);
bottom: anchor(top);
left: anchor(left);
right: anchor(right);
/* Anchor center points */
left: anchor(50%);
top: anchor(50%);
/* With offset */
top: calc(anchor(bottom) + 8px);
left: calc(anchor(left) - 4px);
}
position-area Nine-Grid Positioning
Nine-Grid Syntax
┌─────────────┬─────────────┬─────────────┐
│ top left │ top center│ top right │
│ │ │ │
├─────────────┼─────────────┼─────────────┤
│ center left │ center │ center right│
│ │ │ │
├─────────────┼─────────────┼─────────────┤
│ bottom left │bottom center│ bottom right│
│ │ │ │
└─────────────┴─────────────┴─────────────┘
.tooltip-below {
position-area: bottom center;
}
.tooltip-right {
position-area: center right;
}
.dropdown-below-left {
position-area: bottom left;
}
position-fallback and Multi-Level Fallback Strategy
Boundary Overflow Auto-Flip
.dropdown {
position: fixed;
position-anchor: --trigger;
/* Preferred: below anchor */
top: anchor(bottom);
left: anchor(left);
position-fallback: --dropdown-fallback;
}
@position-fallback --dropdown-fallback {
/* Fallback 1: above anchor */
@try {
bottom: anchor(top);
left: anchor(left);
}
/* Fallback 2: below anchor, right-aligned */
@try {
top: anchor(bottom);
right: anchor(right);
}
/* Fallback 3: above anchor, right-aligned */
@try {
bottom: anchor(top);
right: anchor(right);
}
}
Popover API: Native Popover Layer
Basic Usage
<button popovertarget="my-popover">Open Popover</button>
<div id="my-popover" popover>
<p>This is native Popover content</p>
<button popovertarget="my-popover" popovertargetaction="hide">Close</button>
</div>
[popover] {
position: fixed;
inset: unset;
margin: 0;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: white;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
Popover + Anchor Combination
<button class="menu-trigger" popovertarget="menu-popover">Menu</button>
<div id="menu-popover" popover class="menu-dropdown">
<a href="/profile">Profile</a>
<a href="/settings">Settings</a>
<a href="/logout">Logout</a>
</div>
.menu-trigger {
anchor-name: --menu-anchor;
}
.menu-dropdown {
position-anchor: --menu-anchor;
position-area: bottom left;
margin: 4px;
}
anchor-size(): Auto-Size Based on Anchor Element
.trigger {
anchor-name: --select-anchor;
}
.select-dropdown {
position: fixed;
position-anchor: --select-anchor;
top: anchor(bottom);
left: anchor(left);
/* Dropdown width follows trigger button */
width: anchor-size(width);
/* Or set minimum width */
min-width: anchor-size(width);
}
Practice: Pure CSS Common UI Components
Tooltip
<span class="tooltip-trigger" tabindex="0">
Help
<span class="tooltip-content" role="tooltip">This is a help tooltip</span>
</span>
.tooltip-trigger {
anchor-name: --tt-anchor;
position: relative;
cursor: help;
text-decoration: underline dotted;
}
.tooltip-content {
position: fixed;
position-anchor: --tt-anchor;
position-area: top center;
margin: 8px;
padding: 6px 10px;
background: #1f2937;
color: white;
border-radius: 4px;
font-size: 13px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s;
position-fallback: --tt-fallback;
}
.tooltip-trigger:hover .tooltip-content,
.tooltip-trigger:focus .tooltip-content {
opacity: 1;
}
@position-fallback --tt-fallback {
@try { position-area: bottom center; }
@try { position-area: center right; }
@try { position-area: center left; }
}
Dropdown Menu
<button class="dropdown-trigger" popovertarget="dd-menu">
Options ▾
</button>
<div id="dd-menu" popover class="dropdown-menu">
<button class="dropdown-item">Edit</button>
<button class="dropdown-item">Copy</button>
<button class="dropdown-item">Delete</button>
</div>
.dropdown-trigger {
anchor-name: --dd-anchor;
}
.dropdown-menu {
position-anchor: --dd-anchor;
position-area: bottom left;
margin: 4px;
width: anchor-size(width);
min-width: 160px;
padding: 4px;
border-radius: 8px;
background: white;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
position-fallback: --dd-fallback;
}
.dropdown-item {
display: block;
width: 100%;
padding: 8px 12px;
border: none;
background: none;
text-align: left;
border-radius: 4px;
cursor: pointer;
}
.dropdown-item:hover {
background: #f3f4f6;
}
@position-fallback --dd-fallback {
@try { position-area: top left; }
}
Comparison with Floating UI/Popper.js
| Dimension | CSS Anchor + Popover | Floating UI |
|---|---|---|
| JS dependency | Zero JS | 12KB gzip |
| Performance | Browser native | JS calculation |
| Declarative | CSS declarative | JS imperative |
| Boundary flip | position-fallback | flip() middleware |
| Offset | margin/anchor() | offset() middleware |
| Arrow | CSS pseudo-element | SVG/HTML element |
| Virtual elements | Not supported | Supported |
| Dynamic content | Auto-follow | Needs update() |
| Browser support | Chrome 125+ | All browsers |
| SSR friendly | Fully friendly | Needs client JS |
When You Still Need JS Libraries
Scenarios still requiring Floating UI:
1. Need to support older browsers (Safari < 17.5, Firefox < 131)
2. Need virtual element positioning (cursor position following)
3. Need complex middleware chains (autoUpdate, size, hide)
4. React/Vue declarative wrapper requirements
Scenarios where CSS Anchor works:
1. Tooltip, Dropdown, Popover and other standard popups
2. Only need to support modern browsers
3. Pursuing zero JS dependency
4. Performance-sensitive scenarios
Browser Compatibility and Polyfill
Support Status
| Browser | Anchor Positioning | Popover API |
|---|---|---|
| Chrome 125+ | ✅ | ✅ |
| Edge 125+ | ✅ | ✅ |
| Safari 17.5+ | ✅ | ✅ |
| Firefox 131+ | ✅ | ✅ |
| iOS Safari 17.5+ | ✅ | ✅ |
Polyfill Solutions
<script src="https://cdn.jsdelivr.net/npm/@oddbird/css-anchor-positioning@0.3.0/dist/css-anchor-positioning.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oddbird/popover-polyfill@0.4.0/dist/popover.min.js"></script>
Combining with @starting-style for Popup Animations
[popover] {
opacity: 0;
transform: translateY(-8px) scale(0.95);
transition: opacity 0.2s, transform 0.2s, display 0.2s allow-discrete;
overlay: none;
}
[popover]:popover-open {
opacity: 1;
transform: translateY(0) scale(1);
overlay: auto;
}
@starting-style {
[popover]:popover-open {
opacity: 0;
transform: translateY(-8px) scale(0.95);
}
}
Summary
- CSS Anchor Positioning is the ultimate solution for popup positioning — Zero JS, declarative, browser native
- Popover API makes popup toggling zero JS too — popovertarget attribute handles it in one line
- position-fallback solves boundary overflow — No more JS calculation for flipping
- All major browsers support it in 2026 — Safe to use in production
After 10 years, popup positioning has finally returned to where CSS belongs. This isn't an incremental improvement — it's a paradigm shift, just like Flexbox replacing float layouts.
Try these browser-local tools — no sign-up required →
#CSS锚点定位#Anchor Positioning#Popover API#弹出层#CSS