Estructura semántica con HTML5
La base de una página de login accesible radica en una estructura HTML semántica. La declaración del doctype y las etiquetas meta para codificación y viewport son esenciales.
<form action="/autenticar" method="POST" role="form" aria-label="Formulario de acceso">
<label for="correo">Dirección de email</label>
<input type="email" id="correo" name="correo" required aria-describedby="ayuda-correo">
<span id="ayuda-correo" class="indicador">Formato: usuario@dominio.com</span>
</form>
El uso de aria-* y role mejora la experiencia con tecnologías asistivas. Los atributos nativos como required proporcionan validación básica.
Estilos y diseño visual con CSS
La apariencia profesional se logra mediante técnicas modernas de CSS. El uso de Flexbox o Grid simplifica el centrado y la alineación.
.contenedor-login {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f0f2f5;
}
.formulario {
background: white;
padding: 2.5rem;
border-radius: 0.75rem;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
width: min(90vw, 24rem);
}
El modelo de caja border-box facilita el cálculo de dimensiones. Se recomienda su aplicación global mediante *, *::before, *::after { box-sizing: border-box; }.
Interacciones y estados visuales
Los cambios sutiles al interactuar con elementos mejoran la percepción del usuario.
.campo-entrada {
padding: 0.75rem 1rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
.campo-entrada:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
}
.boton-primario:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
Diseño adaptable con Media Queries
La estrategia mobile-first garantiza compatibilidad en todos los dispositivos. Se definen puntos de quiebre basados en el contenido.
/* Estilos base para móviles */
.formulario { width: 95%; }
/* Punto de quiebre para tabletas */
@media (min-width: 768px) {
.formulario { width: 80%; max-width: 28rem; }
}
/* Punto de quiebre para escritorio */
@media (min-width: 1024px) {
.formulario { box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1); }
}
La unidad rem es preferible para dimensiones tipográficas, facilitando ajustes de escala a nivel global.
Interactividad con JavaScript
La lógica del cliente maneja eventos, valida datos y proporciona retroalimentación imediata.
const formularioAcceso = document.getElementById('acceso-form');
formularioAcceso.addEventListener('submit', function(evento) {
evento.preventDefault();
const email = document.getElementById('email-input').value.trim();
const clave = document.getElementById('clave-input').value;
if (!validarEmail(email)) {
mostrarError('email-input', 'Formato de email inválido');
return;
}
if (clave.length < 8) {
mostrarError('clave-input', 'Mínimo 8 caracteres');
return;
}
// Lógica de envío
});
function validarEmail(direccion) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(direccion);
}
Los eventos input y blur permiten validaciones en tiempo real. La manipulación del DOM debe ser mínima para optimizar rendimiento.
Funcionalidad mostrar/ocultar contraseña
const alternarVisibilidad = (campoId) => {
const campo = document.getElementById(campoId);
const tipoActual = campo.type;
campo.type = tipoActual === 'password' ? 'text' : 'password';
// Alternar ícono visual
};
Gestión de recursos y rendimiento
La optimización de activos es crucial para tiempos de carga rápidos.
Imágenes eficientes
Formatos como WebP ofrecen mejor compresión que JPEG o PNG. Se implementa con <picture> para compatibilidad:
<picture>
<source srcset="fondo.webp" type="image/webp">
<img src="fondo.jpg" alt="Fondo decorativo" loading="lazy">
</picture>
Las imágenes base64 son adecuadas solo para recursos muy pequeños (menos de 2KB).
Carga de fuentes personalizadas
@font-face {
font-family: 'MiFuente';
src: url('fuentes/mifuente.woff2') format('woff2'),
url('fuentes/mifuente.woff') format('woff');
font-display: swap;
unicode-range: U+0000-00FF; /* Subconjunto básico */
}
font-display: swap evita el bloqueo de renderizado de texto. La subconjuntización reduce singificativamente el tamaño del archivo.
Estructura de archivos del proyecto
Una organización clara facilita el mantenimiento y la colaboración.
proyecto-login/
├── index.html
├── css/
│ ├── estilos-base.css
│ └── componentes/
│ ├── formulario.css
│ └── boton.css
├── js/
│ ├── validacion.js
│ └── interactuar.js
├── activos/
│ ├── imagenes/
│ └── fuentes/
└── pruebas/
└── pruebas-unitarias/
Las rutas relativas y absolutas deben ser consistentes. Se recomienda el uso de rutas raíz (/) para activos estáticos.
Principios de seguridad básica
Las medidas preventivas del lado del cliente complementan la seguridad del servidor.
// Sanitización básica contra XSS
function escaparHTML(texto) {
const elementoTemporal = document.createElement('div');
elementoTemporal.textContent = texto;
return elementoTemporal.innerHTML;
}
// Implementación básica de protección contra CSRF
function obtenerTokenCSRF() {
return document.querySelector('meta[name="csrf-token"]')?.content;
}
Los formularios deben usar HTTPS. Las contraseñas nunca deben almacenarse en localStorage sin cifrado adecuado.
Preparación para validación del lado del servidor
La validación del cliente es una mejora de UX, no un reemplazo de la seguridad del servidor. El formulario debe estructurarse para integración futura:
<form action="/api/autenticar" method="POST">
<input type="hidden" name="_token" value="" id="token-csrf">
<!-- Campos visibles -->
</form>