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.

Cómo organizar componentes reutilizables en MJML

Cuando empiezas a maquetar emails con MJML, la primera sensación suele ser bastante agradable: todo parece más limpio que trabajar directamente con tablas HTML, las columnas son más fáciles de entender y la estructura general del email resulta menos intimidante. MJML está pensado precisamente para reducir la complejidad de crear emails responsive, usando una sintaxis más legible que después se transforma en HTML compatible con distintos clientes de correo.

Pero hay un punto en el que esa comodidad inicial puede empezar a desordenarse: cuando dejas de tener una sola newsletter puntual y pasas a mantener varias campañas, automatizaciones, emails transaccionales, cabeceras, footers, banners, bloques de producto, llamadas a la acción y versiones para distintos públicos.

Ahí aparece la gran pregunta: cómo organizar componentes reutilizables en MJML sin convertir el proyecto en una carpeta llena de archivos imposibles de mantener.

La respuesta no está solo en “separar archivos”. Está en pensar el email como un pequeño sistema de diseño: con piezas reutilizables, estilos globales, nombres claros, componentes con una responsabilidad concreta y una estructura que permita escalar sin duplicar código en cada campaña.

Si estás dando tus primeros pasos con esta tecnología, antes puede ayudarte leer qué es MJML y por qué facilita la maquetación de emails responsive. Y si ya vienes de trabajar con HTML tradicional, también te recomiendo revisar la comparación entre MJML vs HTML tradicional para emails, porque entender esas diferencias ayuda mucho a organizar mejor cualquier proyecto.

Por qué tiene sentido crear componentes reutilizables en MJML

Un email rara vez es una pieza completamente nueva desde cero. Aunque cambie el contenido, muchas partes suelen repetirse: el encabezado, el logo, el footer legal, los botones, los separadores, los módulos de imagen más texto, las tarjetas de producto, las llamadas a la acción o los bloques de artículos recomendados.

Si cada email copia y pega esos bloques manualmente, tarde o temprano aparecen problemas: cambios de marca aplicados en unos emails sí y en otros no, botones con paddings diferentes, footers legales desactualizados, errores visuales por duplicar estructuras complejas y mucho tiempo perdido revisando detalles que ya deberían estar resueltos.

Aquí es donde la reutilización aporta valor. Organizar componentes reutilizables en MJML permite convertir piezas repetidas en bloques consistentes, fáciles de mantener y más seguros para producción.

MJML ya trabaja con una lógica basada en componentes. Elementos como <mj-section>, <mj-column>, <mj-text>, <mj-image> o <mj-button> permiten construir emails a partir de bloques comprensibles. La clave está en dar un paso más: no usar solo los componentes base de MJML, sino crear una organización propia para que el proyecto tenga una lógica de sistema.

Esto cobra todavía más importancia cuando trabajas newsletters, campañas recurrentes o automatizaciones. En esos casos, no solo estás diseñando un email; estás creando una pequeña biblioteca de piezas que vas a reutilizar muchas veces.

Qué entendemos por componentes reutilizables en MJML

Cuando hablamos de componentes MJML reutilizables, no nos referimos necesariamente a componentes como los de React, Vue o Svelte. En MJML, la reutilización puede adoptar varias formas.

Puede ser un archivo parcial que se incluye en varias plantillas, una sección completa que se repite, un conjunto de estilos globales dentro de <mj-attributes>, una clase reutilizable con mj-class o incluso un bloque de contenido generado desde Node.js antes de compilar el MJML final.

Lo importante es que el componente tenga una función clara y pueda reutilizarse sin tener que reescribirlo cada vez.

<mj-section background-color="#F8E0EA" padding="32px 24px">
  <mj-column>
    <mj-text font-size="24px" font-weight="700" color="#020101">
      Título de la campaña
    </mj-text>

    <mj-text font-size="16px" line-height="1.6" color="#333333">
      Texto introductorio del email.
    </mj-text>

    <mj-button background-color="#CC2B5E" color="#ffffff" border-radius="8px">
      Leer más
    </mj-button>
  </mj-column>
</mj-section>

Este bloque podría ser un hero reutilizable. Pero si lo copiamos y pegamos en diez emails, no estamos reutilizando de verdad: solo estamos duplicando una estructura. Para que sea realmente reutilizable, lo ideal es aislarlo dentro de una arquitectura clara.

Una estructura de carpetas clara para proyectos MJML

La organización de carpetas es una de las decisiones más importantes. No hace falta complicarla demasiado, pero sí conviene separar las piezas según su responsabilidad.

Una estructura práctica podría ser esta:

emails/
  templates/
    newsletter.mjml
    welcome.mjml
    password-reset.mjml

  components/
    header.mjml
    footer.mjml
    hero.mjml
    button-primary.mjml
    article-card.mjml
    product-card.mjml
    divider.mjml

  layouts/
    base.mjml
    marketing-layout.mjml
    transactional-layout.mjml

  styles/
    attributes.mjml
    fonts.mjml
    head.mjml

  data/
    newsletter.json
    products.json

  dist/
    newsletter.html
    welcome.html

Esta estructura separa varios niveles. Las templates son los emails finales, como una newsletter, un email de bienvenida o una recuperación de contraseña. Los components son piezas reutilizables pequeñas o medianas, como cabeceras, botones, tarjetas o bloques de contenido.

Los layouts definen estructuras generales. Por ejemplo, un layout de marketing con hero y CTA, o un layout transaccional más sobrio. Los archivos de styles agrupan configuración global, fuentes, clases, atributos y estilos comunes. Y la carpeta dist contiene el HTML final compilado, listo para probar o integrar en una herramienta de email marketing.

La ventaja de esta separación es que cada archivo tiene un papel claro. No mezclas el contenido de una campaña concreta con el estilo global de todos los emails, ni duplicas el footer en cada plantilla.

Cómo usar mj-include para dividir archivos

Una de las formas más directas de reutilizar piezas en MJML es usar <mj-include>. Este recurso permite incluir otros archivos dentro de una plantilla principal, lo que resulta muy útil para cabeceras, footers, bloques repetidos o estilos compartidos.

Por ejemplo, una plantilla principal podría verse así:

<mjml>
  <mj-head>
    <mj-include path="../styles/attributes.mjml" />
  </mj-head>

  <mj-body background-color="#ffffff">
    <mj-include path="../components/header.mjml" />
    <mj-include path="../components/hero.mjml" />
    <mj-include path="../components/article-card.mjml" />
    <mj-include path="../components/footer.mjml" />
  </mj-body>
</mjml>

Esto hace que el email final sea mucho más fácil de leer. En lugar de enfrentarte a un archivo enorme de cientos de líneas, puedes ver la composición general del email de un vistazo.

Cuándo usar archivos parciales

Los parciales funcionan muy bien cuando una pieza se repite en varios emails y no cambia demasiado. Por ejemplo: header corporativo, footer legal, bloque de redes sociales, separadores visuales, banner promocional, módulo de artículo destacado, CTA final o bloque de aviso legal.

La clave está en no crear parciales para absolutamente todo. Si fragmentas demasiado, puedes acabar con una arquitectura difícil de seguir. Un componente reutilizable debe tener una razón clara para existir.

Qué no conviene convertir en componente

No todo merece ser un componente. Si un bloque solo aparece una vez y no tiene previsión de repetirse, quizá sea mejor dejarlo dentro de la plantilla.

Tampoco conviene crear componentes demasiado específicos, como este:

hero-black-friday-2026-discount-30.mjml

Ese archivo difícilmente será reutilizable. En cambio, tendría más sentido crear algo como esto:

hero-campaign.mjml

Después puedes modificar el contenido desde la plantilla, desde variables o desde una fase previa de renderizado si trabajas con Node.js.

Centralizar estilos con mj-attributes y mj-class

Una parte esencial de los componentes reutilizables en MJML es evitar repetir estilos en cada bloque. Si todos los botones tienen el mismo color, border-radius, padding y tipografía, no tiene sentido escribir esos atributos una y otra vez.

MJML permite definir estilos globales dentro de <mj-head>, especialmente mediante <mj-attributes>. Esta técnica ayuda a establecer valores por defecto para componentes como <mj-text>, <mj-button> o <mj-section>.

<mj-attributes>
  <mj-text
    font-family="Arial, sans-serif"
    font-size="16px"
    line-height="1.6"
    color="#020101"
  />

  <mj-button
    font-family="Arial, sans-serif"
    font-size="16px"
    font-weight="700"
    background-color="#CC2B5E"
    color="#ffffff"
    border-radius="8px"
    padding="16px 24px"
  />

  <mj-section padding="24px" />
</mj-attributes>

Con esta configuración, cada vez que uses <mj-text> o <mj-button>, partirás de una base común. Después podrás sobrescribir atributos concretos si una pieza necesita un ajuste específico.

Esta idea conecta muy bien con otras decisiones de diseño y desarrollo frontend: crear una base común, reducir la repetición y mantener criterios consistentes. Es el mismo principio que aplicamos cuando organizamos estilos, componentes o patrones visuales en una web.

Crear variantes con mj-class

Además de estilos globales, puedes crear clases reutilizables con mj-class. Por ejemplo:

<mj-attributes>
  <mj-class
    name="text-muted"
    color="#666666"
    font-size="14px"
    line-height="1.5"
  />

  <mj-class
    name="heading-lg"
    font-size="28px"
    font-weight="700"
    line-height="1.3"
    color="#020101"
  />

  <mj-class
    name="button-secondary"
    background-color="#753A88"
    color="#ffffff"
    border-radius="8px"
  />
</mj-attributes>

Después puedes aplicarlas así:

<mj-text mj-class="heading-lg">
  Novedades de la semana
</mj-text>

<mj-text mj-class="text-muted">
  Una selección breve de recursos para mejorar tus emails.
</mj-text>

<mj-button mj-class="button-secondary">
  Ver recursos
</mj-button>

Este enfoque se parece mucho a trabajar con tokens o utilidades de diseño. Ayuda a mantener consistencia sin llenar cada componente de atributos repetidos.

Consejo práctico para nombres de clases

Los nombres deben explicar intención, no apariencia temporal. Es mejor usar nombres como heading-lg, text-muted, button-primary o section-soft que nombres como text-gray, button-purple o pink-section.

¿Por qué? Porque la marca puede cambiar de color, pero la intención del componente se mantiene. Un botón primario seguirá siendo primario aunque mañana cambie de rosa a azul.

Diseñar componentes por responsabilidad, no por campaña

Uno de los errores más comunes al organizar reusable email components es pensar en campañas concretas en lugar de pensar en responsabilidades.

Por ejemplo, podrías tener un archivo llamado:

newsletter-january-header.mjml

Pero eso lo ata demasiado a una campaña concreta. En cambio, conviene pensar en algo más reutilizable:

header-brand.mjml

La pregunta útil es: qué hace este componente.

¿Presenta la marca? ¿Muestra una imagen destacada? ¿Agrupa un listado de artículos? ¿Cierra el email con información legal? ¿Guía hacia una acción principal? ¿Muestra una tarjeta de producto?

Si el nombre responde a la función, será más fácil reutilizarlo en distintos contextos.

Componentes pequeños, medianos y grandes

No todos los componentes tienen que tener el mismo tamaño. Puedes pensar en tres niveles.

Los componentes pequeños pueden ser botones, separadores, bloques de texto, badges o pequeños avisos. Los componentes medianos pueden ser tarjetas de artículo, bloques de producto, módulos de imagen más texto o llamadas a la acción. Y los componentes grandes pueden ser headers, footers, heroes, secciones completas o bloques de campaña.

Esta división ayuda a evitar dos extremos: componentes enormes que hacen demasiadas cosas o componentes tan pequeños que obligan a abrir veinte archivos para entender un email.

Ejemplo de sistema de componentes MJML para una newsletter

Imaginemos una newsletter mensual sobre desarrollo web. Podríamos definir las siguientes piezas:

components/
  header-brand.mjml
  hero-newsletter.mjml
  intro-text.mjml
  article-card.mjml
  resource-list.mjml
  cta-block.mjml
  footer-legal.mjml

La plantilla final quedaría mucho más limpia:

<mjml>
  <mj-head>
    <mj-include path="../styles/head.mjml" />
    <mj-include path="../styles/attributes.mjml" />
  </mj-head>

  <mj-body background-color="#ffffff">
    <mj-include path="../components/header-brand.mjml" />
    <mj-include path="../components/hero-newsletter.mjml" />
    <mj-include path="../components/intro-text.mjml" />
    <mj-include path="../components/article-card.mjml" />
    <mj-include path="../components/resource-list.mjml" />
    <mj-include path="../components/cta-block.mjml" />
    <mj-include path="../components/footer-legal.mjml" />
  </mj-body>
</mjml>

Desde fuera, la plantilla cuenta una historia clara: cabecera, hero, introducción, contenido principal, recursos, CTA y cierre. Eso mejora mucho la mantenibilidad.

Si estás trabajando una primera newsletter desde cero, puedes ampliar esta parte con la guía sobre cómo crear tu primera newsletter responsive con MJML, donde la prioridad está más en la estructura inicial del email y no tanto en la arquitectura de componentes.

Separar estructura, contenido y presentación

Para proyectos pequeños, puedes mantener contenido y estructura juntos. Pero si vas a trabajar con muchas campañas, es mejor separar tres capas: estructura, contenido y presentación.

La estructura define qué bloques existen y en qué orden aparecen. El contenido incluye textos, enlaces, imágenes y datos variables. La presentación agrupa estilos globales, colores, tipografías, espaciados y clases.

Esta separación no tiene que ser perfecta desde el primer día. Pero cuanto más crece el proyecto, más se agradece.

Cómo trabajar con datos dinámicos y MJML

MJML puede integrarse en workflows con Node.js para generar HTML a partir de plantillas. Esto abre una posibilidad interesante: crear emails a partir de datos externos.

Por ejemplo, podrías tener un archivo JSON con el contenido de una newsletter:

{
  "title": "Recursos de frontend de la semana",
  "intro": "Una selección breve para mejorar tus proyectos.",
  "articles": [
    {
      "title": "Cómo mejorar la accesibilidad de tus formularios",
      "url": "https://example.com/articulo-1"
    },
    {
      "title": "Buenas prácticas para emails responsive",
      "url": "https://example.com/articulo-2"
    }
  ]
}

Después, con un script de Node.js, podrías generar el MJML final antes de compilarlo a HTML. En ese caso, MJML funciona como capa de presentación, mientras que los datos vienen de otra fuente: un CMS, una API, un JSON o una base de datos.

Este enfoque es muy útil cuando necesitas producir muchas variaciones de un email sin cambiar manualmente cada archivo. También encaja bien con un workflow frontend moderno, donde el código, los datos y la generación de archivos pueden formar parte de un proceso automatizado.

Buenas prácticas para nombrar componentes MJML

La nomenclatura parece un detalle menor, pero en proyectos reales marca una gran diferencia. Un buen nombre evita confusión y reduce el tiempo de decisión.

Conviene usar nombres descriptivos y funcionales. Es mejor article-card.mjml que block-1.mjml. También es preferible evitar nombres demasiado ligados a una campaña concreta. Por ejemplo, promo-banner.mjml será más útil a largo plazo que summer-sale-2026-banner.mjml.

Mantener una convención consistente también ayuda. Si usas kebab-case, úsalo en todo el proyecto: footer-legal.mjml, hero-simple.mjml, button-primary.mjml.

También puedes distinguir variantes cuando sea necesario. Por ejemplo: hero-simple.mjml, hero-image.mjml y hero-centered.mjml. Pero es importante no crear variantes innecesarias. Si dos componentes solo cambian una palabra o un color, quizá deberían ser el mismo componente con estilos o datos distintos.

Patrones que conviene evitar

Organizar componentes reutilizables en MJML también implica saber qué no hacer. El primer patrón a evitar es copiar y pegar bloques entre emails. Es rápido al principio, pero caro a medio plazo. Si el footer cambia, tendrás que actualizarlo en todos los emails donde lo hayas copiado.

Otro error habitual es crear componentes sin criterio. Separar por separar tampoco ayuda. Si cada <mj-text> vive en un archivo diferente, la plantilla será difícil de leer.

También conviene evitar mezclar estilos globales con estilos puntuales. Si cada componente redefine tipografías, colores y paddings sin seguir una base común, acabarás con un sistema inconsistente.

Por último, no hay que olvidar las limitaciones propias del email. MJML simplifica mucho la maquetación, pero los emails siguen dependiendo de clientes de correo con comportamientos distintos. Por eso es importante revisar qué partes de CSS son realmente seguras. Para profundizar en este punto, puedes leer qué partes de CSS funcionan realmente en email marketing y también el artículo sobre limitaciones reales del CSS en clientes de correo.

Organización recomendada para un workflow frontend moderno

Si trabajas en un entorno frontend moderno, puedes integrar MJML dentro de un flujo con scripts, control de versiones y generación automática.

Una estructura posible sería:

src/
  emails/
    templates/
    components/
    layouts/
    styles/
    data/

scripts/
  build-emails.js

dist/
  emails/

Y en el archivo package.json podrías tener scripts como estos:

{
  "scripts": {
    "build:emails": "node scripts/build-emails.js",
    "watch:emails": "mjml src/emails/templates -o dist/emails --watch"
  }
}

La idea es que los emails formen parte del proyecto, no que vivan como archivos sueltos en una carpeta perdida. Así puedes versionarlos, revisarlos en pull requests, automatizar la compilación y mantener consistencia entre campañas.

Este enfoque también facilita integrar MJML con otras herramientas del ecosistema frontend. Si te interesa esta parte más técnica, puedes continuar con el artículo sobre cómo integrar MJML en un workflow frontend moderno.

Documentar los componentes

Si el proyecto crece, conviene crear una pequeña documentación interna. No hace falta algo enorme. Puede bastar con un archivo README.md donde expliques qué componentes existen, cuándo usar cada uno, qué variantes hay, qué estilos globales están disponibles, cómo compilar los emails y qué criterios seguir antes de crear un componente nuevo.

Esta documentación evita que cada persona del equipo tome decisiones distintas. También reduce la carga cognitiva, porque no tienes que recordar de memoria cómo se construye cada bloque.

Accesibilidad y componentes reutilizables en emails

Cuando organizamos componentes MJML, no deberíamos pensar solo en la parte visual. También conviene tener en cuenta la accesibilidad.

Un componente reutilizable puede ayudar mucho si ya incluye buenas prácticas desde el principio: textos alternativos en imágenes relevantes, jerarquía clara, botones comprensibles, contraste suficiente y una estructura fácil de leer.

Por ejemplo, si tienes un componente de tarjeta de artículo, no debería limitarse a mostrar una imagen, un título y un botón genérico de “Leer más”. Sería más recomendable que el enlace o el botón tengan un contexto claro, especialmente para personas que navegan con lector de pantalla.

En lugar de repetir un patrón poco descriptivo en todos los emails, puedes resolverlo bien una vez y reutilizarlo en cada campaña. Para ampliar este enfoque, puedes revisar la guía sobre emails accesibles, contraste, jerarquía y lectores de pantalla.

Cómo decidir si un bloque merece ser componente

Antes de crear un nuevo componente, puedes hacerte varias preguntas. La primera es sencilla: ¿este bloque se va a repetir? Si la respuesta es sí, probablemente merece convertirse en componente.

También conviene preguntarse si tiene una responsabilidad clara. Un buen componente debe poder explicarse en una frase. Si necesitas demasiada explicación, quizá ese bloque está mezclando demasiadas funciones.

Otra pregunta útil es si depende demasiado de una campaña concreta. Si depende de una promoción única, quizá no sea reusable. En cambio, si representa un patrón que puede volver a aparecer, como una tarjeta, un CTA o un módulo de artículos, sí tiene sentido aislarlo.

Por último, piensa si ese componente mejorará la consistencia del sistema y reducirá errores futuros. Un footer legal, un botón principal o un bloque de baja de suscripción suelen beneficiarse mucho de estar centralizados.

La reutilización no consiste en crear más archivos, sino en reducir decisiones repetitivas. Un buen sistema de componentes debe hacer que crear un nuevo email sea más rápido, más claro y más fiable.

Preguntas frecuentes sobre componentes reutilizables en MJML

¿MJML permite crear componentes como en React?

No exactamente de la misma forma. MJML trabaja con su propio sistema de componentes y permite organizar archivos mediante inclusiones, atributos globales, clases y estructuras reutilizables. También existen formas más avanzadas de crear componentes personalizados, pero para la mayoría de proyectos de email marketing suele ser suficiente trabajar con parciales, layouts y estilos centralizados.

¿Cuándo debería usar mj-include?

Deberías usar <mj-include> cuando una parte del email se repite en varias plantillas o cuando separar un bloque mejora la legibilidad del archivo principal. Es especialmente útil para headers, footers, bloques legales, CTAs, módulos de artículos y estilos compartidos.

¿Es mejor tener muchos componentes pequeños o pocos componentes grandes?

Depende del proyecto, pero normalmente funciona mejor un equilibrio. Los componentes pequeños ayudan a reutilizar piezas concretas, mientras que los componentes medianos o grandes facilitan componer emails rápidamente. Lo importante es que cada componente tenga una responsabilidad clara y que la estructura completa siga siendo fácil de entender.

Reutilizar componentes también es diseñar mejor

Organizar componentes reutilizables en MJML no es solo una decisión técnica. También es una decisión de diseño, de mantenimiento y de experiencia de trabajo.

Un buen sistema de componentes evita repetir decisiones, reduce errores, mejora la coherencia visual y permite que cada nuevo email parta de una base sólida. En lugar de preguntarte cada vez cuánto padding debe tener un botón, cómo se construye el footer o qué estructura debe seguir una tarjeta, puedes apoyarte en un sistema ya definido.

Y eso tiene un impacto directo en la calidad final. Porque cuando la estructura está clara, puedes dedicar más energía a lo importante: el mensaje, la jerarquía, la claridad del contenido y la experiencia de quien recibe el email.

MJML ya te da una base mucho más amable que el HTML tradicional para emails. Pero la diferencia entre “usar MJML” y “trabajar bien con MJML” está en cómo organizas tus piezas.

Un proyecto con buenos reusable email components no solo es más cómodo para desarrollar: también es más consistente, más escalable y más fácil de mantener en el tiempo.

Testing responsive: cómo revisar una app en distintos tamaños de pantalla

Una aplicación puede verse perfecta en el ordenador utilizado durante el desarrollo y, aun así, presentar numerosos problemas cuando se abre desde un teléfono móvil, una tablet o una ventana de navegador más estrecha. Botones ocultos, textos cortados, columnas comprimidas o menús imposibles de cerrar son solo algunos de los errores que pueden aparecer.

Por este motivo, el testing responsive debe formar parte del proceso habitual de desarrollo. No debería reducirse a abrir las herramientas del navegador al final del proyecto y comprobar rápidamente dos o tres dispositivos preconfigurados.

Las pruebas responsive sirven para verificar que una interfaz se adapta correctamente a diferentes anchuras, alturas, orientaciones, navegadores y formas de interacción. El objetivo no es que la aplicación se vea exactamente igual en todas las pantallas, sino que mantenga su legibilidad, su jerarquía visual y sus funciones principales.

En este artículo veremos cómo revisar una app en distintos tamaños de pantalla, qué componentes requieren más atención, qué herramientas se pueden utilizar y cómo incorporar el testing responsive a un flujo de trabajo frontend.

Qué es el testing responsive

El testing responsive es el proceso de comprobar que una web o aplicación funciona correctamente en diferentes condiciones de visualización.

Aunque suele asociarse con la adaptación de una página a dispositivos móviles, una estrategia completa debe considerar muchos más factores:

  • Anchura y altura del viewport.
  • Orientación vertical y horizontal.
  • Densidad de píxeles.
  • Navegador y sistema operativo.
  • Interacción mediante ratón, teclado o pantalla táctil.
  • Zoom del navegador.
  • Tamaño de fuente configurado por la persona usuaria.
  • Aparición del teclado virtual.
  • Barras dinámicas de navegación en dispositivos móviles.
  • Contenido variable o cargado de forma asíncrona.

Por tanto, reducir manualmente la ventana del navegador puede servir como primera comprobación, pero no reproduce todas las situaciones que pueden darse en un dispositivo real.

Una buena estrategia de pruebas debe confirmar que la aplicación conserva cuatro características esenciales:

  1. El contenido continúa siendo legible.
  2. Las acciones importantes permanecen disponibles.
  3. La estructura mantiene una jerarquía comprensible.
  4. Los elementos interactivos pueden utilizarse con comodidad.

Cuando alguna de estas condiciones falla, el problema deja de ser únicamente visual. Un botón oculto detrás de una barra fija, por ejemplo, puede impedir completar una compra, enviar un formulario o guardar una configuración.

Diseño responsive no significa simplemente “versión móvil”

Un diseño responsive no debería entenderse como una versión de escritorio reducida hasta encajar en un teléfono. Cada tamaño de pantalla puede necesitar una distribución diferente.

Una cuadrícula de cuatro columnas puede convertirse en dos columnas en una tablet y en una sola columna en un móvil. Una navegación horizontal puede transformarse en un menú desplegable. Una tabla extensa puede necesitar desplazamiento propio o una representación alternativa mediante tarjetas.

La interfaz cambia, pero la tarea que la persona necesita completar debe seguir siendo posible.

Además, no todas las personas que utilizan un monitor grande navegan con el navegador maximizado. Pueden trabajar con dos ventanas colocadas en paralelo, abrir las herramientas de desarrollo o aumentar el zoom. Las pruebas responsive deben contemplar este rango continuo de situaciones y no únicamente una lista cerrada de dispositivos.

Qué tamaños de pantalla conviene probar

No resulta viable revisar manualmente todos los teléfonos, tablets y ordenadores existentes. Lo más eficaz es combinar tamaños representativos con pruebas específicas alrededor de los puntos de ruptura del diseño.

Crear una matriz básica de viewports

Como punto de partida, la matriz de pruebas puede incluir los siguientes escenarios:

  • Móvil estrecho.
  • Móvil de tamaño medio.
  • Móvil de gran formato.
  • Tablet en orientación vertical.
  • Tablet en orientación horizontal.
  • Portátil.
  • Escritorio.
  • Pantalla amplia.

No es necesario elegir únicamente las medidas de modelos comerciales concretos. El objetivo consiste en cubrir diferentes tipos de distribución y detectar cuándo el contenido deja de funcionar correctamente.

Una matriz sencilla podría utilizar estas medidas orientativas:

EscenarioAnchuraAltura
Móvil estrecho320 px568 px
Móvil estándar375 px667 px
Móvil ancho430 px932 px
Tablet vertical768 px1024 px
Tablet horizontal1024 px768 px
Portátil1366 px768 px
Escritorio1440 px900 px
Pantalla amplia1920 px1080 px

Estas medidas no representan una lista definitiva. Funcionan como puntos de referencia para empezar a revisar la interfaz.

Probar antes y después de cada breakpoint

Los errores responsive suelen aparecer en los tamaños intermedios, especialmente alrededor de las media queries.

Si la interfaz cambia su distribución a los 768 píxeles, conviene probar al menos:

  • 767 píxeles.
  • 768 píxeles.
  • 769 píxeles.

Esta comprobación permite detectar reglas que se pisan, elementos que desaparecen durante un píxel, saltos inesperados o componentes que conservan estilos de la distribución anterior.

También resulta útil arrastrar lentamente el lateral de la ventana. Esta prueba sencilla muestra cómo responde la interfaz de forma continua y ayuda a localizar el punto exacto en el que un bloque empieza a comprimirse o desbordarse.

Elegir breakpoints según el contenido

Los breakpoints no deberían establecerse únicamente porque coinciden con las anchuras habituales de determinados dispositivos.

Una estrategia más resistente consiste en observar el contenido. Cuando una navegación deja de caber, una cuadrícula comprime demasiado sus tarjetas o un formulario pierde legibilidad, se ha encontrado un punto en el que la composición necesita cambiar.

Este criterio evita diseñar para una lista de dispositivos que puede quedar desactualizada y permite construir interfaces más flexibles.

Qué revisar durante las pruebas responsive

Una revisión sin criterios definidos puede centrarse demasiado en el aspecto general y dejar pasar problemas funcionales. Conviene probar la aplicación por componentes y estados.

Estructura y distribución del contenido

El primer paso consiste en comprobar cómo se reorganizan los bloques principales.

Se debe revisar si:

  • Las columnas se apilan en un orden lógico.
  • Las tarjetas mantienen una anchura legible.
  • Los elementos no se superponen.
  • Las barras laterales cambian de posición correctamente.
  • Las cuadrículas reducen sus columnas sin comprimir el contenido.
  • Los márgenes y espacios siguen siendo coherentes.
  • No aparecen zonas vacías excesivas.
  • El contenido principal no se extiende demasiado en pantallas grandes.

Una cuadrícula con un número fijo de columnas puede funcionar correctamente en escritorio y romperse en una tablet. En muchos casos, CSS Grid permite crear una solución más flexible:

.cards-grid {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, 18rem), 1fr)
  );
  gap: 1.5rem;
}

Esta configuración permite que el navegador determine cuántas columnas caben sin reducir cada tarjeta por debajo de un tamaño razonable.

Textos y contenido de longitud variable

Los diseños suelen probarse inicialmente con textos de una longitud muy controlada. Sin embargo, el contenido real puede incluir títulos extensos, nombres largos, mensajes de validación o traducciones que ocupen más espacio.

Durante las pruebas responsive conviene utilizar:

  • Títulos de una sola palabra.
  • Títulos de varias líneas.
  • Nombres y apellidos largos.
  • Direcciones de correo electrónico.
  • URLs.
  • Cifras grandes.
  • Párrafos extensos.
  • Mensajes de error.
  • Estados vacíos.
  • Contenido traducido.

La finalidad es descubrir si el diseño depende de una cantidad concreta de caracteres.

Cuando una palabra extensa supera el ancho del contenedor, puede aplicarse una regla controlada:

.content {
  overflow-wrap: anywhere;
}

No obstante, el texto no debería recortarse por defecto. Propiedades como text-overflow: ellipsis o line-clamp pueden ser adecuadas en una tarjeta secundaria, pero no cuando ocultan información necesaria para completar una tarea.

Imágenes, vídeos y otros recursos multimedia

Las imágenes son una de las causas más frecuentes de desbordamiento horizontal.

Una regla básica puede evitar que superen la anchura de su contenedor:

img,
video {
  max-width: 100%;
  height: auto;
}

Aun así, durante las pruebas también debe comprobarse:

  • La relación de aspecto.
  • El recorte mediante object-fit.
  • Las imágenes de fondo.
  • Las miniaturas de las tarjetas.
  • Los vídeos incrustados.
  • Los mapas.
  • Los gráficos.
  • Los carruseles.
  • Las ilustraciones SVG.

Una imagen puede caber correctamente y, sin embargo, ocupar demasiada altura en móvil o perder parte de la información importante al recortarse.

Si la interfaz todavía se encuentra en fase de diseño, también puede resultar útil validar estos comportamientos antes de comenzar el desarrollo mediante prototipos interactivos en Figma. Probar diferentes distribuciones con antelación ayuda a detectar decisiones que podrían resultar difíciles de mantener posteriormente.

Navegación y menús

La navegación suele experimentar uno de los cambios más importantes entre escritorio y móvil.

Una barra horizontal puede convertirse en un botón que abre un menú desplegable, un panel lateral o una capa que ocupa toda la pantalla. Durante las pruebas se debe comprobar que:

  • El botón de apertura es visible.
  • El menú puede abrirse y cerrarse.
  • Las opciones mantienen un orden comprensible.
  • El contenido no queda oculto detrás del panel.
  • El desplazamiento de fondo se bloquea únicamente cuando corresponde.
  • El menú se cierra después de seleccionar una opción, si el flujo lo requiere.
  • El foco del teclado se gestiona adecuadamente.
  • La tecla Escape permite cerrar el panel cuando resulta apropiado.

También conviene cambiar el tamaño del viewport con el menú abierto. Puede ocurrir que la versión móvil bloquee el scroll del documento y que ese bloqueo se conserve al pasar a la distribución de escritorio.

Este tipo de incidencia demuestra por qué no basta con revisar cada tamaño de forma aislada. También deben probarse las transiciones entre ellos.

Formularios y teclado virtual

Los formularios requieren una revisión específica porque combinan contenido, interacción, validaciones y entrada de datos.

Al tocar un campo desde un dispositivo móvil, el teclado virtual reduce el área visible. En ese momento pueden aparecer problemas que no se observan en la emulación de escritorio:

  • El campo enfocado queda oculto.
  • El botón de envío desaparece detrás del teclado.
  • La página no permite desplazarse lo suficiente.
  • Una barra inferior fija cubre parte del formulario.
  • Los mensajes de error aparecen fuera del área visible.
  • El navegador aplica zoom automático.
  • Se muestra un tipo de teclado inadecuado.

Es importante comprobar que cada campo utiliza el tipo de entrada apropiado, como email, tel, number o date, y que los mensajes de validación no alteran de forma inesperada la composición.

Para profundizar en este punto, puede consultarse la guía sobre formularios accesibles, etiquetas, validaciones y feedback, donde se explican criterios que también deberían formar parte de las pruebas en móvil.

Probar el formulario con errores activos

No basta con completar el formulario correctamente. También debe enviarse vacío o con datos incorrectos.

La aparición de varios errores puede aumentar la altura de los campos, desplazar botones o modificar el scroll. La interfaz debe continuar siendo comprensible y permitir llegar fácilmente al primer dato que necesita corregirse.

Tablas y bloques de gran anchura

Las tablas son difíciles de adaptar porque pueden contener muchas columnas y datos que no deberían reducirse hasta resultar ilegibles.

Existen varias soluciones posibles:

  • Permitir desplazamiento horizontal dentro de un contenedor.
  • Mantener fija la primera columna.
  • Ocultar datos secundarios.
  • Mostrar un resumen y abrir el detalle bajo demanda.
  • Convertir cada fila en una tarjeta.
  • Crear una vista específica para móvil.

No hay una única estrategia válida. La elección depende de la importancia de los datos y de la tarea que deba realizar la persona usuaria.

Lo que sí debería evitarse es que la tabla provoque el desplazamiento horizontal de toda la página.

Modales, banners y elementos fijos

Los modales diseñados para escritorio pueden superar fácilmente la altura de una pantalla móvil. Si su contenido interno no permite desplazamiento, los botones de confirmación o cierre pueden quedar inaccesibles.

También deben probarse:

  • Paneles laterales.
  • Avisos de cookies.
  • Cabeceras fijas.
  • Navegaciones inferiores.
  • Botones flotantes.
  • Notificaciones.
  • Chats integrados.
  • Mensajes promocionales.
  • Barras de progreso.

El principal riesgo aparece cuando varios elementos ocupan simultáneamente la misma zona. Una barra de cookies, una navegación inferior y un botón flotante pueden funcionar correctamente por separado, pero superponerse cuando se muestran juntos.

Cómo utilizar las herramientas del navegador

Las herramientas de desarrollo permiten cambiar el tamaño del viewport, simular dispositivos, alternar la orientación y analizar las reglas CSS activas.

Son un buen punto de partida para cualquier proceso de testing responsive.

Comprobaciones básicas con DevTools

Desde el modo de dispositivo se pueden realizar acciones como:

  • Seleccionar resoluciones preconfiguradas.
  • Introducir una anchura y altura personalizadas.
  • Alternar entre orientación vertical y horizontal.
  • Simular eventos táctiles.
  • Modificar la densidad de píxeles.
  • Limitar la velocidad de red.
  • Inspeccionar media queries.
  • Revisar estilos calculados.
  • Capturar la página completa.

Los presets resultan útiles, pero no deberían sustituir las medidas personalizadas ni la revisión manual alrededor de los breakpoints.

Para localizar cajas problemáticas puede añadirse temporalmente una regla de diagnóstico:

* {
  outline: 1px solid rgb(255 0 0 / 15%);
}

Esta técnica permite identificar elementos con una anchura inesperada, márgenes negativos o contenedores que superan los límites de la página.

Revisar dispositivos reales

La emulación del navegador no reproduce todos los comportamientos de un teléfono físico.

Por esta razón, los flujos más importantes deberían comprobarse al menos en una selección reducida de dispositivos reales. Estas pruebas permiten evaluar:

  • Respuesta táctil.
  • Teclado virtual.
  • Gestos.
  • Barras dinámicas del navegador.
  • Áreas seguras.
  • Orientación.
  • Rendimiento percibido.
  • Legibilidad real.
  • Comportamiento durante el scroll.

No es necesario disponer de una colección completa de dispositivos. Puede combinarse una selección local con plataformas de prueba remota para ampliar la cobertura.

Cómo automatizar las pruebas responsive

Las pruebas manuales son esenciales, pero repetir todos los recorridos en cada tamaño consume tiempo. Una parte del proceso puede automatizarse mediante pruebas end-to-end y comparaciones visuales.

Ejecutar un flujo en varios viewports

Una prueba automatizada puede repetir una misma tarea en distintos tamaños de pantalla:

const viewports = [
  { width: 375, height: 667 },
  { width: 768, height: 1024 },
  { width: 1440, height: 900 }
];

for (const viewport of viewports) {
  test(`flujo de compra a ${viewport.width}px`, async ({ page }) => {
    await page.setViewportSize(viewport);
    await page.goto('/productos');

    await page
      .getByRole('button', { name: 'Añadir al carrito' })
      .click();

    await page
      .getByRole('link', { name: 'Ver carrito' })
      .click();

    await expect(
      page.getByRole('heading', { name: 'Tu carrito' })
    ).toBeVisible();
  });
}

Este tipo de prueba confirma que una acción importante continúa disponible en diferentes distribuciones.

Cuando la aplicación está desarrollada con React, puede combinarse el testing end-to-end con pruebas de componentes mediante React Testing Library. Las pruebas de componentes no sustituyen la validación visual, pero ayudan a asegurar que los controles mantienen su comportamiento cuando la interfaz cambia.

Qué recorridos conviene automatizar

Es recomendable empezar por los flujos con mayor impacto:

  • Inicio de sesión.
  • Registro.
  • Búsqueda.
  • Navegación principal.
  • Compra o reserva.
  • Envío de formularios.
  • Aplicación de filtros.
  • Apertura y cierre de menús.
  • Confirmaciones.
  • Gestión de errores.

La automatización aporta más valor cuando reproduce tareas reales que cuando se limita a verificar que determinados elementos existen en el DOM.

Detectar desbordamiento horizontal

También puede automatizarse una comprobación básica para detectar si el documento es más ancho que el viewport:

const hasHorizontalOverflow = await page.evaluate(() => {
  const documentElement = document.documentElement;

  return (
    documentElement.scrollWidth >
    documentElement.clientWidth
  );
});

expect(hasHorizontalOverflow).toBe(false);

Esta prueba permite detectar un problema general, aunque no identifica directamente qué elemento lo provoca. También debe interpretarse con cuidado si la interfaz incluye carruseles o tablas con desplazamiento horizontal intencionado.

Incorporar las pruebas a integración continua

Las pruebas responsive automatizadas pueden ejecutarse cada vez que se abre una solicitud de cambios o se actualiza una rama principal.

Una herramienta de integración continua puede:

  1. Instalar las dependencias.
  2. Construir la aplicación.
  3. Levantar un entorno de prueba.
  4. Ejecutar los tests en varios viewports.
  5. Guardar capturas o vídeos cuando se produce un error.
  6. Impedir el despliegue si falla un recorrido crítico.

En el artículo sobre automatización e integración continua con GitHub Actions se explica cómo incorporar tareas automáticas al ciclo de desarrollo.

Testing visual y pruebas exploratorias

La automatización no detecta todos los problemas. Una prueba puede confirmar que un botón existe y puede pulsarse, pero no advertir que está demasiado cerca de otro control o que un texto resulta difícil de leer.

Por ello, las pruebas responsive deben combinar varios enfoques.

Comparaciones visuales

Las pruebas visuales capturan la interfaz en diferentes viewports y comparan las imágenes con una versión de referencia.

Este método ayuda a detectar:

  • Cambios inesperados de espaciado.
  • Componentes desplazados.
  • Elementos desaparecidos.
  • Variaciones tipográficas.
  • Modificaciones en los breakpoints.
  • Regresiones provocadas por estilos globales.

No todas las diferencias representan un error. El contenido dinámico, las fechas o las imágenes variables pueden generar cambios legítimos. Las capturas deben revisarse antes de aceptar o rechazar el nuevo resultado.

Testing exploratorio

El testing exploratorio permite utilizar la aplicación con mayor libertad, cambiar el tamaño de la ventana en mitad de una tarea y probar combinaciones que no estaban contempladas en un guion.

Por ejemplo, se puede:

  • Abrir un modal y cambiar la orientación.
  • Aplicar filtros antes de reducir la ventana.
  • Ampliar el zoom con un menú desplegado.
  • Enviar un formulario mientras aparece una notificación.
  • Cambiar de escritorio a móvil con una sesión iniciada.
  • Probar contenido especialmente largo.

Esta forma de trabajar complementa las comprobaciones automatizadas y ayuda a localizar errores difíciles de anticipar. La guía de testing exploratorio desarrolla con más detalle este enfoque.

Errores frecuentes en el diseño responsive

Utilizar demasiadas anchuras fijas

Las anchuras rígidas pueden provocar desbordamientos cuando el espacio disponible es menor de lo previsto.

En muchos componentes resulta preferible combinar:

  • width: 100%.
  • max-width.
  • Unidades relativas.
  • Flexbox.
  • CSS Grid.
  • Funciones como min(), max() y clamp().

Esto no significa que deban eliminarse todos los valores en píxeles. Siguen siendo útiles para determinados límites, iconos o separaciones. El problema aparece cuando toda la estructura depende de dimensiones invariables.

Ocultar contenido sin valorar su importancia

Adaptar una interfaz a móvil no debería consistir en eliminar todo lo que no cabe.

Antes de ocultar un elemento conviene preguntarse:

  • ¿La información continúa disponible en otro lugar?
  • ¿La acción sigue pudiendo realizarse?
  • ¿El contenido es realmente secundario?
  • ¿Puede mostrarse bajo demanda?
  • ¿Su ausencia cambia el significado de la pantalla?

En muchos casos, reorganizar o resumir es mejor que ocultar.

Probar únicamente la página de inicio

La portada suele recibir más atención, pero muchos errores aparecen en páginas interiores:

  • Resultados de búsqueda.
  • Fichas de producto.
  • Formularios.
  • Áreas privadas.
  • Tablas.
  • Historiales.
  • Ajustes.
  • Páginas de error.
  • Estados vacíos.

La estrategia de testing debe cubrir las principales plantillas y no limitarse a la pantalla más visible del proyecto.

Ignorar el zoom y el tamaño del texto

Una persona puede ampliar el navegador o configurar un tamaño de fuente mayor. La interfaz debería soportar estos cambios sin perder información ni funcionalidad.

Conviene probar el zoom al 200 %, prestar atención a los contenedores con alturas fijas y comprobar que el texto no queda cortado.

Proceso recomendado para revisar una app

Un proceso organizado facilita repetir las pruebas y reduce la posibilidad de olvidar escenarios importantes.

1. Identificar los recorridos críticos

Seleccione las tareas cuyo fallo tendría mayor impacto, como iniciar sesión, buscar, comprar, reservar o enviar un formulario.

2. Preparar una matriz de tamaños

Incluya móviles, tablets, escritorio y medidas cercanas a cada breakpoint.

3. Utilizar contenido realista

Pruebe textos largos, mensajes de error, estados vacíos, imágenes con distintas proporciones y datos cargados dinámicamente.

4. Revisar los estados interactivos

Abra menús, modales, desplegables, tooltips y campos. Modifique la orientación o el tamaño con los componentes abiertos.

5. Probar dispositivos reales

Repita los recorridos esenciales en una selección de teléfonos y tablets físicos.

6. Automatizar las regresiones importantes

Añada pruebas funcionales y visuales para los componentes y flujos con mayor riesgo.

7. Documentar cada incidencia

Un error responsive debería incluir:

  • Página afectada.
  • Anchura y altura del viewport.
  • Dispositivo o emulación utilizada.
  • Navegador y sistema operativo.
  • Pasos para reproducirlo.
  • Resultado esperado.
  • Resultado obtenido.
  • Captura o vídeo.
  • Prioridad.

Una descripción como “se ve mal en móvil” no contiene información suficiente para reproducir ni resolver el problema.

Checklist de testing responsive antes de publicar

Antes de lanzar una versión, conviene comprobar lo siguiente:

  • No existe desplazamiento horizontal accidental.
  • Los títulos largos mantienen la estructura.
  • Los textos no quedan cortados.
  • Las imágenes respetan sus contenedores.
  • Los menús pueden abrirse y cerrarse.
  • Los botones son cómodos de utilizar en pantallas táctiles.
  • Los formularios funcionan con el teclado virtual.
  • Los errores son visibles y comprensibles.
  • Los modales permiten acceder a todo su contenido.
  • Las tablas tienen una estrategia específica para móvil.
  • Los elementos fijos no se superponen.
  • El zoom no impide utilizar la interfaz.
  • Los cambios de orientación no rompen la distribución.
  • Los breakpoints funcionan antes, durante y después de su valor.
  • Los recorridos principales pueden completarse en móvil, tablet y escritorio.
  • Las pruebas automatizadas no muestran nuevas regresiones.

Preguntas frecuentes sobre testing responsive

¿Cuántos tamaños de pantalla es necesario probar?

No existe una cantidad universal. Como punto de partida, conviene probar un móvil estrecho, un móvil estándar, una tablet, un portátil y un escritorio.

También es importante revisar los píxeles inmediatamente anteriores y posteriores a cada breakpoint. Más que acumular dispositivos preconfigurados, interesa comprobar cómo se comporta el contenido durante todo el cambio de anchura.

¿Es suficiente utilizar las herramientas de desarrollo del navegador?

No. Las herramientas del navegador son muy útiles para detectar problemas de distribución y probar numerosos viewports, pero no reproducen completamente el teclado virtual, los gestos, las barras dinámicas o determinadas diferencias entre navegadores móviles.

Los recorridos más importantes deberían probarse también en algunos dispositivos reales.

¿Se puede automatizar por completo el testing responsive?

No completamente. Es posible automatizar flujos, capturas de pantalla, comprobaciones de desbordamiento y comparaciones visuales.

Sin embargo, todavía se necesita una revisión humana para valorar la legibilidad, la jerarquía, la comodidad táctil y la calidad general de la experiencia. La estrategia más eficaz combina automatización, pruebas manuales y testing exploratorio.

Una interfaz flexible necesita pruebas flexibles

El testing responsive no es una tarea decorativa ni una comprobación que deba dejarse para los últimos minutos antes de publicar. Es una forma de garantizar que la aplicación continúa siendo útil cuando cambian las condiciones de acceso.

Una interfaz no debería depender de que la persona utilice el mismo dispositivo, orientación o tamaño de ventana imaginado durante el diseño. Debe responder con flexibilidad, conservar sus funciones principales y mantener el contenido comprensible.

Las mejores pruebas responsive no se limitan a preguntar si la página cabe en una pantalla. También comprueban si puede utilizarse con comodidad, si los controles permanecen disponibles, si los mensajes se entienden y si las transiciones entre tamaños se producen sin errores.

Integrar estas comprobaciones durante el desarrollo permite detectar los problemas cuando todavía son fáciles de corregir. También reduce regresiones, mejora la mantenibilidad del CSS y evita que sean las personas usuarias quienes descubran los fallos después del lanzamiento.

En definitiva, un buen diseño responsive no se demuestra con una única captura perfecta. Se demuestra mediante una experiencia sólida en diferentes tamaños, contenidos y formas de interacción.