Scroll suave con CSS scroll-behavior
Hacer clic en un enlace ancla y ver cómo la página salta instantáneamente a una sección resulta abrupto. Desorienta a los usuarios, rompe el flujo de lectura y hace que la navegación dentro de la página parezca poco pulida. La solución es una sola propiedad CSS, sin necesidad de ninguna librería JavaScript.
Puntos clave
- La propiedad CSS
scroll-behavior: smoothpermite el desplazamiento animado para enlaces ancla y APIs de scroll programático sin necesidad de JavaScript. - Aplícala al elemento
html, no abody, para que la propiedad se propague correctamente al viewport. - La curva de animación y la duración controladas por el navegador no pueden personalizarse mediante CSS; recurre a JavaScript solo cuando necesites un control más detallado.
- Usa
scroll-margin-toppara evitar que los encabezados fijos cubran los destinos de los anclas. - Envuelve la regla en una media query
prefers-reduced-motion: no-preferencepara respetar las preferencias de accesibilidad.
¿Qué es scroll-behavior y cómo funciona?
La propiedad CSS scroll-behavior controla cómo se mueve un contenedor de desplazamiento cuando el scroll se activa de forma programática, ya sea mediante enlaces ancla, navegación por hash o APIs de JavaScript como window.scrollTo() o element.scrollIntoView().
Acepta dos valores:
auto: el valor por defecto. Desplaza instantáneamente sin animación.smooth: anima el desplazamiento usando una función de aceleración y duración definidas por el navegador.
Una aclaración importante: scroll-behavior no afecta al desplazamiento provocado por la rueda del ratón, el trackpad o el arrastre de la barra de desplazamiento del usuario. Solo se aplica al desplazamiento activado por anclas y al programático.
La curva de animación y la duración están controladas en su totalidad por el navegador. No puedes personalizarlas únicamente con CSS. Si necesitas una duración específica o una curva de aceleración personalizada, requerirás una solución basada en JavaScript.
Añadir scroll suave con CSS a tu página
Aplica scroll-behavior: smooth al elemento html para habilitar la navegación con desplazamiento suave en toda la página:
html {
scroll-behavior: smooth;
}
Usa html, no body. Cuando se establece en el elemento raíz, la propiedad se aplica al viewport. Configurarla en body no se propaga al viewport, una fuente común de confusión cuando el scroll suave no funciona.
Esta única declaración gestiona automáticamente todo el desplazamiento de enlaces ancla. Sin JavaScript, sin dependencias.
Casos de uso prácticos
Navegación por tabla de contenidos:
<nav>
<a href="#intro">Introduction</a>
<a href="#usage">Usage</a>
<a href="#examples">Examples</a>
</nav>
Botón “Volver arriba”:
<a href="#top">↑ Back to top</a>
Para que el enlace de “volver arriba” llegue al inicio mismo de la página, asegúrate de que exista un elemento con id="top" al comienzo de la página, o utiliza href="#" como alternativa. Ambos patrones funcionan inmediatamente una vez que scroll-behavior: smooth está definido en html.
Gestión de encabezados fijos con scroll-margin-top
Si tu diseño incluye un encabezado fijo, los destinos de los anclas se desplazarán quedando parcialmente ocultos detrás de él. Soluciónalo con scroll-margin-top:
:target {
scroll-margin-top: 80px; /* match your header height */
}
Para una cobertura más amplia —incluyendo el desplazamiento programático a elementos que no son el :target actual— aplica la regla directamente a los propios elementos de sección:
section[id] {
scroll-margin-top: 80px;
}
Esto desplaza el punto donde el navegador aterriza al navegar a un elemento destino, sin necesidad de JavaScript.
Discover how at OpenReplay.com.
Accesibilidad: respetar prefers-reduced-motion
Algunos usuarios experimentan mareos por movimiento o malestar vestibular provocado por el desplazamiento animado. Respeta siempre la media query prefers-reduced-motion:
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}
Este patrón habilita el scroll suave únicamente para los usuarios que no han desactivado el movimiento en la configuración de su sistema operativo. Es el enfoque recomendado para una UX de desplazamiento accesible.
Compatibilidad con navegadores
scroll-behavior cuenta con soporte universal en todos los navegadores modernos: Chrome 61+, Firefox 36+, Edge 79+, Safari 15.4+ y Opera 48+. La compatibilidad global ronda el 95%, por lo que no se necesita ningún polyfill para proyectos actuales.
CSS vs. JavaScript: ¿cuál deberías usar?
| Necesidad | Mejor enfoque |
|---|---|
| Desplazamiento simple por enlaces ancla | CSS |
| Duración o aceleración personalizadas | JavaScript |
| Desplazamiento condicional o basado en lógica | JavaScript |
Para la mayoría de los escenarios de navegación dentro de la página —sitios de documentación, landing pages, portafolios— CSS es suficiente y preferible. Menos código, sin dependencias, rendimiento nativo del navegador.
Lista rápida de implementación
- ☐ Aplica
scroll-behavior: smoothahtml, no abody - ☐ Envuélvelo en
prefers-reduced-motion: no-preference - ☐ Añade
scroll-margin-toppara diseños con encabezado fijo - ☐ Prueba los enlaces ancla y la navegación con teclado
- ☐ Verifica el comportamiento en iOS Safari
Conclusión
El scroll suave con CSS es una de esas mejoras que cuesta casi nada implementar y que hace que la navegación dentro de la página se sienta inmediatamente más intencional. Una declaración, correctamente ubicada, cubre la mayoría de los casos de uso del mundo real sin tocar JavaScript.
Preguntas frecuentes
La propiedad scroll-behavior debe establecerse en el contenedor de desplazamiento que posee el viewport, que es el elemento html. Cuando se aplica a body, el valor no se propaga al scroller a nivel de viewport, por lo que el desplazamiento de enlaces ancla vuelve al salto instantáneo predeterminado. Mueve la regla a html y la animación suave funcionará como se espera.
No. La duración de la animación y la curva de aceleración están determinadas íntegramente por el navegador y no pueden personalizarse mediante CSS. Si tu diseño requiere una velocidad específica o una función de aceleración personalizada, necesitas una solución en JavaScript usando window.scrollTo con behavior smooth combinada con lógica de animación personalizada, o una pequeña librería que gestione el tiempo manualmente.
No. La propiedad solo se aplica al desplazamiento programático y al activado por anclas, como hacer clic en un enlace hash, llamar a scrollIntoView o invocar window.scrollTo. El desplazamiento iniciado por el usuario mediante la rueda del ratón, gestos en el trackpad, arrastre de la barra de desplazamiento o las teclas de flecha del teclado no se ve afectado y sigue utilizando el comportamiento de scroll nativo del navegador.
Usa la propiedad scroll-margin-top en el elemento destino con un valor igual a la altura de tu encabezado. Por ejemplo, establecer scroll-margin-top a 80px en elementos section con un id desplaza la posición de aterrizaje del scroll para que la sección aparezca debajo del encabezado fijo en lugar de quedar oculta detrás. No se requiere JavaScript ni marcado adicional.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.