Cómo crear loaders animados solo con CSS

Cuando una página necesita unos segundos para cargar datos, procesar una acción o completar una petición, el usuario debe recibir alguna señal que confirme que el sistema continúa funcionando. De lo contrario, puede pensar que la interfaz se ha bloqueado, pulsar varias veces el mismo botón o abandonar el sitio antes de que termine el proceso.

Los loaders cumplen precisamente esa función. Son pequeños indicadores visuales que comunican que existe una tarea en curso y que es necesario esperar. Aunque pueden desarrollarse mediante imágenes, SVG o JavaScript, en muchos casos es posible crear un loader CSS animado utilizando únicamente HTML y CSS.

Un círculo giratorio, una secuencia de puntos, unas barras que cambian de tamaño o una línea de progreso indeterminada pueden construirse con unas pocas propiedades, pseudoelementos y una regla @keyframes.

En esta guía veremos cómo crear diferentes loaders animados solo con CSS, cómo personalizarlos y qué medidas debemos aplicar para que sean accesibles, eficientes y coherentes con el diseño de la interfaz.

Si todavía no tienes claros los fundamentos de las transiciones y las animaciones, puedes consultar primero esta guía básica de animaciones CSS, donde se explican conceptos como transition, animation y @keyframes desde cero.

Qué es un loader CSS y para qué sirve

Un loader es un componente visual que informa de que una aplicación está realizando una operación cuyo resultado todavía no está disponible.

Puede aparecer mientras:

  • se recuperan datos de una API;
  • se envía un formulario;
  • se procesa un pago;
  • se sube un archivo;
  • se carga una nueva página;
  • se generan resultados;
  • se prepara una imagen;
  • se actualiza una sección dinámica.

El loader no acelera técnicamente la operación. Su función consiste en hacer comprensible el tiempo de espera.

Cuando el usuario recibe una respuesta visual inmediata, entiende que su acción ha sido registrada. Aunque el proceso tarde exactamente lo mismo, la experiencia resulta más clara y controlada.

Diferencias entre loader, spinner y barra de progreso

Los términos loader y spinner se utilizan a menudo como si fueran sinónimos, pero no significan exactamente lo mismo.

Un loader es cualquier indicador de carga. Puede tener forma circular, lineal, geométrica o incluso representar una pequeña ilustración.

Un spinner CSS es un tipo específico de loader basado en un movimiento giratorio. El ejemplo más habitual es un círculo con una parte del borde destacada que gira de manera continua.

Por otra parte, una barra de progreso puede ser determinada o indeterminada:

  • Una barra determinada muestra cuánto falta para completar el proceso, normalmente mediante un porcentaje.
  • Una barra indeterminada comunica que existe una operación en curso, pero no representa su duración ni su progreso exacto.

Cuando no conocemos el avance real de una tarea, no debemos mostrar un porcentaje inventado. En esos casos, resulta más apropiado utilizar un spinner, una animación de puntos o una barra indeterminada.

Qué significa crear un loader solo con CSS

Crear un loader solo con CSS significa que su representación visual y su movimiento no necesitan imágenes externas, GIF animados ni lógica JavaScript.

La animación puede construirse mediante:

  • bordes;
  • fondos;
  • transformaciones;
  • cambios de opacidad;
  • pseudoelementos;
  • retrasos de animación;
  • reglas @keyframes.

No obstante, conviene aclarar una cuestión importante: CSS puede animar el loader, pero normalmente no decide cuándo debe aparecer o desaparecer.

En una aplicación real, JavaScript, React, Vue, Angular o la tecnología utilizada en el proyecto controlará el estado de carga. CSS se encargará de la apariencia, mientras que la lógica de la aplicación determinará cuándo mostrar u ocultar el componente.

Fundamentos de una animación de carga CSS

Antes de construir los ejemplos, resulta útil conocer las piezas básicas que intervienen en cualquier animación de carga.

La estructura HTML del loader

Un loader sencillo puede partir de un único elemento:

<div class="spinner"></div>

Esta estructura puede ser suficiente para representar la parte visual, pero no comunica qué está ocurriendo a los usuarios de tecnologías de asistencia.

Una versión más completa sería la siguiente:

<div class="loader" role="status">
  <span class="loader__spinner" aria-hidden="true"></span>
  <span class="loader__text">Cargando contenido…</span>
</div>

En este ejemplo:

  • role="status" identifica el contenido como una actualización de estado;
  • aria-hidden="true" oculta el elemento decorativo a los lectores de pantalla;
  • el texto explica qué operación se está realizando.

Esta separación entre contenido y decoración permite cambiar el aspecto del loader sin perder información esencial.

Cómo funciona @keyframes

La regla @keyframes define los distintos estados que atravesará un elemento durante una animación.

Un giro completo puede declararse así:

@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}

El valor 1turn equivale a una vuelta completa. También podríamos utilizar 360deg, aunque 1turn expresa de forma muy clara la intención del movimiento.

Después debemos asociar la animación al elemento:

.loader__spinner {
  animation: spin 0.8s linear infinite;
}

La propiedad abreviada animation indica:

  • spin: nombre de la animación;
  • 0.8s: duración de cada ciclo;
  • linear: velocidad constante;
  • infinite: repetición indefinida.

Propiedades principales de animation

Aunque la sintaxis abreviada es cómoda, una animación está formada por diferentes propiedades:

.elemento {
  animation-name: spin;
  animation-duration: 0.8s;
  animation-timing-function: linear;
  animation-delay: 0s;
  animation-iteration-count: infinite;
  animation-direction: normal;
  animation-fill-mode: none;
}

Para un loading CSS no siempre necesitaremos configurarlas todas. Las más habituales serán el nombre, la duración, la curva de velocidad, el retraso y el número de repeticiones.

Cómo crear un spinner CSS circular

El círculo giratorio es probablemente el tipo de loader más reconocible. Puede construirse aplicando un borde uniforme a un elemento circular y cambiando el color de uno de sus lados.

HTML del spinner

<div class="loader" role="status">
  <span class="spinner" aria-hidden="true"></span>
  <span>Cargando contenido…</span>
</div>

CSS del spinner

.loader {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-family: sans-serif;
  color: #2d2433;
}

.spinner {
  width: 2.5rem;
  height: 2.5rem;
  border: 0.3rem solid #eadde7;
  border-top-color: #cc2b5e;
  border-radius: 50%;
  animation: spinner-rotation 0.8s linear infinite;
}

@keyframes spinner-rotation {
  to {
    transform: rotate(1turn);
  }
}

La clave está en border-top-color. Como uno de los lados tiene un color diferente, el giro produce la sensación de que una sección del círculo está avanzando continuamente.

La propiedad border-radius: 50% transforma el elemento cuadrado en un círculo, mientras que transform: rotate() genera el movimiento.

Este ejemplo demuestra que no necesitamos una imagen para crear una forma reconocible. Si te interesa profundizar en este enfoque, en el artículo sobre cómo dibujar formas básicas con CSS encontrarás más ejemplos de círculos, triángulos, óvalos y otras figuras construidas únicamente con estilos.

Personalizar el spinner mediante variables CSS

Cuando un loader se utiliza en diferentes secciones, es recomendable definir sus principales características mediante propiedades personalizadas.

.spinner {
  --loader-size: 2.5rem;
  --loader-width: 0.3rem;
  --loader-color: #cc2b5e;
  --loader-track: #eadde7;
  --loader-speed: 0.8s;

  width: var(--loader-size);
  height: var(--loader-size);
  border: var(--loader-width) solid var(--loader-track);
  border-top-color: var(--loader-color);
  border-radius: 50%;
  animation: spinner-rotation var(--loader-speed) linear infinite;
}

Ahora podemos crear variantes modificando únicamente las variables:

.spinner--small {
  --loader-size: 1.25rem;
  --loader-width: 0.2rem;
}

.spinner--large {
  --loader-size: 4rem;
  --loader-width: 0.45rem;
  --loader-speed: 1.1s;
}

.spinner--secondary {
  --loader-color: #753a88;
}

Este sistema facilita la reutilización del componente y reduce la duplicación de estilos.

También permite adaptar el spinner a distintos contextos, como un botón pequeño, una pantalla completa o una tarjeta que actualiza su contenido.

Cómo crear un loader de puntos animados

Los puntos secuenciales funcionan especialmente bien en botones, mensajes, chats o espacios reducidos donde un spinner circular podría ocupar demasiado espacio.

Estructura HTML

<div class="loader-dots" role="status">
  <span class="loader-dots__animation" aria-hidden="true">
    <span></span>
    <span></span>
    <span></span>
  </span>

  <span class="visually-hidden">Cargando resultados…</span>
</div>

La animación se marca como decorativa y el texto permanece disponible para los lectores de pantalla.

Estilos de los puntos

.loader-dots__animation {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.loader-dots__animation span {
  width: 0.65rem;
  height: 0.65rem;
  background-color: #cc2b5e;
  border-radius: 50%;
  animation: dot-pulse 1.2s ease-in-out infinite;
}

.loader-dots__animation span:nth-child(2) {
  animation-delay: 0.15s;
}

.loader-dots__animation span:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes dot-pulse {
  0%,
  80%,
  100% {
    transform: scale(0.65);
    opacity: 0.4;
  }

  40% {
    transform: scale(1);
    opacity: 1;
  }
}

Cada punto utiliza la misma animación, pero comienza en un momento diferente gracias a animation-delay.

Este pequeño desfase crea una secuencia continua sin necesidad de definir tres animaciones distintas.

Ocultar el texto solo visualmente

Cuando no queremos mostrar el mensaje junto al loader, podemos ocultarlo visualmente sin eliminarlo del árbol de accesibilidad:

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  white-space: nowrap;
  border: 0;
  clip-path: inset(50%);
}

No deberíamos utilizar display: none para este texto, ya que dejaría de estar disponible para las tecnologías de asistencia.

Cómo crear un loader de barras con CSS

Otra alternativa consiste en animar varias barras verticales. Este patrón puede recordar a un ecualizador y resulta adecuado para interfaces relacionadas con audio, procesamiento o análisis de datos.

HTML del loader

<div class="loader-bars" role="status">
  <span class="loader-bars__animation" aria-hidden="true">
    <span></span>
    <span></span>
    <span></span>
    <span></span>
  </span>

  <span class="visually-hidden">Procesando información…</span>
</div>

CSS de las barras animadas

.loader-bars__animation {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  height: 2rem;
}

.loader-bars__animation span {
  width: 0.35rem;
  height: 100%;
  background-color: #753a88;
  border-radius: 999px;
  transform-origin: center;
  animation: bar-scale 1s ease-in-out infinite;
}

.loader-bars__animation span:nth-child(2) {
  animation-delay: 0.1s;
}

.loader-bars__animation span:nth-child(3) {
  animation-delay: 0.2s;
}

.loader-bars__animation span:nth-child(4) {
  animation-delay: 0.3s;
}

@keyframes bar-scale {
  0%,
  100% {
    transform: scaleY(0.35);
    opacity: 0.5;
  }

  50% {
    transform: scaleY(1);
    opacity: 1;
  }
}

En lugar de modificar directamente la altura de las barras, utilizamos transform: scaleY().

Visualmente, el resultado es similar a cambiar height, pero mantenemos intacto el espacio que ocupa cada elemento dentro del documento.

Cómo crear una barra de carga indeterminada

Cuando el loader debe ocupar un espacio horizontal, podemos crear una línea que se desplace dentro de un contenedor.

Este patrón es apropiado cuando queremos comunicar actividad sin representar un porcentaje exacto.

Marcado HTML

<div class="loading-section" role="status">
  <div class="loading-bar" aria-hidden="true"></div>
  <p>Cargando los datos solicitados…</p>
</div>

Estilos CSS

.loading-section {
  width: min(100%, 24rem);
  font-family: sans-serif;
  color: #2d2433;
}

.loading-bar {
  position: relative;
  width: 100%;
  height: 0.5rem;
  overflow: hidden;
  background-color: #eadde7;
  border-radius: 999px;
}

.loading-bar::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 40%;
  background-color: #cc2b5e;
  border-radius: inherit;
  transform: translateX(-125%);
  animation: loading-progress 1.4s ease-in-out infinite;
}

@keyframes loading-progress {
  to {
    transform: translateX(350%);
  }
}

El contenedor oculta todo lo que sobresale mediante overflow: hidden. El pseudoelemento comienza fuera del extremo izquierdo y atraviesa la barra utilizando translateX().

Este loader comunica que una tarea está activa, pero no muestra un avance real. Por ese motivo, no debemos presentarlo como si fuera una barra de progreso determinada.

Crear un loader con pseudoelementos

Los pseudoelementos ::before y ::after permiten generar formas adicionales sin incorporar más etiquetas al HTML.

Podemos utilizarlos para crear un loader con dos anillos que giren en direcciones distintas:

<div class="double-spinner" role="status">
  <span class="visually-hidden">Cargando aplicación…</span>
</div>
.double-spinner {
  position: relative;
  width: 3.5rem;
  height: 3.5rem;
}

.double-spinner::before,
.double-spinner::after {
  content: "";
  position: absolute;
  border-radius: 50%;
}

.double-spinner::before {
  inset: 0;
  border: 0.3rem solid transparent;
  border-top-color: #cc2b5e;
  border-right-color: #cc2b5e;
  animation: outer-rotation 1s linear infinite;
}

.double-spinner::after {
  inset: 0.65rem;
  border: 0.25rem solid transparent;
  border-bottom-color: #753a88;
  animation: inner-rotation 0.75s linear infinite reverse;
}

@keyframes outer-rotation {
  to {
    transform: rotate(1turn);
  }
}

@keyframes inner-rotation {
  to {
    transform: rotate(1turn);
  }
}

Aunque el diseño utiliza dos formas, el HTML solo necesita un contenedor. Los pseudoelementos se reservan para la parte puramente decorativa.

Este mismo recurso puede emplearse para construir otros elementos de interfaz. En la guía sobre cómo dibujar iconos sencillos con CSS sin utilizar SVG ni imágenes puedes ver cómo combinar bordes, transformaciones y pseudoelementos para crear gráficos ligeros.

Accesibilidad en los loaders animados

Un loader no debería ser únicamente un elemento que se mueve. Si el estado de carga solo se comunica visualmente, algunas personas no sabrán que la interfaz está procesando información.

Añadir un mensaje de estado

La solución más clara consiste en acompañar el loader con un texto:

<div class="loader" role="status">
  <span class="spinner" aria-hidden="true"></span>
  <span>Cargando productos…</span>
</div>

El mensaje debería describir la acción cuando resulte útil:

  • “Cargando productos…”
  • “Enviando formulario…”
  • “Procesando el pago…”
  • “Preparando el archivo…”
  • “Actualizando resultados…”

Un mensaje específico suele resultar más informativo que un “Cargando…” genérico.

Utilizar role="status"

El rol status identifica una actualización que puede anunciarse sin desplazar el foco del usuario.

El elemento visual puede llevar aria-hidden="true" para evitar información innecesaria:

<span class="spinner" aria-hidden="true"></span>

No es necesario describir que existe un círculo de color girando. Lo importante es comunicar que la operación continúa.

Indicar que una región está ocupada

Cuando una sección concreta se está actualizando, puede utilizarse aria-busy="true" en el contenedor correspondiente:

<section class="results" aria-busy="true">
  <div class="loader" role="status">
    <span class="spinner" aria-hidden="true"></span>
    <span>Cargando resultados…</span>
  </div>
</section>

Cuando termina la operación, la aplicación debe actualizar el atributo:

<section class="results" aria-busy="false">
  <!-- Contenido cargado -->
</section>

Este cambio normalmente requerirá JavaScript o la lógica del framework utilizado.

La accesibilidad no debe añadirse al final como una corrección aislada. Conviene plantearla desde el momento en el que diseñamos el componente. En el artículo sobre componentes UI accesibles encontrarás más recomendaciones para crear interfaces comprensibles mediante HTML semántico, estados visibles y compatibilidad con distintas formas de interacción.

Respetar prefers-reduced-motion

Algunas personas configuran su dispositivo para reducir las animaciones. Por ese motivo, un loader CSS animado debería tener en cuenta la media query prefers-reduced-motion.

Podemos detener las animaciones y mantener el mensaje visible:

@media (prefers-reduced-motion: reduce) {
  .spinner,
  .loader-dots__animation span,
  .loader-bars__animation span,
  .loading-bar::before,
  .double-spinner::before,
  .double-spinner::after {
    animation: none;
  }
}

En este contexto, el indicador permanece estático mientras el texto continúa informando del proceso.

También podríamos sustituir el movimiento continuo por un cambio visual más discreto. Sin embargo, para un componente tan funcional como un loader, detener la animación suele ser una solución sencilla y comprensible.

Lo importante es que la información no dependa exclusivamente del movimiento. Aunque la animación se detenga, el usuario debe poder saber que existe una operación en curso.

Rendimiento de una animación de carga CSS

Los loaders suelen repetirse indefinidamente mientras dura una operación. Por tanto, una animación mal planteada puede consumir recursos durante todo el periodo de espera.

Siempre que sea posible, conviene priorizar:

  • transform;
  • opacity.

Estas propiedades permiten crear giros, desplazamientos, escalados y pulsaciones sin modificar directamente la geometría del documento.

Propiedades que conviene evitar

Animar propiedades como width, height, top, left, margin o padding puede obligar al navegador a recalcular posiciones y dimensiones durante la animación.

Por ejemplo, podríamos cambiar la altura de una barra de esta forma:

@keyframes inefficient-bar {
  from {
    height: 0.5rem;
  }

  to {
    height: 2rem;
  }
}

Sin embargo, podemos obtener un resultado visual parecido mediante una transformación:

@keyframes efficient-bar {
  from {
    transform: scaleY(0.25);
  }

  to {
    transform: scaleY(1);
  }
}

El segundo planteamiento mantiene intacto el espacio reservado por el elemento y evita que el resto del contenido tenga que adaptarse a una altura cambiante.

No abusar de will-change

La propiedad will-change puede avisar al navegador de que una característica va a modificarse:

.spinner {
  will-change: transform;
}

Sin embargo, no debería aplicarse automáticamente a todos los elementos animados.

Mantener demasiadas optimizaciones activas puede aumentar el consumo de recursos. En un spinner pequeño, el navegador suele gestionar correctamente una transformación sencilla sin necesidad de añadir will-change.

Retirar el loader cuando deja de ser necesario

Ocultar visualmente un loader no siempre significa que su animación se haya detenido. Si el elemento permanece activo, podría continuar ejecutándose aunque ya no resulte visible.

Lo recomendable es:

  • retirar el componente cuando termina la operación;
  • detener la animación si el loader queda fuera de uso;
  • evitar mantener indicadores de carga permanentes;
  • comprobar que no existen varios loaders ejecutándose sin necesidad.

Cómo elegir la duración y el movimiento adecuados

La velocidad influye directamente en la percepción del componente.

Un spinner excesivamente rápido puede parecer nervioso, mientras que uno demasiado lento puede transmitir que la aplicación está bloqueada.

Como punto de partida, podemos utilizar estos intervalos:

  • entre 0.7s y 1.2s para un spinner circular;
  • entre 1s y 1.5s para una secuencia de puntos;
  • entre 1.2s y 2s para una barra indeterminada.

No se trata de reglas absolutas. La duración debe ajustarse al tamaño del componente, al tipo de movimiento y al lenguaje visual de la interfaz.

Utilizar una curva de velocidad coherente

Para una rotación continua, linear suele ser una buena elección:

animation: spinner-rotation 0.8s linear infinite;

Como no existe un principio ni un final perceptible entre las vueltas, mantener una velocidad constante evita cambios bruscos.

Para puntos, escalados o barras que suben y bajan, una curva como ease-in-out crea una aceleración más gradual:

animation: dot-pulse 1.2s ease-in-out infinite;

No existe una curva universal para todos los loaders. Debemos elegirla en función del tipo de movimiento que queremos representar.

Cuándo mostrar un loader y cuándo evitarlo

No todas las acciones necesitan una animación de carga.

Si una respuesta es prácticamente inmediata, mostrar un loader durante una fracción de segundo puede provocar un destello innecesario. En estos casos, suele ser preferible esperar un pequeño intervalo antes de mostrarlo.

Este retraso no se controla necesariamente desde CSS, ya que depende de la lógica de la aplicación. La idea consiste en mostrar el indicador únicamente cuando la espera empieza a ser perceptible.

También debemos evitar loaders que permanezcan indefinidamente sin ofrecer ninguna salida. Cuando una operación puede fallar, la interfaz debería contemplar:

  • un límite de espera;
  • un mensaje de error;
  • una opción para volver a intentarlo;
  • una forma de cancelar el proceso, cuando sea posible.

El loader comunica actividad, pero no reemplaza la gestión de errores.

Cuándo CSS puede quedarse corto

Para un spinner, unos puntos o una barra indeterminada, CSS suele ser suficiente. Sin embargo, las animaciones más complejas pueden requerir control sobre secuencias, pausas, eventos o líneas temporales.

En esos casos, una biblioteca de animación puede ofrecer un control más preciso. Puedes consultar esta introducción a las animaciones web con GSAP para conocer una alternativa basada en JavaScript.

Esto no significa que debamos utilizar una biblioteca para cualquier loader. Para un indicador de carga sencillo, CSS suele ser la opción más directa y fácil de mantener.

Errores frecuentes al crear loaders con CSS

Utilizar demasiados elementos HTML

Algunos loaders incluyen numerosas etiquetas para dibujar formas que podrían resolverse mediante bordes o pseudoelementos.

No es obligatorio reducir siempre el HTML al mínimo, pero conviene diferenciar entre los elementos que aportan significado y los que son puramente decorativos.

Omitir el texto de carga

Una animación sin mensaje puede ser suficiente para algunos usuarios, pero no comunica el estado de forma universal.

Siempre que el contexto no sea completamente evidente, debemos proporcionar una etiqueta visible o accesible.

Mostrar un porcentaje falso

Una animación que avanza de cero a cien y vuelve a empezar no representa un progreso real.

Si desconocemos el porcentaje completado, debemos utilizar un indicador indeterminado y evitar transmitir una precisión que la aplicación no posee.

Crear una animación demasiado llamativa

El loader debe informar, no convertirse en el protagonista de la pantalla.

Los cambios bruscos de escala, los destellos rápidos o los giros excesivos pueden distraer y empeorar la experiencia.

Bloquear toda la interfaz sin necesidad

No todas las operaciones requieren una capa superpuesta que impida interactuar con la página.

Si solo se está actualizando una sección, podemos colocar el loader dentro de esa zona y mantener disponible el resto del contenido.

Olvidar los estados de error y contenido vacío

Una interfaz no debería quedar atrapada en un estado de carga eterno.

Además del loader, debemos diseñar qué ocurrirá cuando:

  • la petición falle;
  • no existan resultados;
  • se pierda la conexión;
  • la operación tarde más de lo esperado;
  • el usuario cancele el proceso.

Preguntas frecuentes sobre loaders animados con CSS

¿Se puede crear un loader completamente sin JavaScript?

Sí. La apariencia y el movimiento pueden crearse únicamente con HTML y CSS. Un spinner, unos puntos animados o una barra indeterminada no necesitan JavaScript para funcionar visualmente.

Sin embargo, en una aplicación dinámica suele ser necesario utilizar JavaScript o la lógica de un framework para mostrar el loader al comenzar una operación y retirarlo cuando termina.

¿Qué propiedades CSS son mejores para animar un loader?

Siempre que el diseño lo permita, conviene utilizar transform y opacity.

Con ellas podemos crear rotaciones, escalados, desplazamientos y pulsaciones sin modificar directamente el tamaño o la posición calculada de los elementos.

Propiedades como width, height, top o left deberían reservarse para casos en los que no exista una alternativa razonable.

¿Cómo puedo hacer que un spinner CSS sea accesible?

El spinner visual debería marcarse como decorativo mediante aria-hidden="true" y acompañarse de un mensaje que describa el estado, como “Cargando resultados…”.

También puede utilizarse role="status" para identificar la actualización y aria-busy para indicar que una región está siendo procesada. Además, conviene respetar prefers-reduced-motion para reducir o detener la animación cuando el usuario lo haya solicitado.

Una espera bien diseñada también comunica

Crear una animación de carga CSS puede parecer un detalle menor dentro de una interfaz, pero su impacto va más allá del movimiento. Un buen loader confirma que la aplicación ha recibido una acción, reduce la incertidumbre y ayuda al usuario a interpretar lo que está ocurriendo.

La parte visual puede resolverse con muy poco código. Un borde circular, una transformación y una regla @keyframes son suficientes para construir un spinner CSS funcional.

Sin embargo, el verdadero trabajo consiste en decidir cuándo mostrarlo, cómo comunicar el estado, qué ocurrirá si la operación falla y de qué manera se adaptará a las preferencias de movimiento.

Por eso, el mejor loader no es necesariamente el más original ni el más complejo. Es aquel que aparece cuando hace falta, mantiene una animación discreta, no perjudica el rendimiento y desaparece en cuanto el contenido está preparado.

En diseño de interfaces, esperar no siempre puede evitarse. Lo que sí podemos hacer es conseguir que esa espera resulte clara, accesible y coherente.

Pseudo-elementos en CSS: la clave para crear ilustraciones más complejas

Cuando empiezas a dibujar con CSS, lo habitual es construir formas simples: círculos, triángulos o pequeños iconos. Pero en cuanto quieres añadir más detalle o crear composiciones más ricas, aparece una limitación clara: el HTML empieza a crecer innecesariamente.

Si ya has trabajado cómo dibujar formas básicas con CSS, este es el siguiente paso natural.

Aquí es donde entran en juego los pseudo-elementos en CSS.

Gracias a ::before y ::after, puedes añadir capas visuales, detalles decorativos e incluso partes completas de una ilustración sin ensuciar el marcado. Esto no solo mejora la estética, sino también la mantenibilidad y la claridad del código.

Además, su uso tiene impacto directo en algo clave en UX: la relación entre tiempo de decisión y carga cognitiva. Porque no se trata solo de dibujar más, sino de dibujar mejor.


Qué son los pseudo-elementos en CSS y por qué importan tanto

Los pseudo-elementos permiten crear contenido visual adicional dentro de un elemento, sin añadir nodos al HTML.

Los más utilizados son:

  • ::before
  • ::after

Ambos funcionan como capas extra que puedes posicionar, estilizar y animar.

La gran ventaja: más complejidad visual, menos HTML

Uno de los errores más comunes al dibujar con CSS es abusar del HTML.

Si vienes de crear estructuras más básicas como en formas con CSS, aquí notarás la diferencia enseguida.

Resultado:

  • HTML más limpio
  • CSS más potente
  • Componentes más reutilizables

Cómo funcionan ::before y ::after en la práctica

content no es opcional

Sin content, el pseudo-elemento no existe:

.elemento::before {
content: '';
}

Aunque no muestres texto, necesitas declararlo.


Se comportan como hijos del elemento

Se renderizan dentro del elemento, como si fueran hijos:

  • ::before → antes del contenido
  • ::after → después del contenido

Necesitan contexto de posicionamiento

Para trabajar bien con ellos:

.elemento {
position: relative;
}.elemento::before {
content: '';
position: absolute;
}

Esto te permite controlar su posición con precisión.


Por qué son clave para dibujar con CSS

Una sola etiqueta puede convertirse en una mini ilustración

Con pseudo-elementos puedes construir composiciones completas con una sola etiqueta.

Si ya has probado a dibujar iconos con CSS sin usar imágenes, aquí es donde empiezas a escalar el nivel.

Tres capas sin añadir HTML:

  • base
  • ::before
  • ::after

Te obligan a pensar por capas

Este cambio es clave.

Empiezas a diseñar como en ilustración:

  • base
  • volumen
  • sombras
  • detalles
  • interacción

Esto mejora mucho tu forma de construir interfaces.


Tiempo de decisión vs. carga cognitiva: qué tiene que ver esto con los pseudo-elementos

Este punto conecta directamente con diseño UX.

Si te interesa profundizar más en esto, puedes leer también por qué diseñar primero lo esencial mejora la experiencia de usuario.


Cuando reducen el tiempo de decisión

Un pseudo-elemento bien usado puede:

  • indicar interacción
  • reforzar jerarquía
  • guiar la mirada

Ejemplo:

.link::after {
content: '→';
margin-left: 4px;
}

👉 El usuario entiende más rápido qué hacer.


Cuando aumentan la carga cognitiva

Si abusas de ellos:

  • demasiadas capas
  • demasiados efectos
  • demasiada decoración

El resultado es ruido visual.

💡 Clave: no todo lo que se puede hacer, se debe hacer.


Casos de uso reales en ilustración y diseño de interfaz

Decoración estructural sin ensuciar el HTML

Ejemplo: subrayado decorativo

.title::after {
content: '';
display: block;
height: 6px;
background: #f8e0ea;
}

Crear formas compuestas

Ejemplo: bocadillo de diálogo

.bubble::after {
content: '';
position: absolute;
bottom: -10px;
width: 20px;
height: 20px;
background: white;
transform: rotate(45deg);
}

Añadir capas interactivas

Ejemplo: hover animado

.button::before {
content: '';
position: absolute;
inset: 0;
transform: translateX(-100%);
transition: transform .3s;
}.button:hover::before {
transform: translateX(0);
}

Técnicas avanzadas para ilustraciones más complejas

Combinar pseudo-elementos con sombras múltiples

.dot {
box-shadow: 20px 0 0 black, 40px 0 0 black;
}

Permite duplicar formas sin más elementos.


Usar transformaciones para reutilizar piezas

.elemento::before {
transform: rotate(45deg);
}

Reutilizar una misma forma ahorra trabajo y mejora consistencia

Esto mejora:

  • coherencia visual
  • mantenimiento
  • escalabilidad

Mezclar pseudo-elementos con gradientes

.shape {
background: radial-gradient(circle, white, blue);
}

Usar clip-path y pseudo-elementos juntos

Si quieres profundizar en este tipo de técnicas, un buen siguiente paso sería explorar clip-path en CSS (ideal para formas más avanzadas).

.elemento {
clip-path: polygon(...);
}

Ejemplo práctico: construir una ilustración sencilla con una sola etiqueta

<div class="flower"></div>
.flower {
position: relative;
width: 80px;
height: 80px;
background: pink;
border-radius: 50%;
}.flower::before {
content: '';
position: absolute;
box-shadow: 40px 0 pink, -40px 0 pink;
}.flower::after {
content: '';
position: absolute;
width: 30px;
height: 30px;
background: yellow;
border-radius: 50%;
}

Una sola etiqueta → múltiples formas.


Buenas prácticas al usar pseudo-elementos en proyectos reales

  • No metas contenido importante en content
  • Úsalos para decoración o refuerzo visual
  • Mantén el HTML limpio
  • Documenta estructuras complejas

CSS no tiene que demostrar nada

No siempre CSS es la mejor solución.

Si algo es demasiado complejo: usa SVG.


Errores frecuentes al dibujar con CSS usando pseudo-elementos

  • Olvidar position: relative
  • Abusar de z-index
  • Sobrecargar visualmente
  • Crear CSS difícil de mantener

Cuándo usar pseudo-elementos y cuándo no

Úsalos cuando:

  • quieras añadir decoración
  • necesites capas extra
  • quieras evitar más HTML

Evítalos cuando:

  • el contenido sea importante
  • la complejidad sea alta
  • SVG sea más claro

Preguntas frecuentes sobre pseudo-elementos en CSS

¿Se pueden hacer ilustraciones completas con CSS?
Sí, pero no siempre es lo más práctico.

¿Pseudo-elementos vs pseudo-clases?

  • pseudo-clases → estados
  • pseudo-elementos → partes

¿Afectan a la accesibilidad?
Sí, si metes contenido importante en ellos.


Dibujar mejor con CSS no consiste en añadir más, sino en decidir mejor

Los pseudo-elementos en CSS te permiten crear más con menos. Añadir capas, enriquecer interfaces y mejorar la claridad visual sin complicar el HTML.

Pero también exigen algo clave: criterio.

No se trata de añadir más capas, sino de reducir la fricción visual.

Si ya has practicado con formas básicas e iconos, los pseudo-elementos en CSS son el siguiente paso para construir ilustraciones más ricas sin complicar innecesariamente el HTML. Y cuando empieces a usarlos con criterio, verás que no solo mejoras tus dibujos con CSS: también mejoras tu forma de pensar componentes.

La Ley de Miller aplicada al diseño de interfaces: menos es más

La Ley de Miller aplicada al diseño de interfaces: menos es más

Como desarrolladora web, me he enfrentado en múltiples ocasiones al dilema de cómo organizar la información de forma efectiva para que el usuario no se sienta abrumado. A lo largo del tiempo, una de las ideas que más me ha ayudado a tomar decisiones de diseño más acertadas ha sido la Ley de Miller, también conocida como el “número mágico 7 ± 2”. Esta ley, aunque proviene de la psicología cognitiva, tiene una aplicación sorprendentemente útil en el mundo del diseño de interfaces.

En este artículo quiero compartirte, desde mi experiencia práctica, cómo aplicar esta ley a nuestras interfaces y por qué menos es, en realidad, mucho más. Vamos a desmenuzar el concepto, ver ejemplos concretos y descubrir cómo esta teoría puede ayudarte a diseñar experiencias más limpias, intuitivas y eficaces para quienes usan tus productos digitales.

¿Qué es la Ley de Miller?

Origen y base científica

La Ley de Miller fue propuesta por el psicólogo cognitivo George A. Miller en 1956. En su artículo “The Magical Number Seven, Plus or Minus Two”, Miller argumentaba que la memoria a corto plazo humana es capaz de retener entre 5 y 9 elementos de información simultáneamente, con una media de 7.

Este concepto se convirtió rápidamente en una referencia fundamental en el campo de la psicología cognitiva, pero también empezó a encontrar aplicaciones fuera de ese ámbito: desde la educación hasta, por supuesto, el diseño de interfaces.

Cómo impacta en la experiencia del usuario

Cuando una persona navega por una web o app, está tomando decisiones constantemente: qué menú elegir, dónde hacer clic, cómo interpretar un botón o una notificación. Si el número de opciones o elementos visibles supera ese rango de 5 a 9 ítems, el usuario comienza a sentirse saturado, desorientado o incluso frustrado. En ese contexto, la simplicidad no es una estética, es una necesidad cognitiva.

Aplicando la Ley de Miller al diseño de interfaces

Menos elementos, más comprensión

Cuando estamos diseñando una interfaz, ya sea un menú de navegación, un formulario, una tarjeta de producto o incluso un mensaje emergente, debemos preguntarnos:
¿Cuánta información estoy exigiendo procesar al usuario de golpe?

Aquí es donde la Ley de Miller entra en juego como una guía para limitar la cantidad de estímulos al mínimo necesario.

Menús de navegación

Uno de los lugares más evidentes donde aplicar esta ley es en los menús principales de navegación. Si alguna vez has llegado a un sitio con 12 ítems en el menú superior, sabes de lo que hablo: demasiadas opciones generan dudas.

Mi recomendación profesional: mantener el menú principal entre 5 y 7 elementos. Si hay más secciones, considera agruparlas en un desplegable bajo un ítem general como “Más” o “Otros”.

Formularios y tareas

Muchas veces se presentan al usuario listas interminables de campos para completar. Esto no solo agota, sino que ralentiza la conversión.

Una buena estrategia es dividir el formulario en pasos o bloques. Si un formulario tiene 15 campos, distribúyelos en 3 grupos de 5. Visualmente, la carga cognitiva se reduce, y el usuario siente que el proceso es más ágil.

Listas, botones y opciones de filtro

Mostrar más de 7 u 8 opciones sin jerarquía visual puede llevar al usuario a ignorarlas por completo. El resultado: no actúan.

¿La solución? Agrupar, destacar las opciones más relevantes y ofrecer filtros contextuales solo cuando realmente se necesiten.

Estrategias prácticas para aplicar la Ley de Miller

Divide, agrupa y oculta hasta que sea necesario

La mejor forma de ayudar al usuario no es dándole todo lo que necesita al mismo tiempo, sino guiándolo paso a paso. Estas son algunas técnicas efectivas:

1. Agrupación lógica de contenido

Usar espaciado, colores y tipografía para indicar qué elementos pertenecen al mismo grupo. Así, el cerebro puede procesar varios elementos como si fueran uno solo.

2. Prioriza y oculta lo secundario

Muestra solo las opciones clave y deja las demás en segundo plano. Por ejemplo, en un menú de filtros, los más usados deben estar visibles, y los demás pueden plegarse bajo un botón como “Mostrar más”.

3. Usa patrones reconocibles

Reducir el esfuerzo cognitivo no solo tiene que ver con la cantidad, sino también con la familiaridad. Cuando usamos patrones visuales comunes, como íconos estandarizados o layouts conocidos, el usuario los interpreta automáticamente.

¿Cuándo no aplicar la Ley de Miller al pie de la letra?

Casos donde el contexto manda

Aunque la Ley de Miller es muy útil, no es una regla absoluta. Hay situaciones donde mostrar más elementos puede estar justificado, siempre que haya soporte visual, contexto y jerarquía clara.

Interfaces expertas o técnicas

En dashboards dirigidos a perfiles técnicos, el número de elementos puede superar ese límite. La clave está en no tratar a todos los usuarios por igual: conoce tu público.

Visualizaciones complejas

En gráficas o reportes, el objetivo es analizar. Aquí, la clave es dar herramientas para filtrar o navegar, pero no esconder lo relevante.


Preguntas frecuentes (FAQs)

¿La Ley de Miller aplica también al diseño mobile?

Sí, y de hecho es aún más relevante en dispositivos móviles, donde el espacio visual es limitado. Cuanto más pequeña la pantalla, mayor la importancia de priorizar y agrupar.

¿Puedo mostrar más de 9 elementos si los agrupo visualmente?

Sí, eso se llama “chunking”. Puedes presentar 15 elementos, pero si están organizados en 3 bloques bien definidos, parecerán solo 3 a nivel cognitivo.

¿Qué herramientas de diseño me ayudan a aplicar esta ley?

Herramientas como Figma y Adobe XD permiten crear prototipos y jerarquías visuales claras. Y con Hotjar o Google Analytics puedes validar si el diseño está funcionando.


Diseñar pensando en el usuario es un acto de empatía

Como desarrolladora web, me he dado cuenta de que muchas veces queremos demostrar todo lo que sabemos o todo lo que ofrece nuestro producto… pero ese impulso puede ir en contra del usuario. El diseño no se trata de presumir, se trata de acompañar, de guiar, de hacer fácil lo complejo.

Aplicar la Ley de Miller no es recortar contenido sin criterio; es priorizar y estructurar pensando en cómo piensa la persona al otro lado de la pantalla.

En un mundo donde cada segundo cuenta, donde la atención es escasa y los estímulos son infinitos, el diseño inteligente es el que sabe cuándo callar para que el usuario escuche lo importante.