DEV Community

Silviu Technology
Silviu Technology

Posted on

React y CSS para feedback de email estable

En formularios de registro casi siempre ponemos cariño en el boton, el campo y el copy principal. Luego llega la comprobacion asincrona del email y todo se pone raro: aparece una linea nueva, el layout salta, el foco se siente menos firme y el usuario duda si debe esperar o seguir. En equipos frontend esto parece un detalle chico, pero termina afectando conversion, claridad y mantenimiento.

Mi forma de abordarlo en React es separar la ayuda visual del permiso real para enviar. Si el sistema esta revisando disponibilidad, dominio o señales de riesgo, ese estado no deberia empujar el boton ni rehacer media interfaz. Es una decision de UX, pero tambien de rendimiento y accesibilidad.

Por que un mensaje pequeño mueve tanto la experiencia

Cuando el mensaje de ayuda aparece sin espacio reservado, el formulario cambia de altura y mete friccion. Google define un buen objetivo de Cumulative Layout Shift por debajo de 0.1, y aunque un input no siempre rompa toda la pagina, ese micro salto sí se nota en una tarea tan sensible como crear cuenta. El problema no es solo visual: la persona siente que hizo algo mal incluso cuando el sistema solo esta pensando.

Tambien he visto que este patron complica QA. Si el equipo prueba registros con un correo desechable gratis o con bandejas de test, el feedback cambia tan rapido que cuesta distinguir warning temporal de error real. Peor aun cuando en notas internas alguien llama al caso temp org mail y nadie aterriza cómo se debe ver eso para usuarios normales.

Separa estados de negocio y estados de ayuda

El primer cambio util es modelar dos cosas distintas:

  • si el formulario puede enviarse
  • si el campo necesita mostrar contexto adicional

Eso evita que cada comprobacion secundaria controle el CTA. El submit depende de formato minimo y campos requeridos. La ayuda asincrona vive aparte. Es un ajuste simple, pero baja bastante el nerviosismo de la UI.

type HintState =
  | { kind: "idle" }
  | { kind: "checking" }
  | { kind: "info"; message: string }
  | { kind: "warning"; message: string };

function EmailField() {
  const [email, setEmail] = useState("");
  const [hint, setHint] = useState<HintState>({ kind: "idle" });

  useEffect(() => {
    if (!email.includes("@")) {
      setHint({ kind: "idle" });
      return;
    }

    const controller = new AbortController();
    setHint({ kind: "checking" });

    const timer = setTimeout(async () => {
      const response = await fetch(`/api/email-check?value=${encodeURIComponent(email)}`, {
        signal: controller.signal,
      });
      const result = await response.json();
      setHint(result.message ? { kind: result.level, message: result.message } : { kind: "idle" });
    }, 250);

    return () => {
      controller.abort();
      clearTimeout(timer);
    };
  }, [email]);

  const canSubmit = email.length > 3;

  return (
    <div className="field">
      <input
        type="email"
        value={email}
        onChange={(event) => setEmail(event.target.value)}
        aria-describedby="email-hint"
      />
      <p id="email-hint" className="field-hint" aria-live="polite">
        {hint.kind === "checking" ? "Revisando email..." : hint.kind !== "idle" ? hint.message : " "}
      </p>
      <button disabled={!canSubmit}>Crear cuenta</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Este patron se parece bastante a evitar saltos en flujos de verificacion: no mezclas señales y le das a cada estado un trabajo claro. La UI queda mas tranquila, y eso se nota altiro.

Reserva espacio con CSS antes del primer check

La parte que mas se olvida no esta en React sino en CSS. Si sabes que el campo puede mostrar ayuda, reserva esa altura desde el inicio:

.field {
  display: grid;
  gap: 0.5rem;
}

.field-hint {
  min-height: 1.25rem;
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.4;
  color: #5b6470;
}
Enter fullscreen mode Exit fullscreen mode

Con ese min-height, el layout no brinca cuando entra el mensaje. Parece poca cosa, pero ayuda bastante en moviles y en formularios compactos donde el boton queda justo debajo. Nielsen Norman Group insiste en la visibilidad del estado del sistema, y yo añadiria esto: el estado debe ser visible sin empujar el resto de la pantalla.

Tambien conviene mantener el mensaje corto. Si cada revision devuelve un parrafo, el espacio reservado deja de servir. Mejor una linea clara y, si hace falta mas contexto, un enlace secundario o un patron progresivo.

Que mostrar cuando detectas correos temporales

Aqui vale la pena ser pragmatico. Detectar un dominio temporal no siempre significa bloquear. En varios flujos de growth o soporte, incluso he visto equipos usar temp mail so para reproducir activaciones, aislar onboarding y revisar mensajes sin contaminar bandejas reales. El frontend no deberia asumir fraude solo por esa señal.

Yo prefiero tres respuestas posibles:

  • info: "revisa que puedas abrir este correo luego"
  • warning: "algunos dominios temporales pierden mensajes de activacion"
  • bloqueo real: solo si la politica del producto lo exige de verdad

Esa gradacion conversa mejor con producto, soporte y analitica. Incluso se alinea con ideas que ya aparecen en alertas de trial que si orientan: el mensaje sirve cuando ayuda a decidir, no cuando mete panico.

Checklist rapido para revisar el patron

Antes de dar por bueno el campo, yo miro esto:

  • el hint tiene altura reservada desde el primer render
  • aria-live anuncia cambios sin interrumpir demasiado
  • el submit no depende de checks auxiliares
  • las requests viejas se cancelan al cambiar el valor
  • warning y error usan tonos distintos, no el mismo martillo

No es un cambio enorme, pero suele mejorar bastante la sensacion final. Aveses la mejor optimizacion no es una API nueva, sino una interfaz que deja de moverse cuando nadie se lo pidio.

Q&A

¿Siempre hay que reservar espacio?

Si el mensaje puede aparecer debajo del input, yo diria que si. Es una proteccion barata contra CLS y contra esa sensacion medio torpe de formulario inestable.

¿Y si el producto quiere bloquear emails temporales?

Hazlo en submit o con una regla explicitada muy bien. No conviertas cada check intermedio en una alarma total, por que la UX se vuelve cansona rapido.

¿Esto aplica fuera de registro?

Sí. Tambien en recovery, invitaciones, cambios de email y cualquier flujo donde una comprobacion remota quiera colarse entre la escritura y la accion final.

Top comments (0)