Por qué animar transform y opacity antes que width o height

Las animaciones pueden hacer que una interfaz resulte más clara, agradable y fácil de comprender. Un botón que responde al pasar el cursor, una tarjeta que aparece suavemente o un menú que se despliega ayudan a comunicar que se ha producido un cambio.

Sin embargo, no todas las propiedades CSS tienen el mismo coste para el navegador.

Aunque visualmente dos animaciones puedan parecer similares, internamente pueden activar procesos muy distintos. Cambiar el tamaño de un elemento mediante width o height, por ejemplo, suele requerir más trabajo que modificar su apariencia con transform. Del mismo modo, mostrar u ocultar un componente animando opacity suele ser más eficiente que alterar propiedades que afectan directamente a su geometría.

Por este motivo, una de las recomendaciones más habituales al trabajar con movimiento en interfaces web es animar transform y opacity siempre que sea posible, antes que propiedades como width, height, top o left.

Esta recomendación no es una regla arbitraria. Está relacionada con la manera en que el navegador calcula, pinta y compone cada página.

En este artículo veremos por qué conviene animar transform en CSS, cuándo es recomendable animar opacity, cómo influye la elección de propiedades en el rendimiento y qué alternativas podemos utilizar para evitar animar width y height innecesariamente.

Si todavía estás familiarizándote con estos conceptos, puedes comenzar por esta guía básica de animaciones CSS, donde explico las diferencias entre transition, animation y @keyframes.

Cómo renderiza una página el navegador

Para comprender por qué algunas animaciones son más eficientes que otras, primero debemos observar, de forma simplificada, cómo convierte el navegador nuestro HTML y CSS en los píxeles que aparecen en pantalla.

Cuando una página se carga o cambia, el navegador puede atravesar varias etapas:

  1. Cálculo de estilos.
  2. Layout o disposición.
  3. Pintado.
  4. Composición.

No todos los cambios CSS necesitan recorrer todas estas fases. Esa es precisamente la diferencia que puede hacer que una animación se perciba fluida o, por el contrario, entrecortada.

Cálculo de estilos

El navegador analiza qué reglas CSS se aplican a cada elemento.

Si añadimos una clase, modificamos un estado como :hover o cambiamos una propiedad mediante JavaScript, puede ser necesario recalcular parte de los estilos de la página.

Este proceso no siempre es especialmente costoso. El problema aparece cuando afecta a una gran cantidad de elementos o se repite continuamente durante una animación.

Por ejemplo, si una clase modifica varias propiedades en un componente complejo, el navegador debe determinar de nuevo qué estilos corresponden a cada elemento involucrado.

Layout: calcular tamaños y posiciones

Durante la fase de layout, también conocida como reflow, el navegador calcula el tamaño y la posición de los elementos dentro del documento.

Aquí intervienen propiedades como:

  • width
  • height
  • margin
  • padding
  • top
  • left
  • font-size
  • display

Cuando cambia la anchura de un elemento, también puede cambiar la posición de sus hermanos, el tamaño de su contenedor o la distribución de una sección completa.

Imagina una tarjeta situada dentro de una cuadrícula. Si modificamos su width, es posible que el navegador tenga que comprobar si las demás tarjetas caben en la misma fila, si alguna debe pasar a la siguiente línea o si el contenedor necesita cambiar de tamaño.

Por tanto, el navegador no siempre actualiza únicamente el elemento animado. En algunos casos debe volver a calcular una parte considerable de la página.

Paint: convertir los elementos en píxeles

Después del layout, el navegador pinta el contenido.

En esta fase procesa, entre otros elementos:

  • Colores.
  • Fondos.
  • Bordes.
  • Texto.
  • Imágenes.
  • Sombras.
  • Gradientes.

Propiedades como background-color, border-color o box-shadow pueden provocar nuevas operaciones de pintado.

El coste dependerá del tamaño del área modificada y de la complejidad visual del componente. No es lo mismo volver a pintar un icono pequeño que una sección que ocupa toda la pantalla y contiene sombras, imágenes y filtros.

Composición: organizar las capas

En la fase de composición, el navegador combina las distintas capas visuales y genera la imagen final que vemos en pantalla.

Propiedades como transform y opacity pueden gestionarse, en muchos casos, principalmente durante esta etapa. Esto permite reutilizar contenido que ya ha sido calculado y pintado.

Por qué la composición suele ser más eficiente

Imagina una tarjeta que ya ha sido colocada y pintada.

Si la movemos cambiando su propiedad left, el navegador puede necesitar recalcular su posición y comprobar cómo afecta el cambio al resto de la página.

En cambio, si la desplazamos utilizando transform: translateX(), el navegador puede mover visualmente una capa que ya estaba preparada.

En términos sencillos, una opción puede obligar a reconstruir parte de la escena, mientras que la otra mueve una pieza que ya existe.

Por qué conviene animar transform en CSS

La propiedad transform permite modificar visualmente un elemento sin cambiar directamente el espacio que ocupa dentro del flujo del documento.

Con ella podemos aplicar diferentes transformaciones:

  • Desplazamientos con translate().
  • Cambios de escala con scale().
  • Rotaciones con rotate().
  • Inclinaciones con skew().

Estas operaciones suelen ser adecuadas para crear animaciones porque no obligan a recolocar automáticamente los elementos que se encuentran alrededor.

Mover un elemento con left frente a translateX

Supongamos que queremos desplazar un elemento horizontalmente.

Una primera opción sería animar la propiedad left:

.elemento {
  position: relative;
  left: 0;
  transition: left 300ms ease;
}

.elemento:hover {
  left: 40px;
}

El código funciona, pero left participa en el cálculo de la posición del elemento. Durante la transición, el navegador puede tener que actualizar el layout en cada fotograma.

Una alternativa más eficiente consiste en utilizar transform:

.elemento {
  transform: translateX(0);
  transition: transform 300ms ease;
}

.elemento:hover {
  transform: translateX(40px);
}

El efecto visual es parecido, pero el segundo ejemplo puede resolverse principalmente durante la composición.

Cuando el objetivo sea crear un desplazamiento puramente visual, suele ser preferible utilizar translate() en lugar de animar top, right, bottom o left.

Cambiar el tamaño con scale frente a width

También podemos utilizar transform para simular un cambio de tamaño.

Por ejemplo, podríamos hacer crecer un botón modificando su anchura:

.boton {
  width: 180px;
  transition: width 250ms ease;
}

.boton:hover {
  width: 200px;
}

Esta transición altera el tamaño real del botón. Como consecuencia, puede afectar al texto, al contenedor y a los elementos cercanos.

En muchos casos podemos conseguir una respuesta visual similar utilizando scale():

.boton {
  transform: scale(1);
  transition: transform 250ms ease;
}

.boton:hover {
  transform: scale(1.05);
}

Ahora el espacio utilizado por el layout permanece igual. Lo que cambia es la representación visual del botón.

Este recurso puede resultar especialmente útil en botones, tarjetas o pequeños elementos gráficos. También puede aplicarse a algunos iconos creados únicamente con CSS para añadir una respuesta visual sin modificar su tamaño estructural.

Cuándo scale no sustituye realmente a width o height

Es importante comprender que scale() no modifica el espacio reservado en el documento.

Si un elemento mide 200 píxeles de ancho y aplicamos:

transform: scaleX(1.5);

visualmente será más ancho, pero el navegador continuará reservando los 200 píxeles originales dentro del layout.

Esto puede provocar que el elemento transformado se superponga con otros componentes.

Por tanto, scale() es especialmente útil para:

  • Efectos de interacción.
  • Botones que crecen ligeramente.
  • Tarjetas que destacan al pasar el cursor.
  • Iconos que cambian de tamaño.
  • Elementos que entran o salen de escena.
  • Animaciones decorativas.

No siempre es adecuado para cambios estructurales en los que el contenido debe empujar, reducir o recolocar otros elementos.

Por qué animar opacity en CSS suele ser eficiente

La propiedad opacity controla la transparencia de un elemento.

Su valor puede ir desde 0, completamente transparente, hasta 1, completamente visible:

.elemento {
  opacity: 0;
}

Una transición sencilla de aparición podría escribirse así:

.elemento {
  opacity: 0;
  transition: opacity 300ms ease;
}

.elemento.visible {
  opacity: 1;
}

Este cambio no modifica el tamaño ni la posición del componente. Por tanto, generalmente no obliga al navegador a recalcular el layout.

El navegador puede reutilizar la capa del elemento y cambiar su nivel de transparencia durante la composición.

Combinar transform y opacity

Una de las técnicas más útiles para crear entradas suaves consiste en combinar un pequeño desplazamiento con un cambio de opacidad:

.tarjeta {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 300ms ease,
    transform 300ms ease;
}

.tarjeta.visible {
  opacity: 1;
  transform: translateY(0);
}

En este ejemplo, la tarjeta:

  • Comienza ligeramente desplazada.
  • Aparece de forma progresiva.
  • Recupera su posición visual.
  • No necesita animar su altura ni su posición mediante top.

Esta combinación es habitual en tarjetas, modales, menús, avisos y elementos que aparecen al hacer scroll.

Cuando la animación requiere secuencias más complejas, coordinación entre varios elementos o un control preciso de los tiempos, puede ser conveniente utilizar una herramienta específica. En ese caso, puedes consultar cómo crear animaciones para la web con la librería GSAP.

Opacity no elimina el elemento del documento

Un detalle importante es que opacity: 0 no equivale a eliminar un elemento.

Un componente transparente continúa:

  • Ocupando espacio.
  • Pudiendo recibir eventos del puntero.
  • Pudiendo recibir el foco mediante el teclado.
  • Formando parte del árbol de accesibilidad si no se gestiona de otra manera.

Por eso, para ocultar correctamente un elemento interactivo puede ser necesario combinar varias propiedades:

.menu {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-8px);
  transition:
    opacity 200ms ease,
    transform 200ms ease,
    visibility 0s linear 200ms;
}

.menu.abierto {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
  transition-delay: 0s;
}

La opacidad controla la transición visual, mientras que visibility y pointer-events evitan interacciones accidentales cuando el menú está oculto.

Dependiendo del componente, también puede ser necesario actualizar atributos como aria-hidden, aria-expanded o el estado de los elementos que pueden recibir el foco.

Qué ocurre al animar width o height

Las propiedades width y height forman parte de la geometría del elemento.

Cuando cambian, el navegador puede tener que calcular de nuevo:

  • El tamaño del propio elemento.
  • La distribución de sus contenidos.
  • La posición de los elementos cercanos.
  • Las dimensiones del contenedor.
  • Los saltos de línea del texto.
  • El espacio ocupado por la sección.

Si este cálculo se repite muchas veces por segundo, la animación puede exigir más trabajo al hilo principal.

Esto no significa que animar width o height esté siempre mal. Significa que conviene comprobar si el mismo efecto puede conseguirse sin modificar continuamente el layout.

Ejemplo de una animación de width

Observemos una barra de progreso:

.barra {
  width: 0;
  height: 8px;
  transition: width 500ms ease;
}

.barra.completa {
  width: 100%;
}

Animar width puede parecer la solución más evidente. Sin embargo, podemos construir la barra con su tamaño final y animar únicamente su escala horizontal:

.barra {
  width: 100%;
  height: 8px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 500ms ease;
}

.barra.completa {
  transform: scaleX(1);
}

El resultado visual es una barra que crece desde la izquierda, pero sin modificar su anchura real en cada fotograma.

La propiedad transform-origin es fundamental:

transform-origin: left;

Sin ella, la barra crecería desde el centro, ya que ese es el origen predeterminado de la transformación.

Ejemplo de una animación de height

Un patrón frecuente consiste en intentar desplegar un contenido animando su altura:

.contenido {
  height: 0;
  overflow: hidden;
  transition: height 300ms ease;
}

.contenido.abierto {
  height: 300px;
}

El problema es que el contenido puede no medir exactamente 300 píxeles. Además, la altura se recalcula durante toda la transición.

Otra aproximación conocida utiliza max-height:

.contenido {
  max-height: 0;
  overflow: hidden;
  transition: max-height 300ms ease;
}

.contenido.abierto {
  max-height: 600px;
}

Esta técnica puede resultar útil, pero tiene limitaciones. La duración visual real depende de la relación entre la altura del contenido y el valor máximo definido.

Si el bloque mide 150 píxeles y animamos hasta 600, el contenido llegará a su altura visible antes de que termine la transición matemática.

Alternativas para desplegar contenido

No existe una única solución válida para todos los acordeones o paneles.

Dependiendo del diseño, podemos utilizar:

  • CSS Grid.
  • JavaScript para medir scrollHeight.
  • View Transitions cuando el contexto lo permita.
  • Una combinación de opacity y transform.
  • Una animación de altura cuando sea realmente necesaria.

Un ejemplo con CSS Grid consiste en pasar de una fila con fracción cero a una fila con una fracción:

.acordeon__contenedor {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 300ms ease;
}

.acordeon__contenedor.abierto {
  grid-template-rows: 1fr;
}

.acordeon__contenido {
  overflow: hidden;
}

Esta técnica permite desplegar contenido de altura variable sin establecer un número fijo. Aun así, sigue existiendo un cambio de layout, por lo que debe utilizarse de manera razonable.

Transform y opacity no son propiedades mágicas

Aunque el rendimiento de transform y opacity suele ser mejor, no significa que cualquier animación basada en estas propiedades vaya a funcionar perfectamente.

El coste final depende de varios factores:

  • El tamaño del elemento.
  • La cantidad de elementos animados.
  • La complejidad del contenido.
  • El dispositivo utilizado.
  • La memoria gráfica disponible.
  • La duración de la animación.
  • La presencia de filtros, sombras o desenfoques.
  • La cantidad de capas creadas.

Una animación de transform aplicada a cientos de elementos simultáneos puede seguir provocando problemas.

Del mismo modo, animar la opacidad de una capa que cubre toda la pantalla y contiene efectos complejos puede resultar más costoso de lo esperado.

Cuidado con will-change

La propiedad will-change permite avisar al navegador de que una propiedad probablemente cambiará:

.elemento {
  will-change: transform;
}

Este aviso puede ayudar al navegador a preparar determinadas optimizaciones antes de que comience la animación.

Sin embargo, no conviene aplicarlo de forma general:

* {
  will-change: transform;
}

Este enfoque puede aumentar innecesariamente el consumo de memoria y crear demasiadas capas.

Lo recomendable es utilizar will-change únicamente en componentes concretos que realmente lo necesiten:

.modal {
  will-change: transform, opacity;
}

Incluso en esos casos, debemos comprobar si aporta una mejora real. Los navegadores actuales ya realizan muchas optimizaciones sin que tengamos que indicarlas manualmente.

Cómo evitar animar width y height en casos habituales

Antes de crear una transición, conviene preguntarse qué efecto visual queremos conseguir y si realmente necesitamos modificar la estructura del documento.

En muchos casos podemos sustituir una propiedad geométrica por una transformación.

Para mover un elemento

En lugar de modificar su posición con:

left: 20px;
top: 10px;

podemos utilizar:

transform: translate(20px, 10px);

Para agrandar o reducir

En lugar de animar:

width: 110%;
height: 110%;

podemos utilizar:

transform: scale(1.1);

Para mostrar una barra de progreso

En lugar de comenzar con:

width: 0;

podemos utilizar:

transform: scaleX(0);
transform-origin: left;

Para hacer aparecer un componente

Podemos combinar:

opacity: 0;
transform: translateY(12px);

con:

opacity: 1;
transform: translateY(0);

Para ocultar contenido interactivo

Podemos combinar las siguientes propiedades:

opacity
visibility
pointer-events
transform

Cada una cumple una función diferente: transición visual, visibilidad, interacción con el puntero y movimiento.

Cuándo sí tiene sentido animar width o height

Evitar width y height no significa prohibirlas.

Hay situaciones en las que el cambio de tamaño real forma parte de la experiencia y debe modificar el layout.

Por ejemplo:

  • Un panel lateral que reduce el espacio disponible del contenido principal.
  • Un editor con columnas redimensionables.
  • Un acordeón que desplaza los elementos situados debajo.
  • Un componente que expande su contenido de forma estructural.
  • Una interfaz en la que el tamaño final no puede simularse mediante scale().

En estos casos, animar la geometría puede ser una decisión válida.

La clave está en hacerlo conscientemente, limitar el número de elementos afectados y comprobar el resultado en dispositivos reales.

La diferencia entre una animación visual y una estructural

Una animación visual modifica cómo percibimos un elemento, pero no necesita alterar el flujo de la página.

Una animación estructural cambia realmente la distribución del contenido.

Para una respuesta al pasar el cursor, un pequeño desplazamiento o una aparición, transform y opacity suelen ser suficientes.

Para un panel que debe empujar otro contenido, es posible que necesitemos cambiar el layout.

No se trata de escoger siempre la propiedad teóricamente más rápida, sino de utilizar la propiedad adecuada para el comportamiento que estamos diseñando.

Cómo comprobar el rendimiento de una animación

No es recomendable asumir que una animación funciona bien únicamente porque se ve fluida en nuestro ordenador.

Un equipo potente puede ocultar problemas que aparecerán en:

  • Teléfonos antiguos.
  • Dispositivos con poca memoria.
  • Páginas con mucho contenido.
  • Navegadores con implementaciones diferentes.
  • Situaciones en las que se ejecutan varios procesos simultáneamente.

Las herramientas de desarrollo del navegador permiten analizar el trabajo realizado durante una animación.

Podemos revisar:

  • La actividad del hilo principal.
  • Los cálculos de estilo.
  • Las operaciones de layout.
  • Las tareas de pintado.
  • Los fotogramas lentos.
  • La creación de capas.
  • Las zonas que se vuelven a pintar.

Si al animar un componente aparecen operaciones de layout continuas, puede ser una señal de que estamos utilizando propiedades geométricas innecesariamente.

También conviene evaluar la animación limitando artificialmente la CPU desde las herramientas de desarrollo y comprobarla en un dispositivo móvil real.

Accesibilidad y prefers-reduced-motion

El rendimiento no es el único criterio que debemos considerar.

Algunas personas pueden experimentar molestias, mareos o dificultades de concentración ante movimientos intensos. Por eso, es importante respetar la preferencia prefers-reduced-motion.

Podemos establecer una animación normal y reducirla cuando la persona haya solicitado menos movimiento en su sistema:

.tarjeta {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 300ms ease,
    transform 300ms ease;
}

.tarjeta.visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .tarjeta {
    transform: none;
    transition-duration: 1ms;
  }
}

En algunos componentes puede ser mejor eliminar la transición por completo:

@media (prefers-reduced-motion: reduce) {
  .elemento-animado {
    animation: none;
    transition: none;
  }
}

Reducir el movimiento no significa ocultar información. El componente debe seguir siendo comprensible y funcional aunque la animación desaparezca.

Buenas prácticas para crear animaciones CSS eficientes

Una animación efectiva no depende únicamente de las propiedades utilizadas. También influyen su duración, su propósito y el contexto en el que aparece.

Animar solo lo necesario

Evita añadir movimiento a todos los elementos de la interfaz.

Cada animación debería cumplir alguna función:

  • Confirmar una acción.
  • Mostrar una relación entre estados.
  • Orientar la atención.
  • Explicar la aparición de un componente.
  • Suavizar un cambio.
  • Comunicar progreso.

Cuando todo se mueve, ningún movimiento destaca.

Mantener duraciones razonables

Las microinteracciones suelen funcionar bien con duraciones breves, aunque no existe un valor universal.

Una transición demasiado lenta puede hacer que la interfaz parezca pesada. Una transición excesivamente rápida puede resultar imperceptible o brusca.

Como referencia inicial, muchos efectos pequeños pueden situarse entre 150 y 300 milisegundos. No obstante, el valor debe ajustarse según la distancia recorrida, el tamaño del elemento y la importancia del cambio.

Evitar transition: all

Es frecuente encontrar reglas como esta:

.elemento {
  transition: all 300ms ease;
}

Aunque resulte cómoda, puede animar propiedades que no teníamos intención de modificar.

Es mejor especificar qué propiedades deben cambiar:

.elemento {
  transition:
    transform 300ms ease,
    opacity 300ms ease;
}

De esta forma, el comportamiento es más predecible y reducimos el riesgo de crear transiciones costosas accidentalmente.

Utilizar transform-origin correctamente

Cuando trabajamos con scale() o rotate(), el punto de origen cambia la percepción del movimiento.

Por ejemplo:

transform-origin: left center;

puede hacer que un indicador crezca desde la izquierda, mientras que:

transform-origin: center;

hará que se expanda en ambas direcciones.

Elegir el origen adecuado permite sustituir muchas animaciones de anchura o altura por transformaciones visualmente coherentes.

No olvidar las animaciones SVG

Los mismos criterios de rendimiento pueden aplicarse a gráficos e ilustraciones.

En lugar de reconstruir continuamente el tamaño o la geometría de un elemento, podemos animar desplazamientos, rotaciones y opacidad. Para profundizar en este tipo de recursos, puedes consultar el artículo sobre Sass y animaciones SVG.

Errores frecuentes al optimizar animaciones

Uno de los errores más habituales es sustituir todas las propiedades por transform sin considerar el comportamiento real del layout.

Otro consiste en aplicar will-change a numerosos componentes con la idea de que mejorará automáticamente el rendimiento.

También debemos evitar:

  • Animar decenas de elementos sin comprobar el coste.
  • Utilizar filtros intensivos junto con grandes desplazamientos.
  • Ejecutar animaciones infinitas que no aportan información.
  • Mantener elementos transparentes pero interactivos.
  • Ignorar prefers-reduced-motion.
  • Evaluar el rendimiento únicamente en un ordenador potente.
  • Utilizar transition: all por comodidad.
  • Crear animaciones largas para acciones frecuentes.
  • Usar scale() cuando el elemento debe modificar realmente el layout.

La optimización no consiste en memorizar una lista de propiedades permitidas y prohibidas. Consiste en comprender qué trabajo estamos pidiendo al navegador.

Preguntas frecuentes sobre transform, opacity, width y height

¿Por qué transform suele rendir mejor que width o height?

transform modifica principalmente la representación visual del elemento y, en muchos casos, puede gestionarse durante la fase de composición.

Por el contrario, width y height cambian la geometría real del componente y pueden obligar al navegador a recalcular el layout, recolocar otros elementos y volver a pintar parte de la página.

¿Siempre debo evitar animar width y height?

No. Debes evitarlas cuando el efecto sea puramente visual y pueda resolverse con transform.

Si el cambio debe modificar realmente el espacio disponible, desplazar otros componentes o reorganizar el contenido, animar width o height puede ser necesario. Lo importante es utilizar estas propiedades de forma controlada y comprobar su impacto.

¿Es suficiente usar transform y opacity para garantizar una animación fluida?

No. Estas propiedades suelen ser más eficientes, pero el resultado depende del tamaño de los elementos, la cantidad de animaciones, los efectos visuales utilizados, el dispositivo y la complejidad de la página.

Una animación con transform también puede funcionar mal si afecta a demasiados elementos, utiliza capas muy grandes o se combina con filtros y sombras complejas.

Elegir bien qué animar también es diseñar mejor

Elegir entre transform, opacity, width o height no es únicamente una decisión técnica. También es una decisión de diseño.

Cuando animamos una propiedad geométrica, estamos pidiendo al navegador que vuelva a calcular cómo encajan las piezas de la interfaz. Cuando utilizamos transform u opacity, normalmente modificamos cómo se presenta una pieza que ya ha sido calculada.

Por eso, animar transform y opacity suele ofrecer una experiencia más fluida que animar width o height. Sin embargo, esta recomendación no debería convertirse en una regla rígida.

Antes de implementar cualquier movimiento, conviene plantearse una pregunta:

¿Este elemento necesita cambiar realmente el layout o solo debe parecer que se mueve, crece o aparece?

Si el cambio es visual, transform y opacity probablemente sean el mejor punto de partida. Si el cambio es estructural, quizá necesitemos modificar la geometría real.

Una buena animación no es la que utiliza más efectos ni la que resulta más espectacular. Es aquella que ayuda a comprender la interfaz, responde con fluidez y respeta tanto el rendimiento del dispositivo como las preferencias de la persona usuaria.

Cómo animar un menú móvil con CSS

Los menús de navegación para dispositivos móviles se han convertido en un componente imprescindible en la mayoría de los sitios web. Cuando el espacio disponible en pantalla es reducido, la navegación principal suele ocultarse detrás de un botón reconocible, generalmente representado mediante tres líneas horizontales. Al pulsarlo, el menú aparece con una transición que ayuda al usuario a comprender qué está ocurriendo en la interfaz.

Sin embargo, añadir movimiento a un menú no consiste únicamente en aplicar una propiedad transition. Una animación mal planteada puede generar saltos visuales, problemas de rendimiento o dificultades de accesibilidad. Por el contrario, un menú móvil CSS animado correctamente implementado puede mejorar la orientación, reforzar la jerarquía visual y hacer que la navegación resulte más natural.

En esta guía veremos cómo animar un menú móvil con CSS, qué estructura HTML conviene utilizar, cómo controlar su apertura, qué propiedades ofrecen mejor rendimiento y cómo respetar las preferencias de movimiento de cada persona.

¿Qué es un menú móvil animado?

Un menú móvil animado es un sistema de navegación diseñado para pantallas pequeñas que permanece oculto hasta que el usuario activa un botón. Al abrirse o cerrarse, el panel cambia de estado mediante una transición o una animación.

El patrón más habitual es el denominado menú hamburguesa CSS, aunque también pueden utilizarse otros iconos, como una flecha, varios puntos o una etiqueta de texto que indique claramente “Menú”.

La animación puede aplicarse de diferentes maneras:

  • Desplazando el panel desde uno de los laterales.
  • Mostrándolo desde la parte superior.
  • Aplicando una aparición gradual.
  • Escalando el contenido desde un punto concreto.
  • Ocupando toda la pantalla con una capa de navegación.
  • Animando cada enlace con un pequeño retraso.

El objetivo no debería ser llamar la atención mediante efectos complejos. La animación tiene que explicar el cambio de estado, no dificultarlo.

Cuando el menú se desplaza desde el lateral derecho, por ejemplo, el usuario percibe que existe un panel fuera del área visible. Cuando se desvanece, entiende que el elemento estaba oculto y acaba de aparecer. El movimiento aporta contexto espacial a la interacción.

Estructura básica de un menú hamburguesa accesible

Antes de comenzar con la animación, necesitamos una estructura HTML clara y semántica.

Aunque es posible abrir y cerrar un menú utilizando exclusivamente CSS mediante un checkbox, en un proyecto real suele ser preferible emplear un botón y una pequeña cantidad de JavaScript. Esto facilita la gestión de atributos de accesibilidad como aria-expanded.

<header class="site-header">
  <a class="site-logo" href="/">
    Marta González
  </a>

  <button
    class="menu-toggle"
    type="button"
    aria-expanded="false"
    aria-controls="mobile-menu"
    aria-label="Abrir menú"
  >
    <span class="menu-toggle__line"></span>
    <span class="menu-toggle__line"></span>
    <span class="menu-toggle__line"></span>
  </button>

  <nav
    class="mobile-menu"
    id="mobile-menu"
    aria-label="Navegación principal"
  >
    <ul class="mobile-menu__list">
      <li><a href="/">Inicio</a></li>
      <li><a href="/servicios/">Servicios</a></li>
      <li><a href="/proyectos/">Proyectos</a></li>
      <li><a href="/blog/">Blog</a></li>
      <li><a href="/contacto/">Contacto</a></li>
    </ul>
  </nav>
</header>

El botón utiliza aria-controls para indicar qué elemento controla. El atributo aria-expanded comunica si el menú está abierto o cerrado.

Estos detalles no modifican la apariencia, pero sí ayudan a quienes navegan mediante lectores de pantalla u otras tecnologías de asistencia.

¿Por qué conviene utilizar un botón?

Un error habitual consiste en crear el activador mediante un <div> o un <span>. Aunque estos elementos pueden recibir eventos de clic, no ofrecen de forma predeterminada el comportamiento de un botón.

Un elemento <button>:

  • Puede recibir el foco mediante teclado.
  • Se activa con las teclas adecuadas.
  • Tiene una semántica reconocible.
  • Permite utilizar atributos aria.
  • No requiere recrear manualmente comportamientos nativos.

Siempre que un elemento ejecute una acción, el botón suele ser la opción correcta.

Cómo crear la apariencia inicial del menú

Empecemos definiendo algunas variables y los estilos generales del encabezado.

:root {
  --header-height: 4.5rem;
  --menu-background: #ffffff;
  --menu-text: #020101;
  --menu-accent: #cc2b5e;
  --menu-duration: 350ms;
  --menu-easing: cubic-bezier(0.22, 1, 0.36, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  color: var(--menu-text);
  font-family: system-ui, sans-serif;
}

.site-header {
  position: relative;
  z-index: 20;
  display: flex;
  min-height: var(--header-height);
  align-items: center;
  justify-content: space-between;
  padding-inline: 1.25rem;
  background: #ffffff;
}

.site-logo {
  position: relative;
  z-index: 30;
  color: inherit;
  font-weight: 700;
  text-decoration: none;
}

El z-index del encabezado será importante cuando coloquemos el menú en una capa independiente.

Estilos del botón hamburguesa

El botón tendrá tres líneas que después transformaremos en una cruz.

.menu-toggle {
  position: relative;
  z-index: 30;
  display: grid;
  width: 3rem;
  height: 3rem;
  padding: 0.65rem;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.menu-toggle__line {
  display: block;
  width: 100%;
  height: 2px;
  margin: auto;
  border-radius: 999px;
  background: currentColor;
  transform-origin: center;
  transition:
    transform var(--menu-duration) var(--menu-easing),
    opacity 200ms ease;
}

.menu-toggle:focus-visible {
  outline: 3px solid var(--menu-accent);
  outline-offset: 3px;
}

El estilo :focus-visible permite mostrar un indicador claro cuando el control recibe el foco mediante teclado.

No conviene eliminar el contorno con outline: none sin proporcionar una alternativa. El foco visible es esencial para saber qué elemento está activo.

Cómo animar un menú móvil con CSS mediante transform

Una de las soluciones más eficaces consiste en colocar el menú fuera de la pantalla y desplazarlo cuando se activa.

.mobile-menu {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 10;
  display: grid;
  width: min(85vw, 25rem);
  min-height: 100dvh;
  padding:
    calc(var(--header-height) + 2rem)
    2rem
    2rem;
  background: var(--menu-background);
  box-shadow: -1rem 0 3rem rgb(0 0 0 / 15%);
  transform: translateX(100%);
  visibility: hidden;
  transition:
    transform var(--menu-duration) var(--menu-easing),
    visibility 0s linear var(--menu-duration);
}

.mobile-menu.is-open {
  transform: translateX(0);
  visibility: visible;
  transition:
    transform var(--menu-duration) var(--menu-easing),
    visibility 0s linear 0s;
}

El panel empieza con translateX(100%), por lo que queda desplazado hacia la derecha. Al añadir la clase .is-open, vuelve a su posición original.

La propiedad visibility evita que los enlaces ocultos puedan recibir el foco. Su transición se retrasa durante el cierre para que el panel no desaparezca antes de completar el movimiento.

¿Por qué utilizar transform en lugar de left o right?

También podríamos animar la propiedad right:

.mobile-menu {
  right: -100%;
}

.mobile-menu.is-open {
  right: 0;
}

Visualmente podría ofrecer un resultado parecido, pero animar propiedades relacionadas con la posición y la geometría puede obligar al navegador a recalcular la distribución de la página.

En cambio, transform suele ser una opción más adecuada para el movimiento. Por este motivo, al plantear una mobile menu animation CSS, normalmente conviene priorizar:

  • transform
  • opacity

Estas propiedades permiten crear animaciones fluidas sin modificar continuamente las dimensiones o la posición calculada del documento.

Controlar la apertura del menú con JavaScript

CSS se ocupará de la animación, mientras que JavaScript cambiará el estado del componente.

const menuButton = document.querySelector(".menu-toggle");
const mobileMenu = document.querySelector(".mobile-menu");

function setMenuState(isOpen) {
  mobileMenu.classList.toggle("is-open", isOpen);
  menuButton.classList.toggle("is-active", isOpen);

  menuButton.setAttribute("aria-expanded", String(isOpen));
  menuButton.setAttribute(
    "aria-label",
    isOpen ? "Cerrar menú" : "Abrir menú"
  );

  document.body.classList.toggle("menu-open", isOpen);
}

menuButton.addEventListener("click", () => {
  const isOpen =
    menuButton.getAttribute("aria-expanded") === "true";

  setMenuState(!isOpen);
});

La función centraliza todos los cambios relacionados con el estado:

  • Añade o elimina la clase del panel.
  • Actualiza el botón.
  • Modifica aria-expanded.
  • Cambia la etiqueta accesible.
  • Añade una clase al body.

Esta última clase permite impedir que la página continúe desplazándose mientras el menú ocupa la pantalla.

body.menu-open {
  overflow: hidden;
}

Centralizar el comportamiento evita inconsistencias. Si en el futuro añadimos nuevas formas de cerrar el menú, todas podrán llamar a la misma función.

Cómo transformar el icono hamburguesa en una cruz

Las líneas del botón también pueden animarse para indicar que el menú se encuentra abierto.

.menu-toggle.is-active .menu-toggle__line:nth-child(1) {
  transform: translateY(0.56rem) rotate(45deg);
}

.menu-toggle.is-active .menu-toggle__line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.menu-toggle.is-active .menu-toggle__line:nth-child(3) {
  transform: translateY(-0.56rem) rotate(-45deg);
}

La primera y la tercera línea se desplazan hacia el centro y rotan. La línea central desaparece mediante opacity y scaleX.

El resultado es una transición reconocible entre el icono de menú y el icono de cierre.

Ajustar la transformación sin valores frágiles

El desplazamiento de las líneas depende del tamaño del botón, del espacio disponible y de la distribución interna. Por ello, no conviene copiar valores de otro proyecto sin revisarlos.

Una alternativa más controlable consiste en posicionar las líneas de forma absoluta:

.menu-toggle {
  position: relative;
}

.menu-toggle__line {
  position: absolute;
  left: 25%;
  width: 50%;
}

.menu-toggle__line:nth-child(1) {
  top: 32%;
}

.menu-toggle__line:nth-child(2) {
  top: 50%;
  transform: translateY(-50%);
}

.menu-toggle__line:nth-child(3) {
  bottom: 32%;
}

.menu-toggle.is-active .menu-toggle__line:nth-child(1) {
  top: 50%;
  transform: translateY(-50%) rotate(45deg);
}

.menu-toggle.is-active .menu-toggle__line:nth-child(2) {
  opacity: 0;
  transform: translateY(-50%) scaleX(0);
}

.menu-toggle.is-active .menu-toggle__line:nth-child(3) {
  bottom: 50%;
  transform: translateY(50%) rotate(-45deg);
}

Esta solución ofrece un mayor control sobre el punto en el que las líneas se cruzan.

Animar los enlaces del menú de forma escalonada

Además del panel, podemos animar cada enlace con un pequeño retraso. Este recurso se conoce habitualmente como animación escalonada o staggered animation.

.mobile-menu__list {
  display: grid;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.mobile-menu__list li {
  opacity: 0;
  transform: translateX(1.25rem);
  transition:
    transform 300ms var(--menu-easing),
    opacity 300ms ease;
}

.mobile-menu.is-open .mobile-menu__list li {
  opacity: 1;
  transform: translateX(0);
}

Después podemos aplicar diferentes retrasos:

.mobile-menu.is-open .mobile-menu__list li:nth-child(1) {
  transition-delay: 80ms;
}

.mobile-menu.is-open .mobile-menu__list li:nth-child(2) {
  transition-delay: 120ms;
}

.mobile-menu.is-open .mobile-menu__list li:nth-child(3) {
  transition-delay: 160ms;
}

.mobile-menu.is-open .mobile-menu__list li:nth-child(4) {
  transition-delay: 200ms;
}

.mobile-menu.is-open .mobile-menu__list li:nth-child(5) {
  transition-delay: 240ms;
}

El desfase debe ser breve. Si cada elemento tarda demasiado en aparecer, el usuario tendrá que esperar para encontrar el enlace que necesita.

Una animación escalonada funciona mejor cuando:

  • Hay pocos elementos.
  • Los retrasos son reducidos.
  • El movimiento conserva una misma dirección.
  • El contenido continúa siendo usable con rapidez.

Utilizar una variable CSS para controlar el retraso

Podemos evitar varios selectores nth-child utilizando una variable personalizada:

<li style="--item-index: 0"><a href="/">Inicio</a></li>
<li style="--item-index: 1"><a href="/servicios/">Servicios</a></li>
<li style="--item-index: 2"><a href="/proyectos/">Proyectos</a></li>
<li style="--item-index: 3"><a href="/blog/">Blog</a></li>
<li style="--item-index: 4"><a href="/contacto/">Contacto</a></li>
.mobile-menu.is-open .mobile-menu__list li {
  transition-delay: calc(70ms + var(--item-index) * 40ms);
}

Así, la duración puede ajustarse desde un único lugar.

Añadir una capa oscura detrás del menú

Cuando el menú aparece como un panel lateral, resulta útil oscurecer el contenido situado detrás. Esta capa dirige la atención y comunica que la página principal se encuentra temporalmente inactiva.

Podemos crearla mediante un pseudoelemento:

.site-header::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 5;
  background: rgb(0 0 0 / 45%);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--menu-duration) ease,
    visibility 0s linear var(--menu-duration);
}

.site-header:has(.mobile-menu.is-open)::before {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--menu-duration) ease,
    visibility 0s linear 0s;
}

La pseudoclase :has() permite modificar el encabezado cuando contiene un menú abierto.

También podemos crear una capa independiente en el HTML. Esta alternativa resulta práctica cuando queremos cerrar el menú al pulsar fuera del panel.

<div class="menu-overlay" aria-hidden="true"></div>
.menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 5;
  background: rgb(0 0 0 / 45%);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--menu-duration) ease,
    visibility 0s linear var(--menu-duration);
}

.menu-overlay.is-visible {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

Y actualizamos JavaScript:

const menuOverlay = document.querySelector(".menu-overlay");

function setMenuState(isOpen) {
  mobileMenu.classList.toggle("is-open", isOpen);
  menuButton.classList.toggle("is-active", isOpen);
  menuOverlay.classList.toggle("is-visible", isOpen);

  menuButton.setAttribute("aria-expanded", String(isOpen));
  menuButton.setAttribute(
    "aria-label",
    isOpen ? "Cerrar menú" : "Abrir menú"
  );

  document.body.classList.toggle("menu-open", isOpen);
}

menuOverlay.addEventListener("click", () => {
  setMenuState(false);
});

Cerrar el menú con la tecla Escape

Un menú accesible debería poder cerrarse mediante la tecla Escape.

document.addEventListener("keydown", (event) => {
  if (event.key !== "Escape") {
    return;
  }

  const isOpen =
    menuButton.getAttribute("aria-expanded") === "true";

  if (isOpen) {
    setMenuState(false);
    menuButton.focus();
  }
});

Al cerrar el panel, devolvemos el foco al botón que lo abrió. De esta forma, quien navega mediante teclado no pierde su posición en la interfaz.

También puede ser conveniente cerrar el menú después de seleccionar un enlace:

mobileMenu.addEventListener("click", (event) => {
  const clickedLink = event.target.closest("a");

  if (clickedLink) {
    setMenuState(false);
  }
});

Esta medida resulta especialmente útil en páginas de una sola sección, donde los enlaces llevan a distintos puntos del mismo documento.

Animaciones alternativas para un menú móvil

El desplazamiento lateral es una solución frecuente, pero no es la única forma de animar un menú CSS.

Menú desplegable desde la parte superior

Podemos hacer que el menú aparezca debajo del encabezado.

.mobile-menu {
  position: absolute;
  top: 100%;
  right: 0;
  left: 0;
  min-height: auto;
  transform: translateY(-1rem);
  opacity: 0;
  visibility: hidden;
  transition:
    transform 250ms ease,
    opacity 250ms ease,
    visibility 0s linear 250ms;
}

.mobile-menu.is-open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

Esta variante funciona bien en menús cortos que no necesitan ocupar toda la pantalla.

Menú a pantalla completa

Un menú superpuesto puede ocupar todo el viewport:

.mobile-menu {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: 5rem 2rem 2rem;
  background: var(--menu-background);
  opacity: 0;
  transform: scale(1.03);
  visibility: hidden;
  transition:
    opacity 300ms ease,
    transform 400ms var(--menu-easing),
    visibility 0s linear 400ms;
}

.mobile-menu.is-open {
  opacity: 1;
  transform: scale(1);
  visibility: visible;
  transition-delay: 0s;
}

El cambio de escala debe ser sutil. Una ampliación excesiva puede resultar agresiva o generar una sensación de movimiento innecesaria.

Aparición mediante clip-path

Otra opción visual consiste en revelar el menú progresivamente con clip-path.

.mobile-menu {
  position: fixed;
  inset: 0;
  clip-path: circle(0 at calc(100% - 2.5rem) 2.5rem);
  visibility: hidden;
  transition:
    clip-path 500ms var(--menu-easing),
    visibility 0s linear 500ms;
}

.mobile-menu.is-open {
  clip-path: circle(150% at calc(100% - 2.5rem) 2.5rem);
  visibility: visible;
  transition-delay: 0s;
}

Este efecto puede resultar atractivo, pero debería comprobarse en los navegadores y dispositivos incluidos en los requisitos del proyecto.

La solución más llamativa no siempre es la más adecuada. Para la mayoría de las interfaces, una transición sencilla con transform y opacity será más fácil de mantener.

Cómo respetar prefers-reduced-motion

Algunas personas reducen las animaciones en el sistema operativo porque el movimiento puede provocar molestias, mareos o dificultades de concentración.

La media query prefers-reduced-motion permite adaptar la experiencia.

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }
}

También podemos aplicar una versión más específica:

@media (prefers-reduced-motion: reduce) {
  .mobile-menu,
  .mobile-menu__list li,
  .menu-toggle__line,
  .menu-overlay {
    transition: none;
  }
}

La segunda opción evita modificar todas las animaciones del proyecto y se limita al componente.

Respetar esta preferencia no significa eliminar funcionalidad. El menú debe continuar abriéndose y cerrándose, pero sin una transición prolongada.

Adaptar el menú a escritorio

Un menú móvil no debería continuar oculto detrás de un botón cuando existe espacio suficiente para mostrar la navegación completa.

@media (min-width: 48rem) {
  .menu-toggle,
  .menu-overlay {
    display: none;
  }

  .mobile-menu {
    position: static;
    display: block;
    width: auto;
    min-height: auto;
    padding: 0;
    background: transparent;
    box-shadow: none;
    transform: none;
    visibility: visible;
  }

  .mobile-menu__list {
    display: flex;
    align-items: center;
    gap: 1.5rem;
  }

  .mobile-menu__list li {
    opacity: 1;
    transform: none;
    transition: none;
  }

  body.menu-open {
    overflow: auto;
  }
}

El punto de ruptura no debería elegirse únicamente por un dispositivo concreto. Conviene observar el momento en el que los enlaces dejan de caber cómodamente en una línea.

Un menú puede necesitar transformarse en móvil a 768 píxeles, 900 píxeles o cualquier otra anchura, dependiendo de:

  • La cantidad de enlaces.
  • La longitud de las etiquetas.
  • El tamaño del logotipo.
  • El espacio entre elementos.
  • El idioma del sitio.
  • El diseño general del encabezado.

Errores frecuentes al animar un menú móvil

Animar display: none

La propiedad display no puede interpolarse como opacity o transform.

Este código no producirá una transición gradual:

.mobile-menu {
  display: none;
  opacity: 0;
}

.mobile-menu.is-open {
  display: block;
  opacity: 1;
}

Cuando el elemento pasa de display: none a display: block, aparece directamente. Después puede aplicarse la opacidad, pero el proceso no siempre ofrece el resultado esperado.

Para controlar la interacción y la visibilidad durante una transición, podemos combinar:

  • opacity
  • transform
  • visibility
  • pointer-events

Ocultar el menú solo con opacity

Un elemento con opacity: 0 continúa ocupando espacio y puede seguir recibiendo clics o foco.

Por ello, este código es insuficiente:

.mobile-menu {
  opacity: 0;
}

Podemos reforzarlo así:

.mobile-menu {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.mobile-menu.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

Utilizar duraciones demasiado largas

Un menú es una herramienta funcional. El usuario quiere acceder rápidamente a otra sección.

Las transiciones de entre aproximadamente 200 y 400 milisegundos suelen ofrecer una sensación fluida sin introducir una espera evidente. No obstante, la duración adecuada dependerá del tamaño del panel y de la distancia recorrida.

Un panel que atraviesa toda la pantalla puede necesitar algo más de tiempo que un pequeño cambio de opacidad, pero rara vez requiere una animación de un segundo completo.

Sobrecargar el componente con efectos

Mover el panel, escalarlo, rotarlo, difuminarlo y animar simultáneamente todos sus enlaces puede dificultar la lectura.

Una buena regla práctica consiste en elegir:

  1. Una animación principal para el contenedor.
  2. Una transformación clara para el botón.
  3. Una animación secundaria opcional para los enlaces.

Más efectos no implican una mejor experiencia.

No comprobar la navegación mediante teclado

El menú puede parecer correcto con el ratón y presentar problemas al utilizar la tecla Tab.

Antes de darlo por terminado, conviene comprobar que:

  • El botón recibe el foco.
  • El estado del foco es visible.
  • Los enlaces ocultos no pueden enfocarse.
  • El menú puede cerrarse con Escape.
  • El foco vuelve al botón al cerrar.
  • El orden de navegación es lógico.

Prestar atención al control del foco

En un menú que ocupa toda la pantalla, puede ser recomendable mantener temporalmente el foco dentro del panel mientras permanece abierto. Esto se conoce como focus trapping.

No todos los menús desplegables necesitan comportarse como un diálogo modal. Sin embargo, cuando la navegación bloquea completamente el resto de la interfaz, conviene estudiar si el foco debería quedar limitado a sus controles.

Consejos para conseguir una animación más profesional

Mantén una dirección coherente

Si el panel entra desde la derecha, los enlaces pueden aparecer desplazándose ligeramente desde esa misma dirección. Esta coherencia ayuda a que el movimiento resulte predecible.

Utiliza una curva de aceleración adecuada

La función ease puede ser suficiente, pero una curva personalizada permite controlar mejor la sensación de movimiento.

transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);

Esta curva genera una entrada rápida que se suaviza al llegar al destino. El menú se percibe ágil sin detenerse de forma brusca.

Evita utilizar will-change permanentemente

La propiedad will-change puede avisar al navegador de que un elemento cambiará próximamente:

.mobile-menu {
  will-change: transform;
}

Sin embargo, no debería aplicarse indiscriminadamente a muchos componentes. Mantener capas optimizadas de forma permanente puede aumentar el consumo de recursos.

En un menú pequeño quizá no sea necesaria. Conviene utilizarla únicamente cuando las pruebas de rendimiento muestren una mejora real.

Prueba en dispositivos reales

Las herramientas del navegador permiten simular diferentes anchuras, pero no reproducen por completo:

  • El rendimiento de un teléfono de gama media o baja.
  • La interacción táctil.
  • La barra dinámica del navegador.
  • Los cambios de orientación.
  • El comportamiento del teclado virtual.
  • Las áreas seguras de algunos dispositivos.

Para la altura del panel, 100dvh puede adaptarse mejor a los cambios de la interfaz del navegador que el tradicional 100vh.

Lista de comprobación antes de publicar

Antes de dar por finalizado el menú hamburguesa CSS, revisa los siguientes puntos:

  • El botón es un elemento <button>.
  • Incluye aria-controls y aria-expanded.
  • El texto accesible cambia entre abrir y cerrar.
  • El menú oculto no puede recibir el foco.
  • La animación utiliza preferentemente transform y opacity.
  • El contenido no se desplaza accidentalmente al abrir el panel.
  • Existe una forma clara de cerrar el menú.
  • La tecla Escape funciona.
  • El foco es visible.
  • Se respeta prefers-reduced-motion.
  • El menú se adapta correctamente a escritorio.
  • Los enlaces tienen un área táctil suficiente.
  • La duración de la transición no retrasa la navegación.
  • El comportamiento se ha probado con teclado y pantalla táctil.

Esta revisión permite detectar problemas que pueden pasar desapercibidos cuando solo se evalúa la apariencia visual.

Preguntas frecuentes sobre cómo animar un menú móvil con CSS

¿Es posible crear un menú móvil animado solo con CSS?

Sí. Puede utilizarse un checkbox oculto junto con un <label> y selectores como :checked para modificar el estado del menú.

No obstante, esta solución puede complicar la semántica y la actualización de atributos accesibles. En proyectos reales suele resultar más mantenible utilizar CSS para la presentación y una pequeña cantidad de JavaScript para gestionar el estado.

¿Qué propiedades CSS conviene utilizar para animar un menú?

Las propiedades más recomendables son transform y opacity. Permiten desplazar, escalar o desvanecer el menú sin depender continuamente de cambios en su geometría.

Propiedades como width, height, left, right o margin pueden utilizarse, pero suelen requerir más trabajo del navegador durante la animación. Siempre conviene comprobar el resultado con las herramientas de rendimiento.

¿Cuánto debería durar la animación de un menú hamburguesa?

La duración debe ser suficiente para que el cambio de estado se entienda, pero no tan larga como para retrasar el acceso a los enlaces.

Como referencia inicial, pueden probarse valores entre 200 y 400 milisegundos. Los efectos secundarios, como la aparición escalonada de los enlaces, deberían utilizar retrasos pequeños para evitar que la navegación se sienta lenta.

El movimiento debe acompañar a la navegación, no competir con ella

Aprender cómo animar un menú móvil con CSS implica algo más que elegir un efecto llamativo. El componente debe ser comprensible, rápido, accesible y fácil de mantener.

Una animación lateral mediante transform, acompañada de una capa de fondo y una transformación sencilla del botón, suele ser suficiente para crear una experiencia clara. A partir de esa base pueden añadirse detalles como la entrada escalonada de los enlaces, siempre que no retrasen la interacción.

También es importante recordar que el menú es una de las principales vías de acceso al contenido. Si su apertura resulta confusa, si los enlaces ocultos reciben el foco o si el movimiento genera molestias, la animación deja de cumplir su función.

La mejor animación no es necesariamente la más compleja. Es aquella que hace visible el cambio de estado sin obligar al usuario a pensar en el propio efecto. Cuando el movimiento acompaña de forma natural a la navegación, el diseño se percibe más cuidado y la interfaz resulta más fácil de utilizar.

Emails accesibles: contraste, jerarquía y lectores de pantalla

La accesibilidad email no debería verse como un añadido técnico que se incorpora al final, cuando la newsletter ya está diseñada, maquetada y aprobada. En realidad, debería formar parte del proceso desde el primer boceto. Un email accesible es un email que se puede leer, entender y utilizar con menos esfuerzo, tanto por personas con discapacidad visual, motriz o cognitiva como por cualquier usuario que abre el correo en un móvil pequeño, con poca luz, con prisa o con las imágenes bloqueadas.

Cuando hablamos de newsletters accesibles, solemos pensar en lectores de pantalla, textos alternativos o normas WCAG. Todo eso importa, por supuesto. Pero la accesibilidad empieza mucho antes: en el contraste entre texto y fondo, en la jerarquía visual, en el tamaño de la tipografía, en la claridad de los enlaces, en el orden de lectura y en la forma en la que estructuramos el contenido.

Un email puede ser visualmente bonito y, aun así, resultar difícil de leer. También puede estar técnicamente bien maquetado, pero ser confuso para quien navega con tecnologías de asistencia. Por eso, diseñar emails accesibles no consiste solo en “cumplir una checklist”, sino en tomar mejores decisiones de comunicación.

Si estás empezando a trabajar con emails responsive, también puede interesarte leer qué es MJML y por qué facilita la maquetación de emails responsive, porque muchas decisiones de accesibilidad también dependen de cómo estructuramos el HTML del correo desde el principio.

Qué significa crear emails accesibles

Crear emails accesibles significa diseñar y desarrollar mensajes que puedan ser consumidos por el mayor número posible de personas, independientemente de sus capacidades, contexto o dispositivo. En email marketing, esto es especialmente importante porque el entorno es muy fragmentado: Gmail, Outlook, Apple Mail, Yahoo, clientes móviles, modo oscuro, imágenes bloqueadas, zoom de texto y distintas configuraciones de accesibilidad.

A diferencia de una web, donde tenemos algo más de control sobre HTML, CSS y JavaScript, en email development trabajamos con muchas limitaciones. Algunos clientes eliminan estilos, otros interpretan mal ciertas propiedades CSS y otros tienen un soporte irregular para atributos avanzados. Por eso, la accesibilidad en email debe apoyarse en una idea fundamental: hacerlo simple, robusto y legible.

Las WCAG del W3C son una de las referencias principales para entender los criterios de accesibilidad digital. Aunque están pensadas para contenido web en general, muchos de sus principios son perfectamente aplicables al email: que el contenido sea perceptible, operable, comprensible y robusto.

En la práctica, una newsletter accesible debe permitir que la persona usuaria pueda leer el contenido sin forzar la vista, entender qué información es prioritaria, identificar claramente botones y enlaces, navegar el email con lector de pantalla y acceder a una versión funcional aunque las imágenes no carguen.

La accesibilidad no resta diseño. Al contrario: mejora el diseño porque obliga a tomar decisiones más claras.

Contraste en emails: el primer filtro de legibilidad

El contraste es uno de los aspectos más importantes de la accesibilidad email. Si el texto no se distingue bien del fondo, el mensaje falla desde el principio. No importa que el copy esté muy trabajado, que el CTA sea persuasivo o que la newsletter tenga una composición preciosa: si cuesta leer, se pierde impacto.

Por qué el contraste no es solo una cuestión estética

Muchas veces se eligen colores por identidad visual, tendencia o armonía gráfica. Eso está bien, pero en email hay que ir un paso más allá. Un color corporativo puede funcionar muy bien como acento visual y, al mismo tiempo, no ser adecuado para texto pequeño sobre fondo claro. Un gris suave puede parecer elegante en una maqueta, pero volverse ilegible en una pantalla con bajo brillo.

Las WCAG recomiendan una relación de contraste mínima de 4.5:1 para texto normal y 3:1 para texto grande en nivel AA. Para componentes visuales importantes, como bordes de botones o iconos funcionales, el criterio de contraste no textual apunta a una relación mínima de 3:1 frente al color adyacente. Puedes comprobar combinaciones concretas con herramientas como el Contrast Checker de WebAIM.

Esto no significa que todos los emails tengan que ser en blanco y negro. Significa que debemos comprobar que las combinaciones elegidas funcionan. La accesibilidad visual no elimina la personalidad de marca; simplemente evita que la estética se imponga sobre la lectura.

Errores comunes de contraste en newsletters

Uno de los errores más habituales es usar gris claro sobre blanco para textos secundarios. En una landing puede parecer sutil; en un email, especialmente en móvil, puede convertirse en una barrera. Otro error frecuente es colocar texto blanco sobre imágenes sin añadir una capa de contraste suficiente. La imagen puede tener zonas claras y oscuras, y el texto puede quedar bien en una parte pero desaparecer en otra.

También conviene revisar los botones. Un CTA con fondo de color y texto blanco puede parecer correcto, pero no siempre cumple contraste. Lo mismo ocurre con botones secundarios que solo tienen borde: si el borde es demasiado tenue, algunas personas no identificarán el elemento como interactivo.

Este punto conecta muy bien con el diseño de interfaces en general. Si te interesa profundizar en cómo la accesibilidad afecta a patrones habituales de producto digital, puedes leer también el artículo sobre componentes UI accesibles.

Recomendación práctica para contraste

Una buena regla de trabajo es definir una pequeña paleta accesible para email. No hace falta complicarla demasiado, pero sí conviene tener claro qué colores se usan para texto principal, titulares, fondos, botones y elementos secundarios.

  • Un color principal para titulares y texto importante.
  • Un color de cuerpo con contraste alto.
  • Un color de fondo claro y seguro.
  • Un color de CTA validado.
  • Un color secundario que no se use para texto crítico si no cumple contraste.

En lugar de improvisar colores en cada campaña, conviene tener un pequeño sistema. Esto reduce errores, acelera la producción y hace que las newsletters accesibles sean más consistentes.

Jerarquía visual: guiar sin saturar

La jerarquía visual es la forma en que organizamos la información para que el lector entienda qué mirar primero, qué es secundario y qué acción debe realizar. En email, la jerarquía es todavía más importante porque la atención suele ser breve. Muchas personas escanean el mensaje antes de decidir si lo leen completo.

Una buena jerarquía no depende solo del tamaño de letra. También intervienen el peso tipográfico, el espacio, la longitud de los bloques, la posición de los elementos, la repetición de patrones y la claridad de los CTA.

El problema de las newsletters sin estructura

Un email sin jerarquía suele tener varios síntomas: titulares que no destacan, bloques de texto demasiado largos, botones que compiten entre sí, imágenes que ocupan demasiado espacio sin aportar información y enlaces dispersos sin prioridad clara.

Cuando todo parece importante, nada lo es. Y esto afecta tanto a la conversión como a la accesibilidad. Una persona con baja visión puede necesitar más esfuerzo para identificar la información clave. Una persona con dificultades cognitivas puede abandonar si el contenido parece denso o desordenado. Y una persona que utiliza lector de pantalla puede perder contexto si los encabezados no están bien planteados.

En este sentido, la jerarquía visual está muy relacionada con la carga cognitiva. Si un email exige demasiado esfuerzo para entender qué está pasando, la persona tarda más en decidir y tiene más posibilidades de abandonar. Sobre este tema, puedes ampliar con el artículo sobre la Ley de Miller y su impacto en la experiencia de usuario.

Cómo construir una jerarquía accesible en email

Una jerarquía accesible empieza con una pregunta sencilla: ¿qué necesita entender la persona en los primeros segundos?

Normalmente, una newsletter debería responder a estas cuestiones:

  • Quién envía el mensaje.
  • De qué trata.
  • Por qué me interesa.
  • Qué puedo hacer ahora.

El asunto y el preheader abren la experiencia. Después, el encabezado principal debe confirmar la promesa. A partir de ahí, el contenido debería dividirse en secciones claras, con subtítulos descriptivos y bloques breves.

Encabezado principal claro

El H1 o título visual del email debe ser directo. Evita titulares demasiado crípticos si el resto del mensaje no los aclara de inmediato. La creatividad está bien, pero la comprensión debe ir primero.

Subtítulos que ordenan el contenido

Los subtítulos ayudan a dividir bloques y permiten que una persona entienda el recorrido del email sin leer cada palabra. También ayudan a lectores de pantalla cuando la estructura HTML está correctamente definida.

CTAs visibles y específicos

Un botón con el texto “Haz clic aquí” no es tan claro como “Descargar la guía”, “Ver la colección” o “Reservar plaza”. Los enlaces deben tener sentido por sí mismos, porque algunas personas navegan saltando de enlace en enlace con tecnologías de asistencia.

La WAI recomienda que los textos de enlace sean significativos y comprensibles fuera de contexto. Puedes consultar sus recomendaciones en la guía de consejos de escritura para accesibilidad.

Menos ruido, más intención

La jerarquía accesible también implica renunciar. No todos los elementos merecen el mismo peso visual. Si una newsletter tiene tres objetivos distintos, cinco botones y diez enlaces secundarios, probablemente necesite simplificarse.

Una buena newsletter accesible no grita. Acompaña.

Lectores de pantalla: pensar en el orden real del contenido

Los lectores de pantalla transforman el contenido visual en una experiencia auditiva o navegable mediante teclado. Esto cambia por completo la forma en la que debemos pensar el email. Una persona que ve la newsletter puede interpretar columnas, colores, iconos y agrupaciones visuales. Una persona que escucha el email necesita que el orden del HTML tenga sentido.

El orden visual no siempre coincide con el orden de lectura

En email development se utilizan muchas tablas para asegurar compatibilidad. Esto no es un problema por sí mismo, pero puede convertirse en uno si el orden del código no coincide con el orden lógico del mensaje.

Por ejemplo, imaginemos una newsletter con una imagen a la izquierda y un texto a la derecha. Visualmente se entiende bien. Pero si en el HTML el contenido se organiza de forma extraña, el lector de pantalla puede anunciar primero elementos secundarios, luego el CTA y después el titular. El resultado es una experiencia confusa.

Por eso, una de las mejores prácticas es comprobar el email no solo “mirándolo”, sino también revisando el flujo de lectura. El contenido debería tener sentido incluso si se elimina el diseño visual.

Texto alternativo en imágenes

El texto alternativo es clave para que una imagen importante no se pierda cuando alguien no puede verla o cuando el cliente de correo bloquea imágenes. Las alternativas textuales comunican el propósito de imágenes, ilustraciones o gráficos a personas que no las ven o que tienen las imágenes desactivadas.

Ahora bien, no todas las imágenes necesitan el mismo tipo de alt. Una imagen decorativa puede tener alt="" para que el lector de pantalla la ignore. Una imagen informativa necesita un texto alternativo claro. Y una imagen que contiene texto importante debería evitarse siempre que sea posible: es mejor que el texto esté en HTML real.

Ejemplos de alt text en newsletters

Si una imagen muestra un producto, un buen alt podría ser:

“Bolso de piel marrón con asa corta y cierre metálico.”

Si la imagen es un banner decorativo con formas abstractas, puede llevar:

alt=""

Si la imagen anuncia una oferta con texto incrustado, lo ideal es no depender solo de la imagen. El texto “20% de descuento hasta el domingo” debería aparecer también como texto HTML dentro del email.

Cuidado con ARIA en email

En desarrollo web, ARIA puede ayudar a mejorar la accesibilidad cuando se usa correctamente. Pero en email conviene ser prudente. El soporte de atributos ARIA y técnicas avanzadas no siempre es consistente entre clientes de correo. Algunos clientes eliminan atributos por seguridad o los interpretan de forma irregular.

Por eso, en email suele ser más seguro priorizar HTML simple, estructura clara, textos visibles y enlaces descriptivos. Si un botón necesita contexto, muchas veces es mejor escribir un texto visible más claro que depender de un atributo que podría no comportarse igual en todos los clientes.

Este criterio también aplica a otros componentes digitales. Por ejemplo, en interfaces web, un icono sin texto puede parecer limpio, pero resultar ambiguo si no tiene un nombre accesible. En ese caso, puedes ver ejemplos concretos en el artículo sobre iconos sin texto, aria-hidden y nombre accesible.

Tipografía, tamaño y espaciado en newsletters accesibles

La tipografía también forma parte de la accesibilidad. Un email con buen contraste puede seguir siendo difícil de leer si usa una fuente demasiado pequeña, líneas muy largas o poco espacio entre bloques.

En newsletters, especialmente en móvil, conviene trabajar con tamaños cómodos. Para el cuerpo del texto, una base de 16 px suele ser una opción razonable. Los textos legales o notas secundarias pueden ser algo más pequeños, pero no deberían convertirse en letra microscópica. El hecho de que un contenido sea secundario no significa que pueda ser ilegible.

Longitud de línea y bloques de texto

Los párrafos largos cansan. En email, donde el usuario suele desplazarse rápido, los bloques deben ser más breves que en un artículo de blog. Esto no significa escribir de forma superficial, sino presentar la información de manera digerible.

Un patrón útil es combinar párrafos cortos, subtítulos descriptivos, listas cuando ayuden a comparar o resumir, espacio suficiente entre secciones y un CTA principal por bloque.

La accesibilidad cognitiva también importa. Una newsletter clara reduce la carga mental y facilita la toma de decisiones.

Modo oscuro y accesibilidad

El modo oscuro añade otra capa de complejidad. Algunos clientes de correo invierten colores automáticamente, otros respetan ciertos estilos y otros aplican transformaciones parciales. Esto puede afectar al contraste, a los logotipos, a los fondos y a los botones.

Por eso, si se diseña una newsletter accesible, hay que probarla también en modo oscuro. Un logo oscuro en fondo transparente puede desaparecer. Un botón con color de marca puede cambiar de forma inesperada. Una sombra o borde muy sutil puede perderse por completo.

La solución no es evitar el modo oscuro, sino diseñar con suficiente robustez: colores sólidos, bordes visibles, imágenes preparadas y contraste validado en varios escenarios. Si quieres profundizar en este tema desde una perspectiva más amplia de diseño de interfaz, puedes leer Dark mode vs Light mode: cuándo usar cada uno y cómo implementarlos.

Botones y enlaces accesibles

Los botones son una parte crítica de cualquier campaña. Si el CTA no se entiende o no se puede activar cómodamente, la newsletter pierde eficacia.

Un botón accesible debe ser fácil de identificar, tener buen contraste, un área táctil cómoda y un texto claro. En móvil, no conviene crear botones demasiado pequeños ni enlaces pegados entre sí. La precisión táctil no es igual para todo el mundo, y un diseño demasiado apretado genera errores.

Textos de enlace que sí ayudan

Los enlaces deberían explicar la acción o el destino. En lugar de escribir:

“Haz clic aquí”

Es mejor usar:

“Leer la guía completa sobre accesibilidad email”

En lugar de:

“Más información”

Puede funcionar mejor:

“Ver ejemplos de newsletters accesibles”

Este tipo de texto no solo mejora la accesibilidad. También mejora la claridad editorial, la confianza del usuario y la calidad general de la experiencia.

Checklist práctica para crear newsletters accesibles

Antes de enviar una campaña, conviene revisar algunos puntos básicos. Esta checklist no sustituye una auditoría completa, pero ayuda a evitar muchos errores frecuentes en newsletters accesibles.

Contraste y color

  • El texto principal cumple al menos contraste 4.5:1.
  • Los titulares grandes mantienen contraste suficiente.
  • Los botones tienen contraste claro entre fondo y texto.
  • Los bordes o estados visuales importantes no dependen de colores demasiado sutiles.
  • La información no se comunica solo mediante color.

Estructura y jerarquía

  • El email tiene un objetivo principal claro.
  • El titular resume bien el contenido.
  • Los subtítulos ayudan a escanear.
  • Los bloques de texto son breves.
  • Hay una prioridad visual clara entre CTA principal y enlaces secundarios.

Lectores de pantalla

  • El orden del HTML tiene sentido.
  • Las imágenes importantes tienen alt descriptivo.
  • Las imágenes decorativas tienen alt vacío.
  • Los enlaces son comprensibles fuera de contexto.
  • No se depende exclusivamente de imágenes para comunicar información importante.
  • Existe una versión de texto plano cuando sea posible.

Mobile y modo oscuro

  • El texto se lee cómodamente en móvil.
  • Los botones tienen tamaño suficiente.
  • Los enlaces no están demasiado juntos.
  • El email se revisa en modo claro y oscuro.
  • Las imágenes no pierden sentido si se bloquean.

Errores frecuentes al diseñar emails accesibles

Aunque muchas decisiones de accesibilidad parecen sencillas, es fácil cometer errores cuando se trabaja con prisas, plantillas heredadas o campañas con demasiados elementos visuales. Estos son algunos de los problemas más habituales.

Usar imágenes como si fueran texto

Una imagen con todo el mensaje dentro puede parecer cómoda desde diseño, pero suele ser una mala práctica. Si la imagen no carga, el contenido desaparece. Si alguien usa lector de pantalla, dependerá por completo del texto alternativo. Y si el usuario necesita ampliar el texto, no podrá hacerlo con comodidad.

No probar el email sin imágenes

Muchas personas abren emails con imágenes bloqueadas. Por eso, una newsletter debería seguir teniendo sentido aunque los recursos visuales no carguen. El asunto, el preheader, los titulares, los textos y los botones principales deberían funcionar por sí mismos.

Crear demasiados CTAs

Un email con demasiadas llamadas a la acción aumenta la carga cognitiva. La persona no sabe qué decisión tomar, qué botón es prioritario o qué enlace debería seguir. En accesibilidad, la claridad también es una forma de cuidado.

Depender solo del color para comunicar estados

Si una oferta, aviso o mensaje importante se comunica únicamente mediante color, algunas personas pueden no percibirlo correctamente. Es mejor combinar color con texto, iconos comprensibles o etiquetas visibles.

Preguntas frecuentes sobre accesibilidad email

1. ¿La accesibilidad email solo importa para personas que usan lectores de pantalla?

No. Los lectores de pantalla son una parte importante, pero la accesibilidad email beneficia a muchas más personas. También ayuda a usuarios con baja visión, daltonismo, dificultades cognitivas, problemas de movilidad, sensibilidad a diseños saturados o personas que leen desde dispositivos pequeños. Además, mejora la experiencia general porque hace que el mensaje sea más claro, legible y fácil de usar.

2. ¿Puedo usar imágenes con texto dentro de una newsletter accesible?

Se puede, pero no es lo ideal si ese texto es importante. Muchos clientes de correo bloquean imágenes por defecto, y las personas que usan lector de pantalla dependerán del texto alternativo. Lo más recomendable es que la información clave esté siempre en HTML real. Si usas una imagen con texto, asegúrate de repetir esa información en el contenido visible o en un alt text adecuado.

3. ¿Las newsletters accesibles tienen peor diseño?

No. Esta es una idea bastante extendida, pero equivocada. Una newsletter accesible puede ser visualmente atractiva, tener personalidad de marca y usar recursos gráficos cuidados. La diferencia está en que cada decisión visual tiene una intención: buen contraste, jerarquía clara, textos legibles, botones reconocibles y estructura comprensible. La accesibilidad no limita el diseño; lo hace más sólido.

Diseñar emails que no excluyan

Crear newsletters accesibles no consiste en convertir cada campaña en un documento rígido y sin personalidad. Consiste en recordar que detrás de cada apertura hay una persona con un contexto distinto. Algunas leerán desde un móvil en la calle. Otras tendrán las imágenes bloqueadas. Otras escucharán el contenido con un lector de pantalla. Otras necesitarán más contraste, más claridad o menos ruido visual.

La accesibilidad en email es una forma de respeto. También es una forma de eficacia. Un mensaje que se lee mejor, se entiende mejor y se puede usar mejor tiene más posibilidades de cumplir su objetivo.

Por eso, antes de enviar tu próxima campaña, merece la pena hacer una pausa y preguntarte: ¿este email se entiende si no veo las imágenes?, ¿se lee bien en una pantalla pequeña?, ¿el CTA es claro?, ¿el contraste acompaña o dificulta?, ¿la estructura ayuda o confunde?

Si la respuesta no es clara, ahí tienes una oportunidad de mejora.

Diseñar emails accesibles no es añadir una capa técnica al final. Es construir desde el principio una comunicación más clara, más inclusiva y más profesional. Y en un entorno tan competitivo como la bandeja de entrada, esa claridad puede marcar la diferencia.