CSS Anchor Positioning y Popover API: Adiós al JS para Popups en 2026
前端工程
CSS Anchor Positioning: La "Proclamación de Emancipación" para Desarrolladores Frontend
Todo desarrollador frontend ha experimentado este dolor: usar JS para calcular posiciones de Tooltip, manejar desbordamiento de límites, escuchar eventos de scroll y resize... Ahora, CSS puede manejar todo esto de forma nativa.
Revisión histórica: En 2016 usábamos Popper.js, en 2020 Floating UI, y en 2026 finalmente tenemos posicionamiento de ancla CSS nativo. Después de 10 años, el posicionamiento de popups ya no es trabajo de JS.
Evolución del Posicionamiento con JS al Posicionamiento con CSS
2016 jQuery offset() + cálculo manual
Recalcular en cada scroll/resize
2020 Floating UI / Popper.js
Auto-flip, offset, detección de límites
Pero aún requiere init y destroy con JS
2024 CSS Anchor Positioning (Chrome 125+)
Posicionamiento declarativo CSS puro
El navegador maneja límites y scroll nativamente
2026 Anchor Positioning + Popover API
Popups CSS puro: toggle sin JS, posicionamiento sin JS
Solución declarativa completa de popups
anchor-name y position-anchor
Uso Básico
<div class="card">
<button class="trigger">Más Acciones</button>
<div class="dropdown">Contenido desplegable</div>
</div>
.trigger {
anchor-name: --my-anchor;
}
.dropdown {
position: fixed;
position-anchor: --my-anchor;
top: anchor(bottom);
left: anchor(left);
}
Referencia de la Función anchor()
.tooltip {
position: fixed;
position-anchor: --trigger-anchor;
/* Relativo a los bordes del ancla */
top: anchor(bottom);
bottom: anchor(top);
left: anchor(left);
right: anchor(right);
/* Puntos centrales del ancla */
left: anchor(50%);
top: anchor(50%);
/* Con offset */
top: calc(anchor(bottom) + 8px);
left: calc(anchor(left) - 4px);
}
position-area: Posicionamiento en Cuadrícula de Nueve Celdas
Sintaxis de Nueve Celdas
┌─────────────┬─────────────┬─────────────┐
│ 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 y Estrategia de Fallback Multinivel
Auto-Flip por Desbordamiento de Límites
.dropdown {
position: fixed;
position-anchor: --trigger;
/* Preferido: debajo del ancla */
top: anchor(bottom);
left: anchor(left);
position-fallback: --dropdown-fallback;
}
@position-fallback --dropdown-fallback {
/* Fallback 1: encima del ancla */
@try {
bottom: anchor(top);
left: anchor(left);
}
/* Fallback 2: debajo del ancla, alineado a la derecha */
@try {
top: anchor(bottom);
right: anchor(right);
}
/* Fallback 3: encima del ancla, alineado a la derecha */
@try {
bottom: anchor(top);
right: anchor(right);
}
}
Popover API: Capa de Popover Nativa
Uso Básico
<button popovertarget="my-popover">Abrir Popover</button>
<div id="my-popover" popover>
<p>Este es contenido de Popover nativo</p>
<button popovertarget="my-popover" popovertargetaction="hide">Cerrar</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);
}
Combinación Popover + Anchor
<button class="menu-trigger" popovertarget="menu-popover">Menú</button>
<div id="menu-popover" popover class="menu-dropdown">
<a href="/profile">Perfil</a>
<a href="/settings">Configuración</a>
<a href="/logout">Cerrar Sesión</a>
</div>
.menu-trigger {
anchor-name: --menu-anchor;
}
.menu-dropdown {
position-anchor: --menu-anchor;
position-area: bottom left;
margin: 4px;
}
anchor-size(): Auto-Dimensionamiento Basado en el Elemento Ancla
.trigger {
anchor-name: --select-anchor;
}
.select-dropdown {
position: fixed;
position-anchor: --select-anchor;
top: anchor(bottom);
left: anchor(left);
/* El ancho del desplegable sigue al botón trigger */
width: anchor-size(width);
/* O establecer ancho mínimo */
min-width: anchor-size(width);
}
Práctica: Componentes UI Comunes en CSS Puro
Tooltip
<span class="tooltip-trigger" tabindex="0">
Ayuda
<span class="tooltip-content" role="tooltip">Este es un tooltip de ayuda</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; }
}
Menú Desplegable
<button class="dropdown-trigger" popovertarget="dd-menu">
Opciones ▾
</button>
<div id="dd-menu" popover class="dropdown-menu">
<button class="dropdown-item">Editar</button>
<button class="dropdown-item">Copiar</button>
<button class="dropdown-item">Eliminar</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; }
}
Comparación con Floating UI/Popper.js
| Dimensión | CSS Anchor + Popover | Floating UI |
|---|---|---|
| Dependencia JS | Cero JS | 12KB gzip |
| Rendimiento | Nativo del navegador | Cálculo JS |
| Declarativo | Declarativo CSS | Imperativo JS |
| Flip de límites | position-fallback | middleware flip() |
| Offset | margin/anchor() | middleware offset() |
| Flecha | Pseudo-elemento CSS | Elemento SVG/HTML |
| Elementos virtuales | No soportado | Soportado |
| Contenido dinámico | Auto-seguimiento | Necesita update() |
| Soporte de navegadores | Chrome 125+ | Todos los navegadores |
| Compatible con SSR | Totalmente compatible | Necesita JS del cliente |
Cuándo Aún Necesitas Librerías JS
Escenarios que aún requieren Floating UI:
1. Necesitas soportar navegadores antiguos (Safari < 17.5, Firefox < 131)
2. Necesitas posicionamiento de elementos virtuales (seguimiento del cursor)
3. Necesitas cadenas complejas de middleware (autoUpdate, size, hide)
4. Requisitos de wrapper declarativo para React/Vue
Escenarios donde CSS Anchor funciona:
1. Tooltip, Dropdown, Popover y otros popups estándar
2. Solo necesitas soportar navegadores modernos
3. Persiguiendo cero dependencia JS
4. Escenarios sensibles al rendimiento
Compatibilidad de Navegadores y Polyfill
Estado de Soporte
| Navegador | Anchor Positioning | Popover API |
|---|---|---|
| Chrome 125+ | ✅ | ✅ |
| Edge 125+ | ✅ | ✅ |
| Safari 17.5+ | ✅ | ✅ |
| Firefox 131+ | ✅ | ✅ |
| iOS Safari 17.5+ | ✅ | ✅ |
Soluciones de Polyfill
<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>
Combinación con @starting-style para Animaciones de Popup
[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);
}
}
Resumen
- CSS Anchor Positioning es la solución definitiva para el posicionamiento de popups — Cero JS, declarativo, nativo del navegador
- La Popover API hace que el toggle de popups también sea cero JS — el atributo popovertarget lo maneja en una línea
- position-fallback resuelve el desbordamiento de límites — No más cálculos JS para el flip
- Todos los navegadores principales lo soportan en 2026 — Seguro para usar en producción
Después de 10 años, el posicionamiento de popups ha vuelto finalmente a donde pertenece: CSS. Esto no es una mejora incremental — es un cambio de paradigma, tal como Flexbox reemplazó los layouts con float.
Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →
#CSS锚点定位#Anchor Positioning#Popover API#弹出层#CSS