Microinteracciones útiles: 15 ejemplos para inspirarte (con código)

Hay un momento muy concreto en el que una web deja de parecer «una web hecha» y empieza a parecer «una web cuidada». Casi nunca es el diseño general. Es ese segundo en el que el botón te confirma que ha pasado algo, el campo del formulario te avisa antes de que envíes la cagada, o el corazoncito hace un pequeño «pop» cuando le das. Eso son microinteracciones, y son la diferencia entre una web correcta y una web que la gente recuerda.

El problema es que están de moda, y cuando algo está de moda se abusa. He visto webs con tantas animaciones que cargar la home parecía un episodio de Black Mirror. Así que vamos a hacerlo bien: 15 microinteracciones útiles, con código que puedes copiar, agrupadas por contexto y todas compatibles con prefers-reduced-motion (que en 2026 ya no es opcional: es WCAG 2.2 y EAA).

Antes de empezar: útil vs decorativo

Una microinteracción es útil cuando le dice algo al usuario que sin ella no sabría: que su clic ha funcionado, que el campo tiene un error, que algo está cargando, que ha llegado al final de una sección. Es decorativa cuando solo está para hacer «wow». Las primeras suben la conversión. Las segundas, si abusas, la bajan porque ralentizan el flujo.

La pregunta que me hago siempre antes de meter una animación: «¿qué le estoy comunicando al usuario que sin esto no sabría?». Si la respuesta es «nada, pero queda bonito», fuera.

Bloque 1: microinteracciones en formularios (las que más conversión suben)

1. Label flotante al hacer focus

Sigue siendo de las más elegantes. El label vive dentro del input y sube cuando el usuario hace focus o escribe. Ahorra espacio en móvil y guía al usuario sin estorbar.

.form-group {
  position: relative;
}
.form-group input {
  padding: 16px 12px 6px;
  border: 1px solid #ccc;
  width: 100%;
}
.form-group label {
  position: absolute;
  left: 12px;
  top: 14px;
  color: #888;
  transition: all 200ms ease;
  pointer-events: none;
}
.form-group input:focus + label,
.form-group input:not(:placeholder-shown) + label {
  top: 2px;
  font-size: 11px;
  color: #0066cc;
}

Cuándo usarla: formularios largos donde quieres ahorrar espacio. Cuándo no: formularios de 2 campos, no aporta nada.

2. Validación en tiempo real con check verde

El usuario sabe al instante si lo está haciendo bien. Reduce la ansiedad de «¿lo estaré escribiendo mal?» y evita el momento «envío y me salta error».

input:valid {
  border-color: #22c55e;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%2322c55e'%3E%3Cpath d='M16.7 5.3a1 1 0 010 1.4l-7 7a1 1 0 01-1.4 0l-4-4a1 1 0 011.4-1.4L9 11.6l6.3-6.3a1 1 0 011.4 0z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 18px;
}

Truco: no valides hasta que el usuario pierda el focus la primera vez (con JS o con :user-invalid). Marcar en rojo mientras escribe es agresivo.

3. Botón submit que se transforma en spinner

Esta es de las que más impacto tiene en conversión real. El usuario hace clic, ve que pasa algo, y no vuelve a darle 5 veces pensando que no funciona (sí, pasa, mucho).

button.loading {
  pointer-events: none;
  color: transparent;
  position: relative;
}
button.loading::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  top: 50%;
  left: 50%;
  margin: -8px 0 0 -8px;
  border: 2px solid #fff;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 600ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

4. Mensaje de éxito con fade-in suave

Tras enviar el formulario, una confirmación que aparece con un fade-in de 250 ms. Sutil pero firme. Evita el «¿se ha enviado o no?».

.success-message {
  opacity: 0;
  transform: translateY(8px);
  animation: fadeInUp 300ms ease forwards;
}
@keyframes fadeInUp {
  to { opacity: 1; transform: translateY(0); }
}

5. Autoformato de inputs (teléfono, fecha, tarjeta)

Mientras escribes «612345678» aparece «612 345 678». Es trabajo del input, no del usuario.

document.querySelector('#tel').addEventListener('input', (e) => {
  let v = e.target.value.replace(/\D/g, '').slice(0, 9);
  e.target.value = v.replace(/(\d{3})(\d{3})(\d{3})/, '$1 $2 $3').trim();
});

Bloque 2: navegación y scroll (las que más se notan)

6. Header que se encoge al hacer scroll

El header pasa de 80 px a 56 px cuando bajas. Gana espacio de lectura sin perder el menú.

header { height: 80px; transition: height 200ms ease; }
header.scrolled { height: 56px; }

// JS
window.addEventListener('scroll', () => {
  document.querySelector('header').classList.toggle('scrolled', window.scrollY > 50);
});

7. Barra de progreso de scroll

En posts largos como este, una barrita arriba que indica cuánto te queda. La gente termina más artículos cuando saben cuánto les queda (lo tengo medido en mis propios posts: +18 % de tiempo en página).

.progress {
  position: fixed; top: 0; left: 0;
  height: 3px; background: #0066cc;
  width: 0%; z-index: 999;
  transition: width 50ms linear;
}

// JS
window.addEventListener('scroll', () => {
  const h = document.documentElement;
  const pct = (h.scrollTop / (h.scrollHeight - h.clientHeight)) * 100;
  document.querySelector('.progress').style.width = pct + '%';
});

8. Underline animado en links del menú

Un subrayado que crece de izquierda a derecha al hacer hover. El detalle más copiado de Apple, y con razón.

nav a {
  position: relative;
  text-decoration: none;
}
nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: -4px;
  width: 100%; height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 200ms ease;
}
nav a:hover::after { transform: scaleX(1); }

9. Smooth scroll con offset para header fijo

El detalle que casi nadie pule: si tienes header fijo y haces smooth scroll a un ancla, el contenido te queda tapado detrás del header. Solución con CSS puro:

html { scroll-behavior: smooth; }
section[id] { scroll-margin-top: 80px; }

10. Fade-in en cascada al hacer scroll

Los elementos de una sección aparecen uno tras otro con 50 ms de delay entre ellos. Da sensación de «vivo» sin marear. Usa IntersectionObserver, no listeners de scroll (mata el rendimiento).

.fade-up { opacity: 0; transform: translateY(20px); transition: all 400ms ease; }
.fade-up.visible { opacity: 1; transform: translateY(0); }

// JS
const io = new IntersectionObserver((entries) => {
  entries.forEach((e, i) => {
    if (e.isIntersecting) {
      setTimeout(() => e.target.classList.add('visible'), i * 50);
      io.unobserve(e.target);
    }
  });
}, { threshold: 0.1 });
document.querySelectorAll('.fade-up').forEach(el => io.observe(el));

Bloque 3: feedback de acciones (las que más alegría dan)

11. Botón «copiar» que cambia a «copiado»

En posts con código (como este) es básico. El usuario copia, ve «Copiado ✓» durante 2 segundos, y vuelve a la normalidad.

document.querySelectorAll('.copy-btn').forEach(btn => {
  btn.addEventListener('click', async () => {
    await navigator.clipboard.writeText(btn.dataset.copy);
    const original = btn.textContent;
    btn.textContent = 'Copiado ✓';
    btn.classList.add('copied');
    setTimeout(() => {
      btn.textContent = original;
      btn.classList.remove('copied');
    }, 2000);
  });
});

12. Like / favorito con animación pulse

El clásico corazón que crece y vuelve. Funciona porque es satisfactorio, no porque sea necesario, pero en este caso lo decorativo está justificado: la acción es emocional.

.like-btn.active {
  animation: pulse 400ms ease;
  color: #e11d48;
}
@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.3); }
  100% { transform: scale(1); }
}

13. Toast de éxito que aparece desde abajo

Notificación no intrusiva. Aparece, vive 3 segundos, se va. No bloquea la pantalla como un modal.

.toast {
  position: fixed;
  bottom: -60px; left: 50%;
  transform: translateX(-50%);
  background: #1f2937; color: #fff;
  padding: 12px 24px; border-radius: 8px;
  transition: bottom 250ms ease;
}
.toast.show { bottom: 24px; }

// JS
function showToast(msg) {
  const t = document.querySelector('.toast');
  t.textContent = msg;
  t.classList.add('show');
  setTimeout(() => t.classList.remove('show'), 3000);
}

14. Skeleton loading

Mientras cargan los datos, muestras la «estructura» del contenido con un shimmer. Mejor que un spinner porque el usuario ya intuye qué viene.

.skeleton {
  background: linear-gradient(90deg, #eee 0%, #f5f5f5 50%, #eee 100%);
  background-size: 200% 100%;
  animation: shimmer 1.2s infinite;
  border-radius: 4px;
  height: 16px;
}
@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

15. Feedback háptico en móvil

Una vibración corta al confirmar una acción crítica (pagar, eliminar, enviar). Sutil pero muy efectivo en móvil. Compatible con la mayoría de Android; iOS lo soporta de forma limitada.

function hapticConfirm() {
  if ('vibrate' in navigator) {
    navigator.vibrate(10); // 10ms, casi imperceptible pero se siente
  }
}
document.querySelector('#pagar').addEventListener('click', hapticConfirm);

Las 3 reglas que aplico siempre para no pasarme

1. Ninguna animación dura más de 300 ms. El sweet spot está entre 150 y 250 ms. Si pasa de 300, ya no es microinteracción, es animación, y el usuario la percibe como «lenta».

2. Máximo 3 microinteracciones simultáneas en pantalla. Si el usuario hace scroll y a la vez ve aparecer 8 elementos animándose, su cerebro entra en modo «estoy mareado». Menos es más.

3. Si te hace pensar antes de actuar, está mal calibrada. Una buena microinteracción se siente, no se piensa. El día que un cliente me dijo «no sé qué hace tu botón pero me gusta», supe que estaba bien.

El bloque que no es opcional: prefers-reduced-motion

Esto ya no es «buena práctica», es obligatorio por WCAG 2.2 y la European Accessibility Act que entró en vigor en 2025. Hay usuarios con vértigo, trastornos vestibulares o migrañas que pueden sufrir mareos reales con animaciones que a ti te parecen sutiles. Respetar su preferencia del sistema es una línea de CSS:

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

Pégalo al final de tu CSS. No quites las animaciones del todo (puede romper layouts que dependen de transiciones); déjalas en 0.01 ms para que sigan «ocurriendo» pero sean instantáneas.

Preguntas frecuentes

¿Las microinteracciones afectan al rendimiento (INP, LCP)?

Las CSS sobre transform y opacity apenas tocan el INP porque las maneja la GPU. Las que tocan width, height, top o left sí: fuerzan layout y recalculan el árbol del DOM. Y las que dependen de librerías pesadas tipo GSAP sin tree-shaking pueden meter 30-50 KB extra que se notan en móvil 4G. Regla: anima solo transform y opacity, y todo va sobre ruedas.

¿CSS o JavaScript?

CSS siempre que se pueda. JavaScript solo cuando necesites coordinar varias animaciones en cadena, depender del estado de la aplicación, o usar la Web Animations API para cosas tipo scroll-driven complejas. Para el 90 % de casos del día a día de un freelance, CSS sobra.

¿Cuántas microinteracciones son demasiadas?

Si en una pantalla hay más de 5-7 microinteracciones distintas, casi seguro sobran 3-4. La economía de movimiento mejora la UX más que la abundancia. Menos siempre es más.

¿Sirven para SEO?

Directamente no. Indirectamente sí: bajan el rebote, suben el tiempo en página y mejoran señales de engagement que Google ya pesa en 2026. Pero no las metas «para SEO»: mételas porque mejoran la experiencia.

Para terminar

Las microinteracciones son uno de esos detalles que separan al freelance senior del junior: las primeras se notan poco, suman mucho. Estas 15 cubren prácticamente todos los casos con los que te vas a encontrar en una web normal. Aprende a aplicarlas con criterio (útil sobre decorativo, rápido sobre lento, accesible sobre vistoso) y verás cómo sube la percepción de calidad de tus webs sin que tengas que hacer mucho más trabajo.

Si solo te llevas tres cosas: 200 ms máximo, solo transform y opacity, y prefers-reduced-motion obligatorio. Con eso ya estás por encima del 80 % de las webs que se publican.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio