HTML/CSS lab

14 · Interacción

Forms

Un formulario accesible se arma de a pares: cada control tiene una etiqueta asociada, cada error se anuncia, y todo funciona sin mouse. Tailwind no toca ese comportamiento — solo lo viste.

label + for/id: la asociación obligatoria

Sin un label correctamente asociado, un lector de pantalla anuncia el input como 'edit text' sin más contexto — y hacer click en el texto no enfoca el campo.

<label for="email" class="block text-sm font-medium mb-1">
  Correo electrónico
</label>
<input
  id="email"
  type="email"
  class="w-full rounded-lg border border-stone-300 px-3 py-2"
/>

Tipos de input semánticos

El atributo type no solo valida formato: en mobile cambia el teclado que aparece (numérico para tel, con @ visible para email), y el navegador puede ofrecer autocompletado apropiado.

<input type="email" />
<input type="tel" />
<input type="number" min="0" max="10" />
<input type="date" />
<input type="password" />

fieldset + legend: agrupar controles relacionados

fieldset dibuja un grupo semántico (por ejemplo, un set de radios) y legend le da un título. Los lectores de pantalla anuncian la legend antes de cada opción del grupo.

Método de envío
<fieldset class="border border-stone-300 rounded-lg p-4">
  <legend class="text-sm font-medium px-1">Método de envío</legend>
  <label class="flex items-center gap-2 text-sm">
    <input type="radio" name="envio" value="standard" />
    Estándar (3-5 días)
  </label>
  <label class="flex items-center gap-2 text-sm">
    <input type="radio" name="envio" value="express" />
    Express (24h)
  </label>
</fieldset>

Estado de error accesible

Un borde rojo no es suficiente: alguien con daltonismo o usando un lector de pantalla necesita el mensaje de error asociado explícitamente vía aria-describedby, y el campo marcado con aria-invalid.

Debe tener al menos 8 caracteres.

<label for="pw" class="block text-sm font-medium mb-1">Contraseña</label>
<input
  id="pw"
  type="password"
  aria-invalid="true"
  aria-describedby="pw-error"
  class="w-full rounded-lg border-2 border-rose-500 px-3 py-2"
/>
<p id="pw-error" class="text-sm text-rose-600 mt-1">
  Debe tener al menos 8 caracteres.
</p>

button type: submit vs button vs reset

Dentro de un <form>, un <button> sin type explícito actúa como submit por defecto — una fuente clásica de bugs cuando quieres un botón que no envíe el formulario (ej: 'mostrar contraseña').

<button type="submit">Enviar</button>
<button type="button">Mostrar contraseña</button>
<button type="reset">Limpiar formulario</button>

select y textarea

select da opciones cerradas de una lista; textarea es un input multilínea. Ambos necesitan label igual que un input normal.

<label for="pais" class="block text-sm font-medium mb-1">País</label>
<select id="pais" class="w-full rounded-lg border border-stone-300 px-3 py-2">
  <option value="cl">Chile</option>
  <option value="ar">Argentina</option>
</select>

<label for="msg" class="block text-sm font-medium mb-1 mt-3">Mensaje</label>
<textarea id="msg" rows="3" class="w-full rounded-lg border border-stone-300 px-3 py-2"></textarea>