View Transitions API: animaciones entre páginas con CSS

Las animaciones web han pasado de ser un detalle decorativo a convertirse en una parte importante de la experiencia de usuario. Un cambio de estado, una navegación entre páginas o la apertura de una vista nueva pueden sentirse bruscos si todo aparece y desaparece de golpe. Por eso, durante años, muchas interfaces han intentado suavizar esos cambios con JavaScript, librerías de animación, rutas propias en frameworks SPA o trucos complejos de CSS.

La View Transitions API llega precisamente para simplificar ese escenario. Su objetivo es permitir transiciones entre vistas de una forma más nativa, fluida y controlada, tanto en aplicaciones de una sola página como en sitios multipágina. Dicho de otra forma: nos ayuda a crear animaciones entre páginas con CSS sin tener que reconstruir toda la lógica visual desde cero.

Si trabajas con interfaces modernas, blogs, portfolios, documentación, tiendas online o aplicaciones web, entender esta API puede abrirte una forma mucho más elegante de plantear las transiciones. No se trata de animar por animar, sino de mejorar la continuidad visual y ayudar al usuario a entender qué ha cambiado en pantalla.

Qué es la View Transitions API

La View Transitions API es una API del navegador que permite animar el cambio entre dos estados visuales de una página. Ese cambio puede ocurrir dentro de la misma página, como sucede en una SPA, o entre dos documentos distintos, como ocurre al navegar de una página HTML a otra dentro del mismo sitio.

La idea de base es muy interesante: el navegador captura una especie de “fotografía” del estado anterior y otra del estado nuevo. Después, permite animar la transición entre ambas capturas usando CSS. Así se evita tener que mantener manualmente el contenido antiguo y el nuevo al mismo tiempo dentro del DOM, algo que muchas veces terminaba generando código frágil, difícil de mantener y con posibles problemas de accesibilidad.

En términos prácticos, una view transition puede servir para animar:

  • El paso de una página de listado a una página de detalle.
  • El cambio entre temas claro y oscuro.
  • La reorganización de una galería.
  • La navegación entre secciones de una SPA.
  • La transición entre páginas de un blog o portfolio.
  • El cambio de estado de una tarjeta, modal o componente interactivo.

La clave está en que la API no pretende sustituir a todas las animaciones CSS tradicionales. Más bien, resuelve un problema muy concreto: animar cambios de vista completos o parciales sin tener que duplicar manualmente estructuras ni coordinar estados complejos con JavaScript.

Por qué son importantes las transiciones entre páginas CSS

Durante mucho tiempo, las páginas web tradicionales han tenido una navegación bastante abrupta: haces clic en un enlace, el navegador carga otra página y la pantalla cambia de golpe. Esto es perfectamente funcional, pero no siempre transmite continuidad.

Las SPA, por su parte, popularizaron una experiencia más parecida a las aplicaciones nativas. Al cambiar de ruta sin recargar el documento completo, podían añadir animaciones entre vistas, mantener elementos persistentes y hacer que el recorrido pareciera más fluido. El problema es que muchas veces esto implicaba añadir frameworks, routers, estados compartidos y lógica adicional solo para conseguir una transición más amable.

Aquí es donde las transiciones entre páginas CSS cobran valor. Con la View Transitions API, un sitio multipágina puede sentirse más fluido sin convertirse necesariamente en una SPA. Esto es especialmente interesante para proyectos como blogs técnicos, portfolios, documentación, landings o webs corporativas donde no siempre tiene sentido cargar un framework completo solo para animar la navegación.

Además, una buena transición puede mejorar la percepción de rendimiento. No significa que la página cargue mágicamente más rápido, pero sí puede hacer que el cambio resulte menos brusco y más comprensible. Cuando el usuario mantiene una referencia visual —por ejemplo, una imagen que se transforma desde una tarjeta hacia una cabecera de detalle— entiende mejor la relación entre origen y destino.

Continuidad visual y carga cognitiva

Una interfaz no solo debe funcionar: también debe explicar visualmente lo que está ocurriendo. Cuando todo cambia de golpe, el usuario necesita reconstruir el contexto. Tiene que identificar dónde está, qué elemento ha cambiado y cómo volver atrás si lo necesita.

Las transiciones bien diseñadas reducen esa carga cognitiva. No porque añadan “efectos bonitos”, sino porque ayudan a conectar dos estados de la interfaz. Una tarjeta que se expande, una imagen que conserva su posición relativa o un contenido que entra suavemente desde una dirección coherente son recursos visuales que guían la atención.

Eso sí, conviene recordar algo importante: una transición debe estar al servicio de la experiencia. Si ralentiza demasiado la navegación, distrae o se repite de forma excesiva, deja de ayudar. La View Transitions API CSS es potente, pero sigue necesitando criterio de diseño.

Cómo funciona una view transition en el navegador

Para entender la API, podemos imaginar el proceso en tres momentos:

  1. El navegador captura el estado visual actual.
  2. Se produce el cambio de contenido o navegación.
  3. El navegador captura el nuevo estado y anima la transición entre ambos.

Por defecto, si no personalizamos nada, el navegador puede aplicar una transición tipo fundido entre la vista anterior y la nueva. A partir de ahí, podemos intervenir con CSS mediante pseudo-elementos específicos como:

::view-transition-old(root)
::view-transition-new(root)
::view-transition-group(root)

Estos pseudo-elementos representan las capturas de la vista antigua y la vista nueva. Gracias a ellos podemos definir animaciones personalizadas, ajustar duraciones, aplicar transformaciones y controlar cómo aparece o desaparece cada estado.

La palabra root hace referencia a la transición principal de la página. Si queremos animar elementos concretos de forma independiente, podemos asignarles un nombre con view-transition-name.

.card-image {
  view-transition-name: featured-image;
}

Con esto le indicamos al navegador que ese elemento debe participar en una transición propia, separada de la transición general de la página. Es uno de los puntos más interesantes de la API, porque permite crear efectos tipo “elemento compartido” entre una página de listado y una página de detalle.

El papel de view-transition-name

La propiedad view-transition-name permite identificar elementos que queremos animar de manera específica durante una transición. Por ejemplo, imagina una página de artículos donde cada tarjeta tiene una imagen destacada. Al hacer clic en una tarjeta, esa imagen podría transformarse suavemente en la imagen principal del post.

.post-card__image {
  view-transition-name: post-image;
}

.post-header__image {
  view-transition-name: post-image;
}

Para que esto funcione correctamente, el nombre debe ser coherente entre el estado anterior y el nuevo. La API puede relacionar ambos elementos y generar una transición más natural.

Sin embargo, hay una regla importante: no conviene repetir el mismo view-transition-name en varios elementos visibles al mismo tiempo. Si varias piezas comparten el mismo identificador en el mismo estado, el navegador no puede saber cuál debe asociar con cuál. Por eso, en listados dinámicos, suele ser necesario generar nombres únicos.

.card-article-1 {
  view-transition-name: article-1;
}

.card-article-2 {
  view-transition-name: article-2;
}

En proyectos reales, esos nombres suelen generarse a partir de un identificador del contenido, como el slug de un artículo o el ID de un producto.

View Transitions API en SPA: animaciones SPA CSS con JavaScript mínimo

En una SPA, el cambio de vista ocurre dentro del mismo documento. No hay una navegación tradicional a otro HTML, sino una actualización del DOM. Para estos casos se utiliza document.startViewTransition().

Un ejemplo básico podría ser este:

function updateView() {
  const container = document.querySelector(".content");

  container.innerHTML = `
    <article>
      <h2>Nueva vista</h2>
      <p>Contenido actualizado con una transición suave.</p>
    </article>
  `;
}

if (document.startViewTransition) {
  document.startViewTransition(() => updateView());
} else {
  updateView();
}

Aquí hay dos ideas importantes. La primera es que el cambio del DOM se ejecuta dentro de la función que pasamos a startViewTransition(). La segunda es que añadimos una condición para comprobar si el navegador soporta la API. Si no la soporta, simplemente actualizamos la vista sin transición.

Este enfoque es muy útil para crear animaciones SPA CSS sin depender de librerías externas. Puedes integrarlo en una aplicación con React, Vue, Svelte, Astro con islands, JavaScript vanilla o cualquier arquitectura donde el contenido cambie sin recargar todo el documento.

Personalizar la transición con CSS

Una vez activada la transición, podemos definir cómo se animan la vista antigua y la nueva:

::view-transition-old(root) {
  animation: fade-out 0.25s ease both;
}

::view-transition-new(root) {
  animation: fade-in 0.35s ease both;
}

@keyframes fade-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Este ejemplo combina opacity y transform, dos propiedades recomendables cuando buscamos animaciones fluidas. En general, es mejor evitar animar propiedades como width, height, top o left si no es necesario, porque pueden provocar recálculos de layout y afectar al rendimiento.

La View Transitions API no elimina las buenas prácticas de animación CSS: las hace todavía más importantes.

View Transitions API en MPA: transiciones entre páginas reales

Una de las partes más atractivas de esta API es su capacidad para trabajar con sitios multipágina. Es decir, webs donde cada URL carga un documento diferente. En estos casos, no llamamos a document.startViewTransition() manualmente. En su lugar, activamos las transiciones desde CSS con la regla @view-transition.

@view-transition {
  navigation: auto;
}

Esta regla debe estar presente en las páginas que participan en la navegación. Por ejemplo, si quieres animar el paso entre /blog/ y /blog/view-transitions-api/, ambas páginas deberían incluir esta regla en su CSS.

A partir de ahí, el navegador puede aplicar una transición cuando el usuario navega entre documentos del mismo origen. Esto resulta muy interesante para sitios creados con Astro, Eleventy, WordPress, HTML estático o cualquier arquitectura multipágina.

Ejemplo de transición entre páginas

Podemos empezar con una animación general para toda la página:

@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: page-out 0.3s ease both;
}

::view-transition-new(root) {
  animation: page-in 0.35s ease both;
}

@keyframes page-out {
  from {
    opacity: 1;
    transform: translateY(0);
  }

  to {
    opacity: 0;
    transform: translateY(-16px);
  }
}

@keyframes page-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Con muy poco código, un sitio multipágina puede ganar una sensación de continuidad mucho mayor. El cambio sigue siendo una navegación real, pero visualmente se percibe más suave.

Limitaciones importantes en navegación multipágina

Aunque esta API abre muchas posibilidades, no conviene aplicarla sin entender sus límites. En transiciones entre documentos, las páginas deben pertenecer al mismo origen. Además, la navegación tiene que cumplir ciertas condiciones para que el navegador pueda coordinar la transición.

También hay que tener en cuenta que la API es progresiva. Si no está disponible en un navegador concreto, la web debe seguir funcionando perfectamente. Esto es una ventaja: podemos diseñar la experiencia base sin animación y añadir la transición como mejora progresiva.

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

Respetar prefers-reduced-motion es fundamental. Algunas personas prefieren reducir las animaciones por comodidad, accesibilidad o sensibilidad al movimiento. Una transición entre páginas no debería imponerse a toda costa.

Diferencias entre animaciones tradicionales y View Transitions API CSS

Las animaciones tradicionales con CSS funcionan muy bien cuando queremos animar un elemento que ya existe en pantalla: un botón, un menú, una tarjeta, un tooltip o un loader. Podemos usar transition, @keyframes, transform, opacity y muchas otras propiedades.

La View Transitions API CSS, en cambio, se centra en animar el cambio entre dos estados de la interfaz. No anima únicamente un elemento aislado, sino la relación visual entre una vista anterior y una vista nueva.

La diferencia es sutil, pero importante:

  • Una transición CSS tradicional anima un cambio de propiedad.
  • Una view transition anima un cambio de vista.
  • Una SPA puede usarla para cambios internos de contenido.
  • Una MPA puede usarla para suavizar navegaciones reales entre páginas.

Esto no significa que una opción sustituya a la otra. De hecho, lo habitual será combinarlas. Puedes seguir usando transition para microinteracciones y reservar la View Transitions API para cambios de vista más estructurales.

Buenas prácticas para usar view transition en proyectos reales

Antes de añadir transiciones entre páginas a todo un sitio, conviene definir una pequeña estrategia. Animar cada navegación de forma diferente puede parecer atractivo al principio, pero también puede crear una experiencia confusa.

Mantén las animaciones breves

Una transición entre páginas debería ser rápida. Como referencia general, duraciones entre 200 y 400 milisegundos suelen funcionar bien para cambios frecuentes. Si la animación dura demasiado, el usuario puede sentir que la interfaz va lenta.

::view-transition-group(root) {
  animation-duration: 0.3s;
}

La animación debe acompañar, no bloquear.

Usa movimiento con intención

No todos los cambios necesitan desplazamientos grandes. A veces, un simple fundido con una ligera traslación es suficiente. Si un contenido entra desde la derecha, por ejemplo, debería haber una lógica espacial detrás: avance, detalle, navegación lateral, siguiente paso, etc.

El movimiento comunica jerarquía. Por eso, una transición mal planteada puede confundir más de lo que ayuda.

Evita animar demasiados elementos a la vez

La API permite animar elementos concretos con view-transition-name, pero eso no significa que debas nombrarlo todo. Si cada imagen, título, botón y bloque de texto intenta tener una transición propia, el resultado puede parecer caótico.

Lo más efectivo suele ser elegir uno o dos elementos protagonistas:

.article-card__image {
  view-transition-name: article-image;
}

.article-title {
  view-transition-name: article-title;
}

Una imagen destacada y un título pueden ser suficientes para conectar visualmente una tarjeta con su página de detalle.

Diseña siempre una experiencia sin animación

La mejora progresiva debe ser la base. Tu sitio debe poder navegarse, leerse y utilizarse aunque la View Transitions API no esté disponible. Esto implica no depender de la animación para mostrar información esencial.

Una buena práctica es construir primero la experiencia normal y después añadir la transición encima. Así evitas que la interfaz dependa de una característica concreta del navegador.

Casos de uso recomendados

La View Transitions API encaja especialmente bien en interfaces donde existe una relación clara entre dos vistas.

Listado y detalle

Es uno de los casos más naturales. Un usuario hace clic en una tarjeta de un listado y llega a una página de detalle. Si la imagen, el título o una etiqueta se mantienen como referencia visual, la transición ayuda a entender que el contenido se ha expandido.

Esto puede aplicarse a blogs, portfolios, catálogos, recetas, cursos, documentación o productos.

Cambio de tema visual

Otro caso interesante es el cambio entre modo claro y oscuro. En lugar de alternar los colores de golpe, podemos envolver el cambio de clase dentro de startViewTransition().

const button = document.querySelector(".theme-toggle");

button.addEventListener("click", () => {
  const changeTheme = () => {
    document.documentElement.classList.toggle("dark");
  };

  if (document.startViewTransition) {
    document.startViewTransition(changeTheme);
  } else {
    changeTheme();
  }
});

Con CSS, después podemos suavizar el cambio visual. Eso sí, conviene no exagerar: en un cambio de tema, la transición debe ser elegante y rápida.

Galerías y filtros

Cuando una galería cambia de orden, aplica filtros o muestra nuevos elementos, una view transition puede hacer que el cambio resulte más comprensible. En lugar de que los elementos salten bruscamente de lugar, el usuario percibe una reorganización más natural.

En estos casos, la propiedad view-transition-name puede ser especialmente útil si cada elemento tiene un identificador único.

Errores comunes al trabajar con View Transitions API

Uno de los errores más habituales es pensar que esta API convierte cualquier animación en “automática”. No es así. El navegador facilita la captura y la transición, pero el diseño de la animación sigue dependiendo de nosotras.

Otro error común es usar la API para maquillar problemas de rendimiento. Si una página carga lenta, tiene imágenes pesadas o bloquea el hilo principal con JavaScript, una transición puede suavizar la percepción, pero no soluciona el problema de fondo.

También conviene evitar transiciones demasiado protagonistas. Una navegación no debería convertirse en un espectáculo visual cada vez que el usuario hace clic. En interfaces de uso frecuente, la repetición excesiva puede cansar.

Por último, es importante probar en dispositivos reales. Una animación que se ve perfecta en un equipo potente puede sentirse menos fluida en un móvil con menos recursos. La experiencia debe evaluarse en condiciones realistas.

SEO y experiencia de usuario: por qué esta API también importa

Desde el punto de vista SEO, la View Transitions API no posiciona por sí sola. Google no va a premiar una página únicamente porque tenga animaciones entre vistas. Sin embargo, sí puede influir indirectamente en la experiencia de usuario.

Una navegación más agradable puede ayudar a que el usuario explore más contenido, entienda mejor la arquitectura del sitio y perciba la web como más cuidada. En un blog técnico, por ejemplo, una transición sutil entre el listado de artículos y la página de detalle puede reforzar la sensación de continuidad sin sacrificar rendimiento ni accesibilidad.

Además, al poder aplicarse en sitios multipágina, esta API permite conservar ventajas tradicionales de arquitectura web —URLs claras, HTML renderizado, navegación estándar— mientras se añade una capa de interacción más moderna.

La clave está en no confundir animación con valor. Una transición bien pensada mejora la experiencia. Una transición innecesaria solo añade ruido.

Ejemplo completo básico

Este sería un ejemplo mínimo para una web multipágina:

@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: exit-page 0.25s ease both;
}

::view-transition-new(root) {
  animation: enter-page 0.35s ease both;
}

@keyframes exit-page {
  from {
    opacity: 1;
    transform: scale(1);
  }

  to {
    opacity: 0;
    transform: scale(0.98);
  }
}

@keyframes enter-page {
  from {
    opacity: 0;
    transform: scale(1.02);
  }

  to {
    opacity: 1;
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

Este código activa la transición, define una salida suave, una entrada ligera y respeta las preferencias de movimiento reducido. Es una base sencilla, pero suficiente para empezar a experimentar.

Preguntas frecuentes sobre View Transitions API

¿La View Transitions API sustituye a las animaciones CSS tradicionales?

No. La View Transitions API no sustituye a transition, animation o @keyframes. Más bien, los complementa. Las animaciones CSS tradicionales siguen siendo ideales para microinteracciones, botones, menús, loaders o estados hover. La View Transitions API está pensada para animar cambios de vista más amplios, como una navegación entre páginas o una actualización importante del DOM.

¿Puedo usar View Transitions API en una web hecha con WordPress o Astro?

Sí, especialmente en sitios multipágina donde puedas añadir CSS global. Para transiciones entre páginas reales, la regla @view-transition { navigation: auto; } puede ser una forma sencilla de empezar. En proyectos con Astro, WordPress, Eleventy o HTML estático, esta API resulta muy interesante porque permite conseguir una sensación más fluida sin convertir toda la web en una SPA.

¿Es recomendable usar view transition en todas las páginas?

No necesariamente. Lo ideal es usarla donde aporte claridad y continuidad. Por ejemplo, entre un listado y una página de detalle, entre secciones relacionadas o en cambios de estado visual importantes. Si se aplica de forma indiscriminada, puede terminar siendo repetitiva o molesta. Como casi siempre en diseño de interacción, menos puede ser más.

Reflexión final: animar para orientar, no para decorar

La View Transitions API representa un paso muy interesante en la evolución de las animaciones web. Durante años, crear transiciones fluidas entre páginas requería soluciones complejas, dependencias externas o arquitecturas SPA que no siempre eran necesarias. Ahora, el navegador empieza a ofrecernos herramientas más nativas para resolver ese problema con menos fricción.

Pero la verdadera potencia de esta API no está en hacer que todo se mueva. Está en permitir que el movimiento tenga sentido. Una buena transición ayuda al usuario a mantener el contexto, comprender la relación entre dos vistas y sentir que la interfaz responde de forma cuidada.

Por eso, al trabajar con View Transitions API CSS, conviene hacerse una pregunta antes de escribir código: ¿esta animación ayuda a entender mejor la experiencia o solo añade efecto visual? Si la respuesta es la primera, probablemente estás ante un buen caso de uso.

Las mejores animaciones web no son las más llamativas, sino las que hacen que la navegación parezca natural. Y ahí es donde esta API puede convertirse en una herramienta muy valiosa para crear interfaces modernas, fluidas y más humanas.

Animaciones CSS con animation-timeline

Las animaciones CSS han dejado de ser simples efectos decorativos para convertirse en una parte importante de la experiencia de usuario. Durante mucho tiempo, cuando queríamos crear animaciones vinculadas al desplazamiento de la página, lo habitual era recurrir a JavaScript: escuchar el evento de scroll, calcular la posición del usuario, añadir clases dinámicamente y sincronizar cada movimiento con el avance de la navegación.

Ese enfoque sigue siendo válido en muchos casos, pero también puede añadir complejidad, afectar al rendimiento y hacer que una interacción visual sencilla dependa de demasiado código. Por eso resulta tan interesante la llegada de animation-timeline en CSS, una propiedad que permite controlar el progreso de una animación a partir de una línea de tiempo distinta al tiempo tradicional.

Dicho de una forma sencilla: con animation-timeline, una animación ya no tiene por qué avanzar únicamente durante una cantidad fija de segundos. También puede avanzar según el scroll de la página, el desplazamiento de un contenedor o la visibilidad de un elemento dentro del viewport.

Esto abre la puerta a una nueva generación de animaciones CSS modernas, mucho más conectadas con la interacción real del usuario. Podemos crear barras de progreso de lectura, revelados suaves de contenido, efectos de entrada, transformaciones progresivas o pequeñas microinteracciones sin depender siempre de JavaScript.

En este artículo vamos a ver qué es animation-timeline CSS, cómo funciona una CSS animation timeline, qué relación tiene con scroll timeline CSS y cómo utilizar esta técnica con criterio, accesibilidad y buen rendimiento.

Qué es animation-timeline en CSS

La propiedad animation-timeline permite definir qué línea de tiempo controla el progreso de una animación CSS. En una animación tradicional, lo habitual es trabajar con una duración concreta:

.elemento {
  animation: aparecer 1s ease-out forwards;
}

En este ejemplo, la animación empieza, avanza durante un segundo y termina. Es una animación basada en tiempo.

Con animation-timeline, la lógica cambia. La animación puede avanzar en función de otro tipo de timeline, por ejemplo, la visibilidad del elemento en pantalla:

.elemento {
  animation: aparecer linear both;
  animation-timeline: view();
}

Aquí ya no estamos diciendo que la animación dure un segundo. Estamos indicando que el avance de la animación se relaciona con la presencia del elemento dentro del área visible del navegador.

Esta diferencia es importante porque permite crear efectos mucho más naturales. La animación responde a lo que hace el usuario, no a un temporizador fijo.

Si estás empezando a trabajar con movimiento en interfaces, te recomiendo complementar este tema con la guía sobre animaciones CSS desde cero, donde se explican los fundamentos antes de pasar a técnicas más avanzadas.

De las animaciones temporales a las animaciones guiadas por scroll

Las animaciones CSS clásicas funcionan muy bien para muchos casos. Por ejemplo, un botón puede cambiar de color en 300ms, una tarjeta puede aparecer en 600ms o un icono puede rotar suavemente al hacer hover.

Ese tipo de animaciones siguen siendo útiles, especialmente cuando queremos responder a una acción concreta. Sin embargo, hay situaciones en las que una duración fija no representa bien la interacción.

Pensemos en una barra de progreso de lectura. No queremos que avance automáticamente durante dos segundos. Queremos que avance según el usuario recorre el contenido. Lo mismo ocurre con una sección que se revela al entrar en pantalla: tiene más sentido que la animación dependa de su posición en el viewport que de una duración fija al cargar la página.

Ahí entran las scroll-driven animations, es decir, animaciones impulsadas por el scroll. En este modelo, el desplazamiento controla el progreso de la animación. Si el usuario baja, la animación avanza. Si sube, retrocede. Si se detiene, la animación también se detiene.

Para profundizar en este concepto, puedes enlazar este artículo con una guía específica sobre scroll-driven animations: qué son y cómo usarlas, ya que ambos temas están muy relacionados.

Conceptos clave antes de escribir código

Antes de usar animation-timeline, conviene tener claros algunos conceptos:

Timeline: es la línea de tiempo que controla el progreso de una animación. Puede estar basada en tiempo, scroll o visibilidad.

Scroll progress timeline: es una línea de tiempo basada en el progreso de desplazamiento de un contenedor.

View progress timeline: es una línea de tiempo basada en la visibilidad de un elemento dentro del área visible.

scroll(): función que crea una timeline anónima vinculada al scroll de un contenedor.

view(): función que crea una timeline basada en la visibilidad del propio elemento animado.

animation-range: propiedad complementaria que permite definir en qué tramo de la timeline debe ejecutarse la animación.

Estos términos pueden sonar técnicos al principio, pero se entienden mucho mejor cuando los vemos aplicados a ejemplos reales.

Sintaxis básica de animation-timeline

La estructura habitual combina tres partes: una animación definida con @keyframes, la propiedad animation y la propiedad animation-timeline.

@keyframes aparecer {
  from {
    opacity: 0;
    transform: translateY(2rem);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.elemento {
  animation: aparecer linear both;
  animation-timeline: view();
}

En este caso, @keyframes define los estados de la animación. La propiedad animation indica qué animación se aplica, con qué interpolación y con qué comportamiento de relleno. Finalmente, animation-timeline: view(); indica que la animación se vincula a la visibilidad del elemento.

Un detalle importante: conviene declarar animation-timeline después del shorthand animation. Esto se debe a que animation puede resetear algunas propiedades relacionadas con la animación. Si escribimos primero animation-timeline y después animation, es posible que la timeline no se aplique como esperamos.

La forma recomendada sería:

.card {
  animation: fade-in linear both;
  animation-timeline: view();
}

Y no esta:

.card {
  animation-timeline: view();
  animation: fade-in linear both;
}

Este pequeño detalle puede evitar errores difíciles de detectar.

Ejemplo 1: barra de progreso de lectura con scroll()

Uno de los usos más claros de animation-timeline CSS es crear una barra de progreso que avance a medida que el usuario hace scroll por un artículo.

<div class="reading-progress"></div>
.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 0.35rem;
  transform-origin: left center;
  transform: scaleX(0);
  z-index: 9999;

  animation: progress linear both;
  animation-timeline: scroll(root block);
}

@keyframes progress {
  to {
    transform: scaleX(1);
  }
}

En este ejemplo, la animación progress modifica la escala horizontal de la barra. Al principio, scaleX(0) hace que la barra no se vea. Al final, scaleX(1) hace que ocupe todo el ancho disponible.

La clave está en esta línea:

animation-timeline: scroll(root block);

root indica que la timeline se basa en el scroll principal del documento. block indica que se tiene en cuenta el eje de bloque, que normalmente corresponde al desplazamiento vertical en páginas escritas de arriba hacia abajo.

Este tipo de efecto es muy útil en artículos largos, documentación técnica, tutoriales y páginas editoriales. Aporta una referencia visual clara sin interrumpir la lectura.

Además, desde el punto de vista del rendimiento, es preferible animar transform en lugar de modificar directamente el ancho con width. Si quieres profundizar en este criterio, puedes leer el artículo sobre por qué animar transform y opacity antes que width o height.

Ejemplo 2: revelar tarjetas al entrar en pantalla con view()

Otro uso habitual de animation-timeline es animar elementos cuando entran en el viewport. Antes, este patrón solía resolverse con JavaScript y la API Intersection Observer. Ahora, en muchos casos, podemos hacerlo solo con CSS.

<section class="grid">
  <article class="card">Contenido 1</article>
  <article class="card">Contenido 2</article>
  <article class="card">Contenido 3</article>
</section>
.card {
  opacity: 0;
  transform: translateY(3rem) scale(0.96);

  animation: reveal-card linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 35%;
}

@keyframes reveal-card {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

Aquí la animación no se ejecuta simplemente al cargar la página. Se vincula a la visibilidad de cada .card. Cuando el elemento entra en pantalla, la animación avanza.

La propiedad animation-range permite ajustar el tramo en el que se desarrolla la animación:

animation-range: entry 0% cover 35%;

Esto indica que la animación empieza cuando el elemento entra en el área visible y progresa durante una parte concreta de su recorrido. El resultado es una aparición más controlada, sin movimientos demasiado bruscos ni animaciones innecesariamente largas.

Este patrón funciona muy bien para listados de proyectos, bloques de servicios, secciones de landing page o tarjetas de contenido.

Ejemplo 3: una timeline con nombre usando scroll-timeline

Además de usar timelines anónimas con scroll() o view(), también podemos crear timelines con nombre. Esto resulta útil cuando varios elementos deben depender del mismo contenedor de scroll o cuando queremos que el CSS sea más explícito.

<div class="scroller">
  <div class="content">
    <div class="box"></div>
  </div>
</div>
.scroller {
  height: 400px;
  overflow-y: auto;
  scroll-timeline: --panel-scroll block;
}

.box {
  width: 120px;
  height: 120px;

  animation: rotate-box linear both;
  animation-timeline: --panel-scroll;
}

@keyframes rotate-box {
  to {
    transform: rotate(360deg);
  }
}

En este ejemplo, .scroller define una timeline llamada --panel-scroll. Después, .box utiliza esa timeline para controlar su animación.

Este enfoque puede ser más claro en proyectos grandes, porque permite nombrar la timeline y reutilizarla en diferentes elementos.

Diferencia entre scroll() y view()

Aunque scroll() y view() se utilizan con animation-timeline, no sirven exactamente para lo mismo.

Cuándo usar scroll()

Conviene usar scroll() cuando la animación depende del progreso general de desplazamiento de un contenedor.

Algunos casos habituales son:

  • Barras de progreso de lectura.
  • Indicadores de avance en una página.
  • Animaciones vinculadas al scroll completo de un contenedor.
  • Transformaciones progresivas desde el inicio hasta el final de una sección.

Ejemplo:

.progress {
  animation: grow linear both;
  animation-timeline: scroll(root block);
}

En este caso, la animación se controla mediante el scroll global de la página.

Cuándo usar view()

Conviene usar view() cuando la animación depende de la visibilidad de un elemento concreto.

Algunos casos habituales son:

  • Tarjetas que aparecen al entrar en pantalla.
  • Imágenes que se revelan progresivamente.
  • Títulos que se desplazan suavemente al hacerse visibles.
  • Elementos decorativos que reaccionan cuando pasan por el viewport.

Ejemplo:

.image {
  animation: reveal linear both;
  animation-timeline: view();
  animation-range: entry 10% cover 40%;
}

En este caso, la animación se controla por la relación entre el elemento y el área visible.

Una regla práctica para decidir

Puedes quedarte con esta idea:

Si quieres animar algo según el avance total del scroll, usa scroll().

Si quieres animar algo según cuándo aparece en pantalla, usa view().

Esta diferencia ayuda mucho a elegir la solución correcta y evita complicar el CSS sin necesidad.

Buenas prácticas para usar animaciones CSS modernas

animation-timeline es una herramienta potente, pero no conviene usarla sin criterio. Una animación puede mejorar mucho una interfaz, pero también puede empeorarla si añade ruido, distrae o dificulta la lectura.

Prioriza transform y opacity

Para conseguir animaciones más fluidas, es recomendable priorizar propiedades como:

transform
opacity

Estas propiedades suelen ser más eficientes que animar width, height, top, left, margin o padding, porque reducen el riesgo de provocar recálculos de layout costosos.

Por ejemplo, para una barra de progreso es mejor usar:

transform: scaleX();

en lugar de animar directamente:

width

Visualmente pueden parecer soluciones parecidas, pero a nivel de rendimiento no siempre lo son. Este criterio también se relaciona con otras buenas prácticas que puedes ampliar en el artículo sobre animaciones CSS y rendimiento.

Usa animaciones con intención

Una animación debería cumplir una función concreta. Puede guiar la atención, reforzar una jerarquía visual, explicar una relación entre elementos o hacer más fluida una transición.

Lo que conviene evitar son las animaciones gratuitas. Si cada bloque entra con un efecto exagerado, si todo se mueve al mismo tiempo o si el scroll se vuelve incómodo, la experiencia se resiente.

Una buena animación CSS moderna debería sentirse natural. Está ahí para acompañar, no para robar protagonismo.

Si tienes dudas sobre cuándo aplicar movimiento y cuándo simplificar, puedes enlazar este tema con el artículo sobre cuándo usar animaciones CSS y cuándo evitarlas.

Respeta prefers-reduced-motion

No todas las personas disfrutan de las animaciones. Algunas pueden experimentar incomodidad, mareo o fatiga visual con movimientos constantes. Por eso es importante respetar la preferencia del sistema prefers-reduced-motion.

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }

  .card,
  .image,
  .section-title {
    animation-timeline: auto;
    transform: none;
    opacity: 1;
  }
}

Este bloque reduce o desactiva animaciones para las personas que han indicado esa preferencia en su sistema operativo o navegador.

La accesibilidad no es un añadido final. Es parte de una buena implementación front-end. Por eso, cualquier técnica moderna de animación debería plantearse siempre desde la mejora progresiva y el respeto a las preferencias del usuario.

Añade fallbacks con @supports

Como animation-timeline es una característica moderna, conviene ofrecer una experiencia alternativa en navegadores que no la soporten completamente.

Una buena forma de hacerlo es utilizar @supports:

.card {
  opacity: 1;
  transform: none;
}

@supports (animation-timeline: view()) {
  .card {
    opacity: 0;
    transform: translateY(2rem);
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 35%;
  }
}

Así nos aseguramos de que, si el navegador no entiende animation-timeline, el contenido siga siendo visible y usable.

Este punto es fundamental: el contenido nunca debería depender de una animación para poder verse.

Errores frecuentes al usar animation-timeline

Aunque la sintaxis no es excesivamente compleja, hay algunos errores habituales que conviene evitar.

Declarar animation-timeline antes de animation

Como el shorthand animation puede resetear algunas propiedades, es mejor declarar animation-timeline después.

Correcto:

.element {
  animation: reveal linear both;
  animation-timeline: view();
}

Incorrecto:

.element {
  animation-timeline: view();
  animation: reveal linear both;
}

Este error puede hacer que la timeline no se aplique correctamente y que la animación se comporte como una animación temporal tradicional.

Olvidar que sigue haciendo falta @keyframes

animation-timeline no crea una animación por sí sola. Solo define qué timeline controla su progreso. Para que haya un cambio visual, necesitamos seguir usando @keyframes.

@keyframes fade-in {
  to {
    opacity: 1;
  }
}

Sin una animación definida, no hay nada que ejecutar.

Animar demasiados elementos a la vez

Aunque CSS permita crear efectos muy llamativos, conviene limitar la cantidad de elementos animados simultáneamente. Una página con demasiadas animaciones vinculadas al scroll puede sentirse pesada, especialmente en dispositivos móviles o equipos con menos potencia.

La recomendación práctica es empezar con pocos efectos, probar la experiencia real y ajustar según el contexto.

No probar en diferentes navegadores

Las animaciones CSS modernas avanzan rápido, pero no siempre llegan igual a todos los navegadores. Antes de utilizar animation-timeline en producción, conviene comprobar la compatibilidad, probar en dispositivos reales y asegurar que el contenido sigue siendo accesible si la animación no se ejecuta.

Ejemplo completo: sección editorial animada con CSS

Veamos ahora un ejemplo más completo. Imaginemos una sección editorial con texto e imagen.

<section class="feature">
  <div class="feature__content">
    <p class="feature__eyebrow">CSS moderno</p>
    <h2 class="feature__title">Animaciones vinculadas al scroll</h2>
    <p class="feature__text">
      Crea efectos progresivos sin depender de JavaScript para cada interacción visual.
    </p>
  </div>

  <img class="feature__image" src="imagen.jpg" alt="Interfaz con animación CSS">
</section>
.feature {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
  align-items: center;
  padding: 6rem 2rem;
}

.feature__content,
.feature__image {
  opacity: 0;
  transform: translateY(3rem);
  animation: reveal-feature linear both;
  animation-timeline: view();
  animation-range: entry 10% cover 40%;
}

.feature__image {
  transform: translateY(3rem) scale(0.96);
}

@keyframes reveal-feature {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (max-width: 768px) {
  .feature {
    grid-template-columns: 1fr;
  }
}

Este ejemplo crea una entrada suave para el contenido y la imagen. La animación está vinculada a la visibilidad de cada elemento, no al momento de carga de la página.

El resultado es una experiencia más natural: el contenido aparece cuando tiene sentido, no antes.

Mejora progresiva aplicada al ejemplo

Para que el ejemplo sea más robusto, podemos envolver la parte avanzada con @supports:

.feature__content,
.feature__image {
  opacity: 1;
  transform: none;
}

@supports (animation-timeline: view()) {
  .feature__content,
  .feature__image {
    opacity: 0;
    transform: translateY(3rem);
    animation: reveal-feature linear both;
    animation-timeline: view();
    animation-range: entry 10% cover 40%;
  }
}

De esta manera, los navegadores sin soporte verán el contenido de forma normal. Los navegadores compatibles disfrutarán de la animación.

Este enfoque es muy recomendable para proyectos reales, porque combina innovación con estabilidad.

SEO, UX y rendimiento: por qué importan estas animaciones

Desde el punto de vista SEO, una animación no posiciona por sí sola. Google no va a premiar una página únicamente porque utilice animation-timeline CSS. Sin embargo, una buena experiencia de usuario sí puede influir indirectamente en cómo se percibe el contenido.

Una página clara, fluida, accesible y agradable puede favorecer la lectura, reducir la fricción y ayudar a que las personas permanezcan más tiempo interactuando con el contenido. Pero para lograrlo, las animaciones deben estar al servicio del mensaje.

En artículos técnicos, por ejemplo, una barra de progreso puede ayudar al lector a orientarse. En un portfolio, una animación de entrada puede presentar los proyectos de forma más cuidada. En una landing page, una transición sutil puede reforzar la narrativa visual.

La clave está en usar las animaciones como parte del diseño, no como decoración añadida al final.

Criterios para decidir si una animación aporta valor

Antes de implementar un efecto con animation-timeline, puedes hacerte estas preguntas:

  • ¿Ayuda a entender mejor la interfaz?
  • ¿Refuerza la jerarquía visual?
  • ¿Acompaña una acción del usuario?
  • ¿Funciona bien en móvil?
  • ¿Sigue siendo usable si la animación no se ejecuta?
  • ¿Respeta las preferencias de accesibilidad?

Si la respuesta es sí, probablemente la animación tenga sentido. Si la respuesta es no, quizá conviene simplificar.

Preguntas frecuentes sobre animation-timeline CSS

¿Qué es animation-timeline en CSS?

animation-timeline es una propiedad CSS que permite indicar qué línea de tiempo controla una animación. En lugar de depender únicamente del paso del tiempo, una animación puede progresar según el scroll de la página, el scroll de un contenedor o la visibilidad de un elemento.

Esto permite crear efectos como barras de progreso, revelados al entrar en pantalla o animaciones vinculadas al desplazamiento sin tener que recurrir siempre a JavaScript.

¿Cuál es la diferencia entre scroll() y view()?

scroll() se utiliza cuando queremos que la animación dependa del progreso de desplazamiento de un contenedor. Por ejemplo, una barra que se llena a medida que avanzamos por un artículo.

view() se utiliza cuando queremos que la animación dependa de la visibilidad de un elemento. Por ejemplo, una tarjeta que aparece progresivamente cuando entra en el viewport.

En resumen: scroll() mira el avance del scroll; view() mira la presencia del elemento en la zona visible.

¿Es recomendable usar animation-timeline en producción?

Sí, puede ser recomendable, pero con cuidado. Lo ideal es aplicarlo con mejora progresiva, revisar compatibilidad, usar @supports, mantener el contenido visible aunque la animación no funcione y respetar prefers-reduced-motion.

También conviene probar en dispositivos reales y evitar que las animaciones sean necesarias para comprender o utilizar la página.

Una forma más natural de animar la web

animation-timeline representa un paso importante en la evolución de CSS. Nos permite crear animaciones más conectadas con la interacción real del usuario, especialmente en experiencias basadas en scroll.

La gran ventaja no es solo técnica. También es conceptual. En lugar de pensar en animaciones como eventos aislados que ocurren durante una cantidad fija de segundos, podemos diseñarlas como respuestas progresivas al movimiento, a la lectura y a la presencia del contenido en pantalla.

Esto hace que las interfaces puedan sentirse más vivas, pero también más coherentes. Una barra que avanza mientras leemos, una imagen que se revela cuando llega su momento o una tarjeta que aparece suavemente al entrar en el viewport son detalles que pueden mejorar la experiencia sin sobrecargarla.

Como ocurre con cualquier recurso visual, la clave está en el equilibrio. No se trata de animar por animar, sino de utilizar las herramientas modernas de CSS para construir interfaces más claras, accesibles y agradables.

Si se usa con intención, animation-timeline CSS puede convertirse en una pieza muy valiosa dentro del conjunto de animaciones CSS modernas. Permite reducir dependencias innecesarias de JavaScript, escribir código más declarativo y crear experiencias visuales que acompañan al usuario de forma natural.

En definitiva, las animaciones con animation-timeline no son solo una novedad técnica. Son una invitación a pensar el movimiento en la web de una manera más fluida, contextual y humana.

Cómo crear un efecto typing con CSS

El efecto typing con CSS es una animación sencilla, visual y muy reconocible: el texto aparece progresivamente, como si alguien lo estuviera escribiendo en una máquina de escribir o en una terminal. Es un recurso muy utilizado en portfolios, páginas personales, landings, cabeceras creativas y secciones hero donde interesa captar la atención desde el primer vistazo.

Lo interesante de este efecto es que no necesitas JavaScript para conseguirlo. Con unas cuantas líneas de HTML y CSS puedes crear una animación ligera, fácil de personalizar y compatible con la mayoría de proyectos web. Eso sí, como ocurre con cualquier animación CSS, conviene aplicarla con criterio para que no perjudique la legibilidad, el rendimiento ni la accesibilidad.

En este artículo veremos cómo crear un efecto typing CSS paso a paso, cómo funciona la propiedad steps(), cómo añadir un cursor parpadeante, cómo adaptar el efecto a diferentes pantallas y qué buenas prácticas deberías tener en cuenta antes de incluirlo en una web real.

Si estás trabajando una serie de contenidos sobre movimiento en interfaces, este efecto encaja muy bien dentro de una estrategia más amplia sobre animaciones CSS, especialmente si quieres aprender a crear detalles visuales pequeños pero efectivos.

Un efecto typing en CSS, también conocido como efecto máquina de escribir, es una animación que muestra un texto de forma progresiva. En lugar de aparecer completo desde el inicio, el contenido se revela carácter a carácter o por pequeños bloques, generando la sensación de que se está escribiendo en tiempo real.

Este tipo de animación suele combinar tres elementos principales:

  • Un texto que inicialmente está oculto.
  • Una animación que va ampliando el área visible del texto.
  • Un cursor visual, normalmente una línea vertical, que parpadea al final de la frase.

El resultado recuerda a una máquina de escribir clásica, a una consola de comandos o a una interfaz tecnológica. Por eso también se le suele llamar typewriter effect o typing animation CSS.

Aunque puede parecer un detalle puramente decorativo, bien utilizado puede ayudar a destacar una frase importante, reforzar la identidad visual de una página o aportar una sensación de dinamismo sin sobrecargar la interfaz.

El texto animado CSS puede resultar muy atractivo, pero no debería usarse en cualquier parte de una página. Como cualquier recurso visual, debe tener una intención clara.

El efecto typing funciona especialmente bien en:

  • La cabecera principal de un portfolio.
  • Una landing page con una propuesta de valor breve.
  • Un titular destacado dentro de una página de servicios.
  • Interfaces con estética de terminal, tecnología o escritura.
  • Presentaciones personales donde quieras añadir un punto expresivo.

Por ejemplo, en una página de inicio podrías usar una frase como:

<h1>Creo interfaces web accesibles y cuidadas</h1>

Aplicada con efecto typing, esa frase gana presencia y puede llamar más la atención. Sin embargo, si animas párrafos largos o información demasiado importante, el usuario puede sentirse obligado a esperar para leer. Y eso no siempre es buena idea.

La clave está en usar este recurso en textos breves, relevantes y fáciles de entender. Una animación debería acompañar el contenido, no convertirse en una barrera.

El efecto typing con CSS suele construirse animando el ancho del elemento que contiene el texto. Al principio, ese ancho es 0, por lo que el texto no se ve. Después, el ancho va creciendo hasta mostrar la frase completa.

Para que funcione correctamente, se combinan varias propiedades:

  • overflow: hidden, para ocultar el texto que queda fuera del ancho visible.
  • white-space: nowrap, para evitar que el texto salte de línea.
  • width, para controlar cuánto texto se muestra.
  • animation, para animar el cambio de ancho.
  • steps(), para que la animación avance por saltos y no de forma fluida.
  • border-right o un pseudo-elemento, para simular el cursor.

Si ya has trabajado antes con @keyframes en CSS, este efecto te resultará bastante familiar. La diferencia principal está en el uso de steps(), que es lo que da esa sensación de escritura carácter a carácter.

La estructura HTML puede ser muy sencilla. Puedes aplicar el efecto directamente sobre un título, un párrafo o un span.

<h1 class="typing">Cómo crear interfaces con personalidad</h1>

También puedes aplicarlo solo a una parte de una frase:

<h1>
  Diseño <span class="typing">experiencias digitales</span>
</h1>

Esta segunda opción es muy útil cuando quieres que solo una parte del titular tenga movimiento. Por ejemplo, podrías mantener una frase fija y animar únicamente el concepto principal.

El siguiente código crea un efecto máquina de escribir sencillo, con texto progresivo y cursor parpadeante.

.typing {
  display: inline-block;
  width: 38ch;
  overflow: hidden;
  white-space: nowrap;
  border-right: 3px solid currentColor;
  animation: typing 3s steps(38), blink 0.7s step-end infinite;
}

@keyframes typing {
  from {
    width: 0;
  }

  to {
    width: 38ch;
  }
}

@keyframes blink {
  50% {
    border-color: transparent;
  }
}

Con este bloque ya tienes un efecto typing CSS funcional. El texto se revela poco a poco y el borde derecho actúa como cursor.

Usamos display: inline-block para que el elemento pueda comportarse como texto en línea, pero permita controlar su ancho. Sin esta propiedad, algunos elementos en línea no responderían igual al cambio de width.

display: inline-block;

La propiedad width define cuánto espacio ocupa el texto cuando la animación termina.

width: 38ch;

La unidad ch es muy práctica para este tipo de animación porque se relaciona con el ancho de los caracteres de la fuente. No es una medida perfecta para todos los casos, pero suele funcionar bien en textos de una sola línea.

Esta propiedad oculta la parte del texto que queda fuera del ancho visible.

overflow: hidden;

Sin overflow: hidden, el texto aparecería completo desde el principio y la animación perdería su sentido.

Esta línea evita que el texto salte a una segunda línea.

white-space: nowrap;

En un efecto máquina de escribir básico, normalmente queremos que la frase se mantenga en una sola línea mientras se revela.

El cursor se consigue con un borde derecho.

border-right: 3px solid currentColor;

Usar currentColor permite que el cursor herede el color del texto. Esto es útil si más adelante cambias el color del titular o trabajas con temas claros y oscuros.

La función steps() es una de las claves del efecto typing. Si animáramos el ancho de forma lineal, el texto se revelaría como una cortina horizontal continua. En cambio, steps() divide la animación en saltos.

animation: typing 3s steps(38);

Esto hace que el texto aparezca poco a poco, de una forma más parecida a la escritura real.

El número que colocas dentro de steps() debería coincidir aproximadamente con el número de caracteres del texto. Si la frase tiene 38 caracteres, puedes usar steps(38). Si tiene 24, puedes usar steps(24).

No hace falta que sea exacto al cien por cien, pero cuanto más se acerque el valor al contenido real, más natural se verá la animación.

A continuación tienes un ejemplo completo que puedes adaptar a una sección hero o a un bloque destacado de tu web.

<section class="hero">
  <p class="hero__intro">Frontend & UX</p>

  <h1 class="hero__title">
    <span class="typing">Creo interfaces web con CSS</span>
  </h1>
</section>
.hero {
  min-height: 60vh;
  display: grid;
  place-content: center;
  text-align: center;
  padding: 2rem;
}

.hero__intro {
  margin-bottom: 1rem;
  font-size: 1rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hero__title {
  margin: 0;
  font-size: clamp(2rem, 6vw, 4.5rem);
  line-height: 1.1;
}

.typing {
  display: inline-block;
  width: 29ch;
  overflow: hidden;
  white-space: nowrap;
  border-right: 0.08em solid currentColor;
  animation: typing 3s steps(29), blink 0.7s step-end infinite;
}

@keyframes typing {
  from {
    width: 0;
  }

  to {
    width: 29ch;
  }
}

@keyframes blink {
  50% {
    border-color: transparent;
  }
}

Este ejemplo utiliza clamp() para que el tamaño del texto sea más adaptable. Además, el cursor usa em, de modo que su grosor se relaciona con el tamaño de la fuente.

Si te interesa profundizar en qué propiedades conviene animar y cuáles pueden afectar más al rendimiento, puedes revisar esta guía sobre animaciones CSS y rendimiento.

La velocidad del efecto se controla principalmente desde la propiedad animation.

animation: typing 3s steps(29), blink 0.7s step-end infinite;

El valor 3s indica cuánto tarda el texto en escribirse por completo. Si quieres una animación más rápida, puedes reducir ese tiempo:

animation: typing 2s steps(29), blink 0.7s step-end infinite;

Si prefieres un ritmo más pausado, puedes aumentarlo:

animation: typing 4s steps(29), blink 0.7s step-end infinite;

Como referencia, para titulares cortos suele funcionar bien una duración entre 2 y 4 segundos. Si el texto tarda demasiado en aparecer, la animación puede volverse pesada.

El cursor es un detalle pequeño, pero influye mucho en el aspecto final. Puedes hacerlo más fino, más grueso, más rápido o más lento.

Para modificar el grosor, cambia el valor de border-right:

border-right: 2px solid currentColor;

También puedes usar unidades relativas:

border-right: 0.08em solid currentColor;

Esta opción suele integrarse mejor en diseños responsive porque el cursor escala junto con el texto.

La velocidad del parpadeo se controla en la animación blink.

animation: blink 0.7s step-end infinite;

Un valor menor hará que el cursor parpadee más rápido:

animation: blink 0.4s step-end infinite;

Un valor mayor hará que parpadee más despacio:

animation: blink 1s step-end infinite;

En general, conviene evitar un parpadeo demasiado rápido. Puede resultar visualmente agresivo y distraer más de la cuenta.

También puedes hacer que el texto se escriba, permanezca visible durante un momento y luego desaparezca. Para ello, basta con modificar los porcentajes del @keyframes.

<h2 class="typing-loop">Diseño. Desarrollo. Optimizo.</h2>
.typing-loop {
  display: inline-block;
  width: 28ch;
  overflow: hidden;
  white-space: nowrap;
  border-right: 0.08em solid currentColor;
  animation:
    typing-loop 6s steps(28) infinite,
    blink 0.7s step-end infinite;
}

@keyframes typing-loop {
  0% {
    width: 0;
  }

  40% {
    width: 28ch;
  }

  60% {
    width: 28ch;
  }

  100% {
    width: 0;
  }
}

@keyframes blink {
  50% {
    border-color: transparent;
  }
}

En este caso, el texto se escribe, se mantiene visible y después se borra. Es una solución útil para una frase fija, aunque si quieres rotar varias frases diferentes quizá te interese combinar CSS con JavaScript.

Para efectos sencillos, CSS es más que suficiente. Para secuencias más complejas, JavaScript te dará más control sobre los tiempos, las pausas y el contenido dinámico.

Uno de los principales problemas del efecto máquina de escribir es que puede romperse en pantallas pequeñas. Como usamos white-space: nowrap, el texto no salta de línea, y eso puede provocar desbordamientos si la frase es demasiado larga.

Para evitarlo, lo mejor es escribir frases breves y comprobar siempre el resultado en móvil.

Puedes controlar mejor el tamaño del texto con clamp():

.typing {
  font-size: clamp(1.5rem, 5vw, 3.5rem);
}

De esta forma, el texto se adapta mejor a diferentes anchos de pantalla.

En algunos casos, la mejor decisión es mostrar el texto completo en móvil y desactivar la animación.

@media (max-width: 480px) {
  .typing {
    width: auto;
    white-space: normal;
    border-right: 0;
    animation: none;
  }
}

Esta solución prioriza la legibilidad. El efecto typing puede ser muy atractivo en escritorio, pero si en móvil dificulta la lectura, es mejor simplificarlo.

Las animaciones pueden mejorar una interfaz, pero también pueden generar molestias si se usan sin cuidado. Algunas personas prefieren reducir el movimiento en sus dispositivos por razones de accesibilidad, concentración o comodidad visual.

CSS permite respetar esta preferencia con la media query prefers-reduced-motion.

@media (prefers-reduced-motion: reduce) {
  .typing {
    width: auto;
    overflow: visible;
    white-space: normal;
    border-right: 0;
    animation: none;
  }
}

Con esta regla, si el usuario tiene activada la reducción de movimiento, el texto se muestra directamente y la animación se desactiva.

Este detalle es especialmente importante cuando el texto contiene información relevante. El usuario no debería depender de una animación para entender el contenido.

Si quieres profundizar en este tema, puedes leer también la guía sobre prefers-reduced-motion en CSS, donde se explica cómo crear interfaces más respetuosas con las preferencias del usuario.

El texto animado CSS puede aportar personalidad, pero conviene usarlo con moderación. Estas recomendaciones te ayudarán a conseguir un resultado más profesional.

El efecto typing funciona mejor con frases breves. Una línea clara y directa suele tener más impacto que un titular largo que tarda demasiado en escribirse.

Si el mensaje es importante, no obligues al usuario a esperar. La animación debe acompañar la lectura, no retrasarla.

Un cursor parpadeante puede funcionar muy bien en una cabecera, pero varios cursores animados a la vez pueden resultar molestos.

El efecto puede verse perfecto en escritorio y romperse en pantallas pequeñas. Antes de publicarlo, revisa el resultado en diferentes anchos.

Añadir soporte para prefers-reduced-motion es una buena práctica básica cuando trabajas con animaciones. No cuesta mucho y mejora la experiencia de quienes prefieren interfaces más estáticas.

Uno de los errores más habituales es copiar un ejemplo sin adaptar el width ni los steps() al texto real.

width: 40ch;
animation: typing 3s steps(40);

Si tu frase tiene menos caracteres, quedará demasiado espacio vacío. Si tiene más, puede cortarse antes de tiempo.

Sin overflow: hidden, el texto se mostrará completo desde el principio. La animación seguirá existiendo, pero visualmente no parecerá una máquina de escribir.

Este efecto no está pensado para párrafos enteros. Funciona mejor en titulares, frases cortas o pequeños fragmentos destacados.

Si el cursor tiene poco contraste con el fondo, puede pasar desapercibido. Si tiene demasiado peso visual, puede distraer. Lo ideal es que acompañe al texto sin robar protagonismo.

Aunque este efecto es ligero, sigue siendo una animación. Si tu página ya tiene muchos elementos en movimiento, quizá convenga reducir o simplificar. En este sentido, también puede ayudarte revisar cuándo conviene usar transform y opacity en animaciones CSS frente a otras propiedades.

Si quieres aplicar el efecto typing en varios textos, puedes usar variables CSS para controlar el número de caracteres y la duración.

<h1 class="typing" style="--characters: 31; --duration: 3s;">
  Animaciones CSS con estilo
</h1>
.typing {
  display: inline-block;
  width: calc(var(--characters) * 1ch);
  overflow: hidden;
  white-space: nowrap;
  border-right: 0.08em solid currentColor;
  animation:
    typing var(--duration) steps(var(--characters)),
    blink 0.7s step-end infinite;
}

@keyframes typing {
  from {
    width: 0;
  }

  to {
    width: calc(var(--characters) * 1ch);
  }
}

@keyframes blink {
  50% {
    border-color: transparent;
  }
}

Esta variante es muy útil si quieres reutilizar el mismo efecto en diferentes componentes. Solo tienes que cambiar --characters y --duration en cada elemento.

Otra forma de crear el cursor es usar un pseudo-elemento ::after. Esta técnica da más control sobre la altura, el grosor y la posición del cursor.

<h1 class="typing typing--cursor">Efecto typing solo con CSS</h1>
.typing {
  position: relative;
  display: inline-block;
  width: 29ch;
  overflow: hidden;
  white-space: nowrap;
  animation: typing 3s steps(29) forwards;
}

.typing--cursor::after {
  content: "";
  position: absolute;
  right: 0;
  top: 0;
  width: 0.08em;
  height: 1em;
  background: currentColor;
  animation: blink-cursor 0.7s step-end infinite;
}

@keyframes typing {
  from {
    width: 0;
  }

  to {
    width: 29ch;
  }
}

@keyframes blink-cursor {
  50% {
    opacity: 0;
  }
}

Esta opción puede venir bien si el borde derecho no se ajusta exactamente al diseño que necesitas. Con ::after puedes modificar el alto, la posición y el comportamiento del cursor de forma más precisa.

Una pregunta habitual es si este efecto debería hacerse con CSS o con JavaScript. La respuesta depende del nivel de control que necesites.

CSS es una buena opción cuando:

  • El texto es fijo.
  • La animación es sencilla.
  • No necesitas cambiar frases dinámicamente.
  • Buscas una solución ligera.
  • Quieres añadir un detalle visual a una sección concreta.

JavaScript puede ser más adecuado cuando:

  • Quieres rotar varias frases.
  • Necesitas borrar y escribir textos diferentes.
  • El contenido viene de una API.
  • Quieres controlar pausas y velocidades de forma dinámica.
  • Necesitas una animación más interactiva.

Para una cabecera, una frase destacada o un pequeño detalle visual, CSS suele ser suficiente. Para sistemas más complejos, JavaScript ofrece más flexibilidad.

También es importante entender la diferencia entre distintos tipos de animación. Si estás comparando enfoques, puedes revisar este artículo sobre transition vs animation en CSS, donde se explica cuándo conviene usar cada una.

Sí. Puedes crear un efecto typing solo con CSS usando overflow: hidden, white-space: nowrap, una animación sobre width y la función steps(). También puedes añadir un cursor con border-right o con un pseudo-elemento.

steps() divide la animación en saltos. En lugar de revelar el texto de forma fluida, lo muestra por partes, generando una sensación similar a la escritura carácter a carácter.

Depende de la longitud del texto y del diseño. Si la frase es corta, puede funcionar bien. Si el texto se desborda o dificulta la lectura, es mejor desactivar la animación en pantallas pequeñas y mostrar el contenido completo.

Crear un efecto typing con CSS es relativamente sencillo, pero aplicarlo bien requiere algo más que copiar y pegar código. Hay que pensar en la longitud del texto, el ritmo de la animación, el comportamiento responsive, el cursor y la accesibilidad.

El efecto máquina de escribir puede aportar personalidad a una interfaz, especialmente en titulares y secciones hero. Sin embargo, su fuerza está en la sutileza. Cuando se usa en una frase breve y relevante, puede captar la atención sin resultar invasivo. Cuando se aplica a demasiados textos o se alarga demasiado, puede convertirse en una distracción.

CSS nos permite crear movimiento de forma ligera y expresiva, pero el criterio de diseño sigue siendo lo más importante. Una buena animación no es la que más se nota, sino la que aparece en el momento adecuado, con la duración justa y al servicio de un mensaje claro.

Si quieres añadir dinamismo a tu web sin depender de JavaScript, el efecto typing CSS es una opción práctica, visual y fácil de adaptar. Bien utilizado, puede convertir una simple línea de texto en un detalle memorable dentro de la experiencia de usuario.