Paso 1 de 5
La interfaz
Un <output> para la contraseña, un botón para copiarla, una barra de fuerza y un formulario con la longitud (un control deslizante de 8 a 64) y casillas para los tipos de caracteres.
La contraseña usa una fuente monoespaciada y overflow-wrap: anywhere, para que una contraseña larga salte de línea en lugar de salirse de la caja.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Generador de contraseñas</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<main class="generador">
<h1>Generador de contraseñas</h1>
<div class="resultado">
<output id="clave" class="clave" aria-live="polite">Pulsa «Generar»</output>
<button type="button" id="copiar" class="copiar">Copiar</button>
</div>
<div class="fuerza" aria-hidden="true"><span id="barra"></span></div>
<p id="nivel" class="nivel"></p>
<form id="opciones" class="opciones">
<label for="longitud">Longitud: <strong id="valor-longitud">16</strong></label>
<input type="range" id="longitud" min="8" max="64" value="16">
<label><input type="checkbox" id="mayusculas" checked> Mayúsculas (A-Z)</label>
<label><input type="checkbox" id="numeros" checked> Números (0-9)</label>
<label><input type="checkbox" id="simbolos" checked> Símbolos (!@#…)</label>
<button type="submit">Generar</button>
</form>
</main>
<script src="app.js"></script>
</body>
</html>
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 16px;
background: #ecfeff;
color: #083344;
font-family: system-ui, sans-serif;
}
.generador {
width: min(100%, 440px);
padding: 24px;
border-radius: 20px;
background: #ffffff;
box-shadow: 0 12px 32px rgb(8 51 68 / 0.12);
}
h1 { margin: 0 0 16px; font-size: 1.4rem; }
.resultado { display: flex; gap: 8px; }
.clave {
flex: 1;
min-width: 0;
padding: 12px;
border-radius: 10px;
background: #083344;
color: #a5f3fc;
font-family: ui-monospace, Consolas, monospace;
font-size: 1.05rem;
overflow-wrap: anywhere;
}
button {
padding: 10px 16px;
border: 0;
border-radius: 10px;
background: #0e7490;
color: #ffffff;
font: inherit;
font-weight: 700;
cursor: pointer;
}
button:focus-visible, input:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
.fuerza { height: 8px; margin-top: 12px; border-radius: 999px; background: #e2e8f0; overflow: hidden; }
.fuerza span { display: block; height: 100%; width: 0; background: #dc2626; transition: width 300ms ease, background-color 300ms ease; }
.nivel { min-height: 1.5em; margin: 6px 0 16px; font-weight: 600; }
.opciones { display: grid; gap: 10px; }
.opciones label { display: flex; align-items: center; gap: 8px; }
.opciones input[type="range"] { width: 100%; accent-color: #0e7490; }
.opciones input[type="checkbox"] { width: 1.1rem; height: 1.1rem; accent-color: #0e7490; }
// El JavaScript llega en el paso 2