Etichette e associazioni

Esempio negativo

Accesso

Ricordami
<!-- ❌ Negativo: Etichette mancanti -->
<form>
  <h4>Accesso</h4>

  <!-- Nessuna etichetta associata -->
  <input
    type="email"
    placeholder="La tua email">

  <!-- Nessuna etichetta associata, nessun autocompletamento -->
  <input
    type="password"
    placeholder="Password">

  <!-- Casella di controllo non associata -->
  <input type="checkbox">
  <span>Ricordami</span>

  <button type="submit">Accedi</button>
</form>

Esempio positivo

Accesso

Non condivideremo mai la tua email
Minimo 8 caratteri con maiuscole e numeri
<!-- ✅ Positivo: Etichette esplicite e associate -->
<form>
  <h4>Accesso</h4>

  <!-- Etichetta associata tramite for/id -->
  <label for="email-good">Indirizzo email</label>
  <input
    type="email"
    id="email-good"
    placeholder="esempio@dominio.com"
    required
    aria-describedby="email-help"
    autocomplete="email">
  <div id="email-help">
    Non condivideremo mai la tua email
  </div>

  <!-- Etichetta con istruzioni -->
  <label for="password-good">Password</label>
  <input
    type="password"
    id="password-good"
    required
    aria-describedby="password-help"
    autocomplete="current-password">
  <div id="password-help">
    Minimo 8 caratteri con maiuscole e numeri
  </div>

  <!-- Casella di controllo correttamente associata -->
  <input type="checkbox" id="remember-good">
  <label for="remember-good">Ricordami</label>

  <button type="submit">Accedi</button>
</form>

Spiegazione: Ogni campo del modulo deve avere un'etichetta esplicita e correttamente associata. I placeholder non sostituiscono le etichette.

Validazione e messaggi di errore

Esempio negativo

Iscrizione newsletter

<!-- ❌ Negativo: Errori annunciati male -->
<form @submit.prevent="submitBadForm">
  <h4>Iscrizione newsletter</h4>

  <!-- Nessuna associazione etichetta-input -->
  <label>Nome</label>
  <input
    type="text"
    v-model="badForm.name"
    :class="{ 'error': submitted && !badForm.name }">

  <!-- Solo errore visivo -->
  <label>Email</label>
  <input
    type="email"
    v-model="badForm.email"
    :class="{ 'error': submitted && !isValidEmail(badForm.email) }">

  <!-- Nessuna informazione sull'errore -->
  <label>Età</label>
  <input
    type="number"
    v-model="badForm.age"
    :class="{ 'error': submitted && badForm.age < 16 }">

  <!-- Messaggio di errore generico e inutile -->
  <div v-if="submitted && badFormErrors.length">
    ❌ Correggi gli errori
  </div>

  <button type="submit">Iscriviti</button>
</form>

Esempio positivo

Iscrizione newsletter

Devi avere almeno 16 anni
<!-- ✅ Positivo: Errori chiaramente associati -->
<form @submit.prevent="submitGoodForm" novalidate>
  <h4>Iscrizione newsletter</h4>

  <!-- Riepilogo errori con navigazione -->
  <div
    v-if="submitted && goodFormErrors.length"
    role="alert"
    aria-labelledby="error-summary-title">
    <h5 id="error-summary-title">Errori da correggere:</h5>
    <ul>
      <li v-for="error in goodFormErrors" :key="error.field">
        <a :href="`#${error.field}-good-form`">
          
        </a>
      </li>
    </ul>
  </div>

  <!-- Campo con errore specifico associato -->
  <label for="name-good-form">Nome completo *</label>
  <input
    type="text"
    id="name-good-form"
    v-model="goodForm.name"
    required
    aria-invalid="submitted && !goodForm.name"
    aria-describedby="name-error">
  <div
    v-if="submitted && !goodForm.name"
    id="name-error"
    role="alert">
    ❌ Il nome è obbligatorio
  </div>

  <button type="submit">Iscriviti</button>
</form>

Spiegazione: Gli errori devono essere chiaramente annunciati, associati ai campi pertinenti e fornire istruzioni precise per la correzione.

Gruppi di campi con aria-labelledby

Esempio negativo

Preferenze

<!-- ❌ Negativo: Struttura disorganizzata -->
<form>
  <h4>Preferenze</h4>

  <!-- Elementi misti senza logica -->
  <div>
    <input type="radio" name="contact-bad"
           id="email-contact-bad" value="email">
    <label for="email-contact-bad">Email</label>

    <input type="checkbox" id="newsletter-bad"
           value="newsletter">
    <label for="newsletter-bad">Newsletter</label>
  </div>

  <div>
    <input type="radio" name="contact-bad"
           id="phone-contact-bad" value="phone">
    <label for="phone-contact-bad">Telefono</label>

    <!-- Campo di testo senza contesto -->
    <input type="text" placeholder="Nome">
  </div>

  <div>
    <input type="checkbox" id="offers-bad" value="offers">
    <label for="offers-bad">Offerte</label>

    <input type="radio" name="contact-bad"
           id="sms-contact-bad" value="sms">
    <label for="sms-contact-bad">SMS</label>
  </div>

  <!-- Select senza contesto -->
  <select>
    <option>Scegli...</option>
    <option>Giornaliera</option>
    <option>Settimanale</option>
  </select>

  <button type="submit">OK</button>
</form>

Esempio positivo

Preferenze di contatto

Informazioni personali
Il tuo nome completo come apparirà nelle nostre comunicazioni
Useremo questa email per tutte le nostre comunicazioni
Metodo di contatto preferito *
Tipi di comunicazioni

Seleziona i tipi di comunicazioni che desideri ricevere

Frequenza delle comunicazioni
Imposta la frequenza massima dei nostri invii
<!-- ✅ Positivo: Gruppi logici con aria-labelledby -->
<form>
  <h4>Preferenze di contatto</h4>

  <!-- Sezione informazioni personali -->
  <div role="group" aria-labelledby="personal-info-title">
    <h5 id="personal-info-title">
      Informazioni personali
    </h5>

    <label for="name-good">Nome completo *</label>
    <input
      type="text"
      id="name-good"
      name="name"
      required
      aria-required="true"
      aria-describedby="name-desc">
    <span id="name-desc">
      Il tuo nome completo come apparirà nelle nostre comunicazioni
    </span>
  </div>

  <!-- Gruppo di pulsanti radio -->
  <div
    role="radiogroup"
    aria-labelledby="contact-method-title"
    aria-required="true">
    <h5 id="contact-method-title">
      Metodo di contatto preferito *
    </h5>

    <input
      type="radio"
      name="contact-good"
      id="email-contact-good"
      value="email"
      required>
    <label for="email-contact-good">Email</label>

    <input
      type="radio"
      name="contact-good"
      id="phone-contact-good"
      value="phone"
      required>
    <label for="phone-contact-good">Telefono</label>
  </div>

  <!-- Gruppo di caselle di controllo -->
  <div role="group" aria-labelledby="comm-types-title">
    <h5 id="comm-types-title">Tipi di comunicazioni</h5>
    <p id="comm-types-desc">
      Seleziona i tipi di comunicazioni che desideri ricevere
    </p>

    <input
      type="checkbox"
      id="newsletter-good"
      value="newsletter"
      aria-describedby="comm-types-desc">
    <label for="newsletter-good">Newsletter</label>
  </div>

  <button type="submit">
    Salva preferenze
  </button>
</form>

Spiegazione: I gruppi di campi correlati devono essere associati ai loro titoli tramite aria-labelledby per creare relazioni semantiche chiare, in particolare per i pulsanti radio e le caselle di controllo.

Modulo complesso con validazione in tempo reale

Esempio negativo

Crea account

<!-- ❌ Negativo: Validazione intrusiva -->
<form>
  <h4>Crea account</h4>

  <!-- Validazione immediata ad ogni digitazione -->
  <label>Nome utente</label>
  <input
    type="text"
    v-model="complexBadForm.username"
    @input="validateUsernameBad"
    :class="{ 'error': usernameBadError }">
  <!-- Errore che appare/scompare costantemente -->
  <div v-if="usernameBadError">
    
  </div>

  <!-- Nessuna associazione con aria-describedby -->
  <label>Email</label>
  <input
    type="email"
    v-model="complexBadForm.email"
    @input="validateEmailBad"
    :class="{ 'error': emailBadError }">
  <div v-if="emailBadError"></div>

  <!-- Messaggi di errore troppo brevi e non utili -->
  <!-- Nessun autocompletamento per aiutare i gestori di password -->
  <label>Password</label>
  <input
    type="password"
    v-model="complexBadForm.password"
    @input="validatePasswordBad"
    :class="{ 'error': passwordBadError }">
  <div v-if="passwordBadError"></div>

  <button type="submit">Crea account</button>
</form>

Esempio positivo

Crea account

Requisiti:
  • Almeno 3 caratteri
  • Solo lettere, numeri e underscore
Forza della password:
8+ caratteri
Maiuscola
Numero
Carattere speciale
<!-- ✅ Positivo: Validazione rispettosa con feedback -->
<form>
  <h4>Crea account</h4>

  <!-- Validazione su blur, non su input -->
  <label for="username-complex">Nome utente *</label>
  <input
    type="text"
    id="username-complex"
    v-model="complexGoodForm.username"
    @blur="validateUsernameGood"
    :class="{ error: usernameGoodError,
              success: usernameGoodValid }"
    aria-describedby="username-requirements username-feedback"
    aria-invalid="!!usernameGoodError"
    required>

  <!-- Istruzioni chiare -->
  <div id="username-requirements">
    <h6>Requisiti:</h6>
    <ul>
      <li :class="{ valid: complexGoodForm.username.length >= 3 }">
        Almeno 3 caratteri
      </li>
      <li :class="{ valid: /^[a-zA-Z0-9_]+$/.test(username) }">
        Solo lettere, numeri e underscore
      </li>
    </ul>
  </div>

  <!-- Feedback accessibile -->
  <div
    v-if="usernameGoodError"
    id="username-feedback"
    role="alert"
    aria-live="polite">
    ❌ 
  </div>
  <div
    v-else-if="usernameGoodValid"
    id="username-feedback"
    role="status"
    aria-live="polite">
    ✅ Nome utente disponibile
  </div>

  <!-- Indicatore di forza della password -->
  <label for="password-complex">Password *</label>
  <input
    type="password"
    id="password-complex"
    v-model="complexGoodForm.password"
    @input="validatePasswordGood"
    aria-describedby="password-strength"
    autocomplete="new-password"
    required>

  <div id="password-strength">
    <h6>Forza della password:</h6>
    <div class="strength-indicator">
      <div
        class="strength-bar"
        :class="`strength-${passwordStrength}`"
        :style="{ width: `${passwordStrength * 25}%` }">
      </div>
    </div>
  </div>

  <button type="submit" :disabled="!isFormValid">
    Crea account
  </button>
</form>

Spiegazione: Per i moduli complessi, la validazione in tempo reale può aiutare, ma deve essere implementata in modo non intrusivo e accessibile.