overflow-anchor ya es Baseline: cómo evitar saltos de scroll sin romper CLS
overflow-anchor ya funciona en los navegadores principales. Explicamos cómo depurar saltos de scroll, proteger la lectura y medir el resultado sin romper CLS.
overflow-anchor ya es Baseline: cómo evitar saltos de scroll sin romper CLS
Sí: overflow-anchor ya es una opción de producción para controlar el anclaje de scroll, pero no corrige CLS por sí solo. El navegador puede conservar el punto que el usuario está leyendo cuando cambia el contenido fuera del viewport. overflow-anchor: none sirve para excluir una zona que está interfiriendo con ese comportamiento. La corrección depende de saber qué nodo está anclando el navegador y qué parte de la página está cambiando.
En nuestro trabajo de auditoría vemos un patrón que confunde a los equipos: el CLS puede verse limpio mientras el lector pierde el párrafo que estaba leyendo. La causa no siempre es una imagen sin width y height. También ocurre cuando un componente se inserta, se reordena o cambia de posición después de que el usuario ya se desplazó.
MDN ahora etiqueta el módulo como Baseline 2026, recién disponible. Los navegadores principales ya lo soportan, así que la decisión ya no es si conviene probarlo. La pregunta útil es: ¿en qué parte del documento hay que intervenir?
Qué problema resuelve realmente
El CSS Scroll Anchoring Module define un nodo de anclaje para cada contenedor de scroll. Cuando cambia el contenido por encima de la zona visible, el navegador puede ajustar el offset de scroll para que el elemento que la persona está leyendo permanezca en el mismo lugar de la pantalla.
MDN confirma que el comportamiento está activo por defecto en los navegadores compatibles. auto es el valor inicial. none excluye un elemento o un contenedor de la selección del ancla.
.article {
/* El navegador puede elegir un descendiente como ancla */
overflow-anchor: auto;
}
.dynamic-island {
/* Esta zona no debe competir por ser el ancla */
overflow-anchor: none;
}
El error habitual es añadir overflow-anchor: none a todo el documento "por si acaso". Eso elimina una protección nativa y deja que regrese el problema que intentabas corregir.
Por qué CLS puede permanecer en verde
CLS mide desplazamientos inesperados del contenido visible. No mide completamente si la persona perdió su lugar. Google explica en su guía de CLS que los cambios dentro de los 500 ms posteriores a una interacción no se cuentan. También diferencia el CLS de laboratorio del CLS de campo: CrUX observa la vida completa de la página, mientras que una carga inicial de Lighthouse puede no mostrar los cambios posteriores.
| Síntoma que ve el usuario | Qué puede estar pasando | Qué dice CLS |
|---|---|---|
| Salta un bloque al terminar el scroll | El nodo de anclaje cambió o el contenido se insertó fuera del viewport | Puede no contar |
| El párrafo se mueve después de una respuesta JSON | El contenido se actualiza en un contenedor y el ancla deja de ser estable | Puede quedar fuera si fue post-input |
| Una imagen sin dimensiones empuja todo | Falta espacio reservado antes de cargar | Suele contar |
Un cambio de position rompe el anclaje | El navegador desactiva el anclaje en esa zona | No siempre coincide con el CLS reportado |
Si el usuario dice "se me movió la lectura", no descartes el problema porque CLS esté en 0.08. Graba la sesión o usa el panel de Layout Shifts para encontrar el nodo afectado.
El patrón que cambia el diagnóstico
En el trabajo de auditoría, el patrón que más cambia nuestro diagnóstico aparece en páginas con filtros, tablas y tarjetas que se actualizan después del scroll. El CLS puede verse bajo y, aun así, la persona pierde la línea que estaba leyendo. No hace falta inventar un porcentaje para encontrar el problema: basta con reproducir el cambio y observar dónde termina el nodo de anclaje.
La primera hipótesis suele ser "faltan dimensiones". La tabla puede tener una altura calculada, pero el contenido se inserta dentro de un contenedor que se reordena al cambiar el estado. Si el nodo que el navegador puede elegir está dentro de una tarjeta animada, un cambio de tamaño puede suprimir el anclaje y dejar el offset de scroll donde estaba.
La secuencia para confirmar el diagnóstico es:
- Reservar la altura mínima del bloque que puede cambiar.
- Sacar la zona animada de la selección del ancla con
overflow-anchor: none. - Registrar la posición visual del párrafo antes y después de la actualización.
- Repetir la prueba con una conexión lenta y una ventana de móvil.
El segundo punto suele faltar. overflow-anchor no crea una reserva de espacio. Solo controla qué nodo puede usarse para compensar un cambio. Si el layout está mal dimensionado, desactivar el anclaje cambia el síntoma, no la causa.
Una tabla para decidir dónde usarlo
Después de comparar páginas con banners, cards animadas y filtros, este es el marco que usamos antes de tocar CSS:
| Componente | ¿Cambia fuera del viewport? | ¿El usuario está leyendo cerca? | Acción recomendada |
|---|---|---|---|
| Hero estático | No | No | Dejar auto |
| Card con imagen tardía | Sí, arriba | A veces | Reservar espacio y dejar auto |
| Banner animado sobre la lectura | Sí | Sí | Excluir la zona con none |
| Filtro que reemplaza resultados | Sí | Frecuentemente | Separar el bloque y reservar altura |
Panel con position: fixed | No | No | Revisar stacking y scroll |
| Contenido insertado por JavaScript | Sí | Variable | Medir el shift y corregir la causa primero |
La decisión no debería ser global. Es una pregunta por componente. Un carrusel puede quedar arriba del viewport y no necesitar none; el mismo carrusel insertado entre dos párrafos puede ser un pésimo ancla.
Cómo depurarlo en el navegador
La guía de Google para depurar layout shifts recomienda usar la Layout Instability API. La API solo está disponible en navegadores Chromium, pero sigue siendo el punto de partida más útil para identificar los nodos que se movieron.
Este snippet registra el valor y los elementos afectados sin contar los cambios que siguen a una interacción reciente:
let cls = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (entry.hadRecentInput) continue;
cls += entry.value;
const sources = entry.sources
?.map(({ node, previousRect, currentRect }) => ({
node: node?.id || node?.className || node?.tagName,
previousRect,
currentRect,
}))
.slice(0, 5);
console.table({ value: entry.value, startTime: entry.startTime, sources });
}
}).observe({ type: 'layout-shift', buffered: true });
No lo ejecutes en producción sin filtrar: el volumen de logs puede afectar la sesión. Úsalo en una build de diagnóstico o detrás de una bandera local.
El test que encuentra el nodo problemático
Reproduce el caso en una ventana con una altura parecida a la de un móvil. Desplázate hasta una línea concreta. Guarda la posición visual del párrafo. Inserta o cambia el bloque sospechoso. La variable importante no es scrollY; es la posición del texto en la pantalla.
const before = paragraph.getBoundingClientRect().top;
updateContent();
requestAnimationFrame(() => {
const after = paragraph.getBoundingClientRect().top;
console.log({ before, after, visualDelta: after - before });
});
Si visualDelta es cercano a cero, el scroll anclado está cumpliendo su trabajo. Si el número coincide con la altura del bloque insertado, el anclaje no está corrigiendo esa zona. Ahora tienes evidencia para decidir entre reservar espacio, excluir el ancla o rehacer el flujo del componente.
overflow-anchor: none no arregla CLS
La propiedad controla la estabilidad percibida durante cambios de contenido. No corrige imágenes sin dimensiones, fuentes que ocupan otra altura, iframes sin espacio reservado ni un contenedor que cambia de tamaño antes de que el usuario pueda leerlo.
Google distingue entre CLS de carga y CLS de campo. Los labs suelen capturar el bloque de carga; CrUX registra shifts a lo largo de la sesión. Un cambio al hacer scroll puede aparecer en el campo aunque Lighthouse no lo haya visto, y los cambios post-input tienen una ventana de gracia de 500 ms.
Por eso el orden correcto es:
- Encontrar el nodo que se mueve.
- Corregir dimensiones, espacio reservado o el flujo de layout.
- Medir el antes y después.
- Usar
overflow-anchor: nonesolo para una zona que interfiere con la lectura.
Poner * { overflow-anchor: none; } al principio del proyecto parece una política de rendimiento. En la práctica elimina el comportamiento nativo que mantiene estable el contenido mientras se carga. No lo hagas.
Un patrón para Astro y Next.js
En Astro, el HTML se genera antes de que la página llegue al navegador. El componente que puede cambiar debería tener una altura conocida o un contenedor que no se reinserte encima de la lectura:
<article>
<div class="article-copy">
<slot />
</div>
<aside class="related-content" aria-label="Contenido relacionado">
<slot name="related" />
</aside>
</article>
<style>
.related-content {
min-height: 18rem;
overflow-anchor: none;
}
</style>
En Next.js, no anclaría un Server Component que se reemplaza completo después de una mutación. Mantendría el shell y cambiaría solo el contenido:
export function ResultsPanel({ children }: { children: React.ReactNode }) {
return (
<section className="results-panel">
{children}
</section>
);
}
.results-panel {
min-height: 24rem;
contain: layout;
}
contain: layout puede acotar el reflow del panel, pero no reemplaza las dimensiones explícitas ni evita que un componente se monte dos veces. Úsalo junto con una estructura estable, no como un parche.
La decisión final: auto, none o corregir primero
| Situación | Decisión |
|---|---|
| El contenido cambia arriba y el lector sigue el texto | Dejar auto |
| El ancla seleccionada no representa la lectura | none solo en esa zona |
| El contenido no tiene dimensiones conocidas | Corregir dimensiones primero |
| Ocurre un shift después de una interacción | Auditar la interacción; puede no contar en CLS |
| CLS está bajo pero la lectura salta | Medir con getBoundingClientRect() |
Para conectar este diagnóstico con el resto de tu arquitectura, revisa nuestra guía de rendimiento cross-browser, el framework de presupuestos para medios sintéticos y la implementación de carga diferida de video y audio.
Scroll anchoring ya es Baseline. La noticia útil no es que la propiedad exista: el navegador nos da una pieza de infraestructura que antes tocaba replicar con scripts frágiles. Pero el mayor salto de calidad sigue estando en el HTML: una imagen con tamaño, un panel con altura mínima y un componente que no mueve el contenido que alguien está leyendo.
En la práctica, no activamos overflow-anchor como una feature. Diagnóstico primero, medición después. El código debería ser el último paso de una investigación, no el primer parche.
Fuentes
Preguntas Frecuentes
¿Qué hace overflow-anchor en CSS?
Permite excluir una zona del documento de la selección del nodo de anclaje del navegador. El scroll anchoring está activo por defecto; overflow-anchor: none desactiva esa protección solo en la zona que indicas.
¿Usar overflow-anchor mejora el CLS?
No siempre. Puede reducir saltos percibidos cuando aparece contenido fuera del viewport, pero CLS ignora los desplazamientos que ocurren dentro de los 500 ms posteriores a una interacción. La métrica y la experiencia de lectura no son la misma medida.
¿Cuándo debería usar overflow-anchor: none?
Úsalo cuando el anclaje automático elige un nodo que no representa lo que el usuario está leyendo, como un carrusel, un bloque animado o un panel con posición dinámica. No lo uses como una regla global para tapar un problema de layout.



