Un login es una de las pantallas más repetidas. Aunque “solo sea un formulario”, aquí se nota la diferencia entre un diseño improvisado y una interfaz cuidada: jerarquía visual, alineación, accesibilidad y mensajes claros.
1) Estructura base (card centrada)
<main class="min-vh-100 d-flex align-items-center bg-light">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-sm-10 col-md-6 col-lg-4">
<div class="card shadow-sm">
<div class="card-body p-4">
<h1 class="h4 mb-3">Iniciar sesión</h1>
<form id="loginForm" novalidate>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input id="email" type="email" class="form-control" required autocomplete="username">
<div class="invalid-feedback">Introduce un email válido.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Contraseña</label>
<input id="password" type="password" class="form-control" required minlength="6" autocomplete="current-password">
<div class="invalid-feedback">La contraseña debe tener al menos 6 caracteres.</div>
</div>
<button class="btn btn-primary w-100" type="submit">Entrar</button>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
2) Validación básica con JavaScript
Bootstrap funciona muy bien con el patrón needs-validation, pero también puedes aplicar is-invalid tú mismo. Ejemplo simple:
const form = document.querySelector("#loginForm");
form.addEventListener("submit", (e) => {
e.preventDefault();
if (!form.checkValidity()) {
form.querySelectorAll("input").forEach((i) => {
i.classList.toggle("is-invalid", !i.checkValidity());
});
return;
}
// Aquí ya enviarías el login (fetch/AJAX) o redirigirías.
alert("Login OK (demo)");
});
3) Detalles de UX que suben el nivel
- Autocompletado:
autocomplete="username"ycurrent-password. - Mensajes cortos: explica el error y cómo solucionarlo.
- Estado loading: desactiva el botón mientras envías.
- Accesibilidad: labels siempre asociados, contraste suficiente, foco visible.
