3.2.6 - Következetes segítségnyújtás

Röviden a szabványpontról

A WCAG 2.2 Success Criterion 3.3.1 (Error Identification) megköveteli, hogy amikor beviteli hibát észlelünk, a hibát azonosítsuk és szöveges formában ismertessük a felhasználóval. Ez vonatkozik minden felhasználói beviteli űrlapra, interaktív vezérlőre és adatbeviteli pontra a webes tartalomban. A cél annak biztosítása, hogy azok a felhasználók, akik hibákat ejtenek űrlapok kitöltése, vagy a bevitelek használata során, világosan tájékoztatva legyenek arról, hogy mi ment rosszul, így ki tudják javítani azt.

Cél: Segíteni a felhasználókat abban, hogy elkerüljék a zavart és frusztrációt azáltal, hogy világos és érthető hibaüzeneteket kapnak, amelyek pontosan megmondják, mit kell javítaniuk. Ez különösen fontos a kisegítő technológiákat használó felhasználók számára.

Mire vonatkozik: Minden olyan webes tartalomra, amely felhasználói adatbevitelt fogad, beleértve az űrlapokat, interaktív vezérlőket, keresőmezőket és bármilyen adatvalidációt igénylő elemet.

Kiket érint

Elsődleges felhasználók: Kognitív fogyatékossággal, tanulási nehézségekkel, gyengénlátással élő emberek vagy azok, akik képernyőolvasóra támaszkodnak, nagy hasznot húznak a világos hibaazonosításból. Segít nekik megérteni, hogy melyik bevitelt kell javítaniuk.

Másodlagos előnyök: Minden felhasználó számára előnyös a világos hibaüzenetek, javítva az általános használhatóságot és csökkentve a hibákat űrlap beküldése vagy interakció során.

Tesztelés

  1. Manuális űrlap tesztelés: Szándékosan érvénytelen adatokat írj be vagy hagyd ki a kötelező mezőket és ellenőrizd, hogy megjelennek-e a hibaüzenetek és világosan leírják-e a problémát
  2. Képernyőolvasó tesztelés: Használj képernyőolvasót (pl. NVDA, JAWS, VoiceOver) annak ellenőrzésére, hogy a hibaüzenetek programozottan kapcsolódnak a megfelelő beviteli mezőkhöz és felolvasásra kerülnek
  3. Kizárólag billentyűzetes navigáció: Küldj el hibás űrlapokat csak billentyűzet használatával és győződj meg róla, hogy a fókusz a hibaüzenetre vagy annak közelébe kerül
  4. Automatizált eszközök: Használj akadálymentességi tesztelő eszközöket, mint az axe DevTools a hiányzó vagy helytelenül kapcsolt hibaüzenetek észlelésére
  5. Kód vizsgálat: Ellenőrizd, hogy a hibaüzenetek kapcsolódnak a bevitelekhez ARIA attribútumok használatával, mint az aria-describedby vagy a hibaüzenet elhelyezésével a címkékben vagy bevitelek közelében

Jó gyakorlatok

1. Explicit hibaüzenetek beviteli mezőkhöz kapcsolva

<!-- REGISZTRÁCIÓS ŰRLAP - Teljes hibakezeléssel -->
<form class="registration-form" novalidate>
  <div class="form-section">
    <h2>Új fiók regisztrációja</h2>
    
    <!-- Általános hibák megjelenítése -->
    <div id="form-errors" class="form-errors" role="alert" aria-live="polite" style="display: none;">
      <h3>Hibák történtek az űrlap kitöltése során:</h3>
      <ul id="error-list"></ul>
    </div>
  </div>
  
  <div class="form-group">
    <label for="username">Felhasználónév *</label>
    <input 
      type="text" 
      id="username" 
      name="username" 
      required 
      minlength="3"
      maxlength="20"
      pattern="[a-zA-Z0-9]+"
      aria-describedby="username-help username-error"
      autocomplete="username"
    >
    <div id="username-help" class="field-help">
      3-20 karakter, csak betűk és számok használhatók
    </div>
    <div id="username-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="email">E-mail cím *</label>
    <input 
      type="email" 
      id="email" 
      name="email" 
      required
      aria-describedby="email-help email-error"
      autocomplete="email"
    >
    <div id="email-help" class="field-help">
      Valós e-mail címet adjon meg (pl. nev@domain.hu)
    </div>
    <div id="email-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="password">Jelszó *</label>
    <input 
      type="password" 
      id="password" 
      name="password" 
      required
      minlength="8"
      aria-describedby="password-help password-error"
      autocomplete="new-password"
    >
    <div id="password-help" class="field-help">
      Minimum 8 karakter, tartalmazzon legalább egy számot és egy nagybetűt
    </div>
    <div id="password-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="password-confirm">Jelszó megerősítése *</label>
    <input 
      type="password" 
      id="password-confirm" 
      name="password-confirm" 
      required
      aria-describedby="password-confirm-help password-confirm-error"
      autocomplete="new-password"
    >
    <div id="password-confirm-help" class="field-help">
      Írja be újra a jelszót a megerősítéshez
    </div>
    <div id="password-confirm-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="birth-date">Születési dátum *</label>
    <input 
      type="date" 
      id="birth-date" 
      name="birth-date" 
      required
      max="2006-01-01"
      aria-describedby="birth-date-help birth-date-error"
    >
    <div id="birth-date-help" class="field-help">
      Legalább 18 évesnek kell lennie a regisztrációhoz
    </div>
    <div id="birth-date-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <fieldset>
      <legend>Értesítési beállítások</legend>
      <div class="checkbox-group">
        <input type="checkbox" id="newsletter" name="newsletter" value="yes">
        <label for="newsletter">Szeretnék hírlevelet kapni</label>
      </div>
      <div class="checkbox-group">
        <input 
          type="checkbox" 
          id="terms" 
          name="terms" 
          required
          aria-describedby="terms-error"
        >
        <label for="terms">
          Elfogadom a <a href="/felhasznalasi-feltetelek" target="_blank">felhasználási feltételeket</a> *
        </label>
      </div>
      <div id="terms-error" class="error-message" role="alert" style="display: none;"></div>
    </fieldset>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-submit">Fiók létrehozása</button>
    <button type="reset" class="btn-reset">Űrlap törlése</button>
  </div>
</form>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const form = document.querySelector('.registration-form');
  const inputs = form.querySelectorAll('input[required]');
  
  // Valós idejű validáció (opcionális)
  inputs.forEach(input => {
    input.addEventListener('blur', function() {
      validateField(input);
    });
    
    // Jelszó megerősítés speciális kezelése
    if (input.id === 'password-confirm') {
      input.addEventListener('input', function() {
        validatePasswordMatch();
      });
    }
  });
  
  // Űrlap beküldési validáció
  form.addEventListener('submit', function(event) {
    event.preventDefault();
    
    let isValid = true;
    const errors = [];
    
    // Minden kötelező mező ellenőrzése
    inputs.forEach(input => {
      const fieldValid = validateField(input);
      if (!fieldValid) {
        isValid = false;
        const label = form.querySelector(`label[for="${input.id}"]`);
        const fieldName = label ? label.textContent.replace(' *', '') : input.name;
        errors.push({
          field: input.id,
          fieldName: fieldName,
          message: getErrorMessage(input)
        });
      }
    });
    
    // Jelszó egyezés ellenőrzése
    if (!validatePasswordMatch()) {
      isValid = false;
    }
    
    if (isValid) {
      // Sikeres validáció - űrlap elküldése
      submitForm();
    } else {
      // Hibák megjelenítése
      displayFormErrors(errors);
      
      // Fókusz az első hibás mezőre
      const firstErrorField = form.querySelector('input[aria-invalid="true"]');
      if (firstErrorField) {
        firstErrorField.focus();
        firstErrorField.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }
  });
  
  function validateField(input) {
    const errorElement = document.getElementById(input.id + '-error');
    let isValid = true;
    let errorMessage = '';
    
    // Kötelező mező ellenőrzése
    if (input.hasAttribute('required') && !input.value.trim()) {
      isValid = false;
      errorMessage = `A ${getFieldLabel(input)} megadása kötelező.`;
    }
    // E-mail formátum ellenőrzése
    else if (input.type === 'email' && input.value && !isValidEmail(input.value)) {
      isValid = false;
      errorMessage = 'Kérjük, adjon meg egy érvényes e-mail címet (pl. nev@domain.hu).';
    }
    // Jelszó erősség ellenőrzése
    else if (input.id === 'password' && input.value && !isStrongPassword(input.value)) {
      isValid = false;
      errorMessage = 'A jelszónak legalább 8 karakter hosszúnak kell lennie, és tartalmaznia kell legalább egy számot és egy nagybetűt.';
    }
    // Felhasználónév formátum ellenőrzése
    else if (input.id === 'username' && input.value && !isValidUsername(input.value)) {
      isValid = false;
      if (input.value.length < 3) {
        errorMessage = 'A felhasználónév legalább 3 karakter hosszú legyen.';
      } else if (input.value.length > 20) {
        errorMessage = 'A felhasználónév legfeljebb 20 karakter hosszú lehet.';
      } else {
        errorMessage = 'A felhasználónév csak betűket és számokat tartalmazhat.';
      }
    }
    // Születési dátum ellenőrzése
    else if (input.id === 'birth-date' && input.value && !isValidAge(input.value)) {
      isValid = false;
      errorMessage = 'Legalább 18 évesnek kell lennie a regisztrációhoz.';
    }
    // Checkbox ellenőrzése
    else if (input.type === 'checkbox' && input.hasAttribute('required') && !input.checked) {
      isValid = false;
      errorMessage = 'El kell fogadnia a felhasználási feltételeket a folytatáshoz.';
    }
    
    // Hibaállapot beállítása
    input.setAttribute('aria-invalid', isValid ? 'false' : 'true');
    
    if (isValid) {
      errorElement.style.display = 'none';
      errorElement.textContent = '';
      input.classList.remove('field-error');
    } else {
      errorElement.style.display = 'block';
      errorElement.textContent = errorMessage;
      input.classList.add('field-error');
    }
    
    return isValid;
  }
  
  function validatePasswordMatch() {
    const password = document.getElementById('password');
    const passwordConfirm = document.getElementById('password-confirm');
    const errorElement = document.getElementById('password-confirm-error');
    
    if (passwordConfirm.value && password.value !== passwordConfirm.value) {
      passwordConfirm.setAttribute('aria-invalid', 'true');
      passwordConfirm.classList.add('field-error');
      errorElement.style.display = 'block';
      errorElement.textContent = 'A két jelszó nem egyezik meg.';
      return false;
    } else if (passwordConfirm.value) {
      passwordConfirm.setAttribute('aria-invalid', 'false');
      passwordConfirm.classList.remove('field-error');
      errorElement.style.display = 'none';
      errorElement.textContent = '';
    }
    
    return true;
  }
  
  function displayFormErrors(errors) {
    const formErrors = document.getElementById('form-errors');
    const errorList = document.getElementById('error-list');
    
    if (errors.length > 0) {
      errorList.innerHTML = '';
      errors.forEach(error => {
        const li = document.createElement('li');
        const link = document.createElement('a');
        link.href = `#${error.field}`;
        link.textContent = `${error.fieldName}: ${error.message}`;
        link.addEventListener('click', function(e) {
          e.preventDefault();
          document.getElementById(error.field).focus();
        });
        li.appendChild(link);
        errorList.appendChild(li);
      });
      
      formErrors.style.display = 'block';
      formErrors.scrollIntoView({ behavior: 'smooth', block: 'center' });
      
      // Képernyőolvasó értesítése
      announceToScreenReader(`${errors.length} hibát találtunk az űrlapban. Kérjük, javítsa ki őket.`);
    } else {
      formErrors.style.display = 'none';
    }
  }
  
  function getFieldLabel(input) {
    const label = form.querySelector(`label[for="${input.id}"]`);
    return label ? label.textContent.replace(' *', '').toLowerCase() : input.name;
  }
  
  function getErrorMessage(input) {
    const errorElement = document.getElementById(input.id + '-error');
    return errorElement ? errorElement.textContent : 'Érvénytelen érték.';
  }
  
  function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
  
  function isStrongPassword(password) {
    return password.length >= 8 && 
           /[A-Z]/.test(password) && 
           /[0-9]/.test(password);
  }
  
  function isValidUsername(username) {
    return username.length >= 3 && 
           username.length <= 20 && 
           /^[a-zA-Z0-9]+$/.test(username);
  }
  
  function isValidAge(birthDate) {
    const today = new Date();
    const birth = new Date(birthDate);
    const age = today.getFullYear() - birth.getFullYear();
    const monthDiff = today.getMonth() - birth.getMonth();
    
    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
      return age - 1 >= 18;
    }
    return age >= 18;
  }
  
  function submitForm() {
    announceToScreenReader('Űrlap sikeresen elküldve. Kérjük, várjon...');
    
    // Itt történne a tényleges űrlap elküldése
    console.log('Űrlap elküldése...');
    
    // Siker üzenet megjelenítése
    setTimeout(() => {
      alert('Regisztráció sikeres! Ellenőrizze e-mail címét az aktiválási linkért.');
    }, 1000);
  }
  
  function announceToScreenReader(message) {
    const announcement = document.createElement('div');
    announcement.setAttribute('role', 'status');
    announcement.setAttribute('aria-live', 'polite');
    announcement.className = 'visually-hidden';
    announcement.textContent = message;
    document.body.appendChild(announcement);
    
    setTimeout(() => {
      if (announcement.parentNode) {
        document.body.removeChild(announcement);
      }
    }, 1000);
  }
});
</script>

<style>
.registration-form {
  max-width: 600px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.form-section h2 {
  margin: 0 0 1.5rem 0;
  color: #495057;
}

.form-errors {
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.form-errors h3 {
  margin: 0 0 0.5rem 0;
  color: #721c24;
  font-size: 1.1em;
}

.form-errors ul {
  margin: 0;
  padding-left: 1.2rem;
}

.form-errors li {
  margin-bottom: 0.25rem;
}

.form-errors a {
  color: #721c24;
  text-decoration: none;
}

.form-errors a:hover, .form-errors a:focus {
  text-decoration: underline;
}

.form-group {
  margin-bottom: 1.5rem;
}

.form-group label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: #495057;
}

.form-group input, .form-group select {
  width: 100%;
  padding: 0.75rem;
  border: 2px solid #ced4da;
  border-radius: 4px;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}

.form-group input:focus, .form-group select:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

.form-group input.field-error {
  border-color: #dc3545;
  background-color: #fff5f5;
}

.form-group input.field-error:focus {
  border-color: #dc3545;
  box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
}

.field-help {
  margin-top: 0.5rem;
  font-size: 0.9em;
  color: #6c757d;
  line-height: 1.4;
}

.error-message {
  margin-top: 0.5rem;
  padding: 0.5rem;
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  color: #721c24;
  font-size: 0.9em;
  font-weight: 500;
}

fieldset {
  border: 1px solid #e9ecef;
  border-radius: 4px;
  padding: 1rem;
  margin: 0;
}

legend {
  font-weight: 600;
  color: #495057;
  padding: 0 0.5rem;
}

.checkbox-group {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.checkbox-group input[type="checkbox"] {
  width: auto;
  margin-top: 0.25rem;
}

.checkbox-group label {
  margin-bottom: 0;
  font-weight: normal;
  cursor: pointer;
}

.form-actions {
  display: flex;
  gap: 1rem;
  margin-top: 2rem;
}

.btn-submit, .btn-reset {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-submit {
  background: #28a745;
  color: white;
}

.btn-submit:hover, .btn-submit:focus {
  background: #218838;
}

.btn-reset {
  background: #6c757d;
  color: white;
}

.btn-reset:hover, .btn-reset:focus {
  background: #545b62;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 768px) {
  .registration-form {
    margin: 1rem;
    padding: 1rem;
  }
  
  .form-actions {
    flex-direction: column;
  }
}
</style>

Magyarázat: A hibaüzenetek az aria-describedby attribútummal kapcsolódnak a mezőkhöz, role=”alert” attribútummal dinamikusan bejelentésre kerülnek, és az aria-invalid attribútum jelzi a hibás állapotot a kisegítő technológiák számára.

2. Fókusz kezelése hiba után és programozott hibaazonosítás

<!-- KAPCSOLATFELVÉTELI ŰRLAP - Fejlett hibakezeléssel -->
<form class="contact-form" novalidate>
  <div class="form-header">
    <h2>Kapcsolatfelvételi űrlap</h2>
    
    <!-- Hibák összesítő doboza -->
    <div id="error-summary" class="error-summary" role="alert" aria-labelledby="error-summary-title" style="display: none;">
      <h3 id="error-summary-title">Az űrlapban hibák találhatók</h3>
      <p>Kérjük, javítsa ki az alábbi hibákat a folytatáshoz:</p>
      <ul id="error-summary-list"></ul>
    </div>
  </div>
  
  <div class="form-content">
    <div class="form-group">
      <label for="contact-name">
        Teljes név 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <input 
        type="text" 
        id="contact-name" 
        name="name" 
        required 
        minlength="2"
        aria-describedby="contact-name-help contact-name-error"
        autocomplete="name"
      >
      <div id="contact-name-help" class="field-help">
        Adja meg teljes nevét (vezetéknév és keresztnév)
      </div>
      <div id="contact-name-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-email">
        E-mail cím 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <input 
        type="email" 
        id="contact-email" 
        name="email" 
        required
        aria-describedby="contact-email-help contact-email-error"
        autocomplete="email"
      >
      <div id="contact-email-help" class="field-help">
        Érvényes e-mail címet adjon meg (pl. nev@domain.hu)
      </div>
      <div id="contact-email-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-phone">Telefonszám</label>
      <input 
        type="tel" 
        id="contact-phone" 
        name="phone"
        pattern="[\+]?[0-9\s\-\(\)]+"
        aria-describedby="contact-phone-help contact-phone-error"
        autocomplete="tel"
      >
      <div id="contact-phone-help" class="field-help">
        Opcionális. Magyar vagy nemzetközi formátumban (pl. +36 1 234 5678)
      </div>
      <div id="contact-phone-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-subject">
        Tárgy 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <select id="contact-subject" name="subject" required aria-describedby="contact-subject-help contact-subject-error">
        <option value="">Válasszon témát</option>
        <option value="general">Általános kérdés</option>
        <option value="technical">Technikai probléma</option>
        <option value="billing">Számlázási kérdés</option>
        <option value="complaint">Panasz</option>
        <option value="suggestion">Javaslat</option>
      </select>
      <div id="contact-subject-help" class="field-help">
        Válassza ki a legmegfelelőbb kategóriát
      </div>
      <div id="contact-subject-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-message">
        Üzenet 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <textarea 
        id="contact-message" 
        name="message" 
        required
        minlength="10"
        maxlength="1000"
        rows="5"
        aria-describedby="contact-message-help contact-message-error contact-message-count"
      ></textarea>
      <div id="contact-message-help" class="field-help">
        Részletesen írja le kérését vagy problémáját (minimum 10, maximum 1000 karakter)
      </div>
      <div id="contact-message-count" class="character-count" aria-live="polite">
        0 / 1000 karakter
      </div>
      <div id="contact-message-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <fieldset>
        <legend>Adatvédelmi hozzájárulás</legend>
        <div class="checkbox-group">
          <input 
            type="checkbox" 
            id="privacy-consent" 
            name="privacy" 
            required
            aria-describedby="privacy-consent-error"
          >
          <label for="privacy-consent">
            Elolvastam és elfogadom az 
            <a href="/adatvedelem" target="_blank" rel="noopener">adatvédelmi tájékoztatót</a>
            <span class="required-indicator" aria-label="kötelező">*</span>
          </label>
        </div>
        <div id="privacy-consent-error" class="error-message" role="alert" style="display: none;"></div>
      </fieldset>
    </div>
    
    <div class="form-actions">
      <button type="submit" class="btn-submit">
        Üzenet küldése
        <span id="submit-status" class="visually-hidden" aria-live="polite"></span>
      </button>
      <button type="reset" class="btn-reset">Mezők törlése</button>
    </div>
  </div>
</form>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const form = document.querySelector('.contact-form');
  const messageTextarea = document.getElementById('contact-message');
  const characterCount = document.getElementById('contact-message-count');
  const submitButton = form.querySelector('.btn-submit');
  const submitStatus = document.getElementById('submit-status');
  
  let isSubmitting = false;
  
  // Karakter számláló frissítése
  messageTextarea.addEventListener('input', function() {
    const length = this.value.length;
    const maxLength = 1000;
    characterCount.textContent = `${length} / ${maxLength} karakter`;
    
    if (length > maxLength * 0.9) {
      characterCount.classList.add('warning');
    } else {
      characterCount.classList.remove('warning');
    }
  });
  
  // Valós idejű validáció
  const inputs = form.querySelectorAll('input, select, textarea');
  inputs.forEach(input => {
    input.addEventListener('blur', function() {
      validateField(this);
    });
    
    // Azonnali visszajelzés javítás után
    input.addEventListener('input', function() {
      if (this.getAttribute('aria-invalid') === 'true') {
        clearFieldError(this);
      }
    });
  });
  
  // Űrlap beküldés kezelése
  form.addEventListener('submit', function(event) {
    event.preventDefault();
    
    if (isSubmitting) return;
    
    const validationResult = validateForm();
    
    if (validationResult.isValid) {
      submitForm();
    } else {
      handleFormErrors(validationResult.errors);
    }
  });
  
  function validateForm() {
    const errors = [];
    let isValid = true;
    
    inputs.forEach(input => {
      const fieldValid = validateField(input);
      if (!fieldValid.isValid) {
        isValid = false;
        errors.push({
          field: input.id,
          fieldName: getFieldLabel(input),
          message: fieldValid.message,
          element: input
        });
      }
    });
    
    return { isValid, errors };
  }
  
  function validateField(input) {
    const errorElement = document.getElementById(input.id + '-error');
    let isValid = true;
    let message = '';
    
    // Kötelező mezők ellenőrzése
    if (input.hasAttribute('required')) {
      if (input.type === 'checkbox' && !input.checked) {
        isValid = false;
        message = 'Az adatvédelmi tájékoztató elfogadása kötelező.';
      } else if (input.type !== 'checkbox' && !input.value.trim()) {
        isValid = false;
        message = `A ${getFieldLabel(input).toLowerCase()} megadása kötelező.`;
      }
    }
    
    // Specifikus validációk
    if (isValid && input.value.trim()) {
      switch (input.type) {
        case 'email':
          if (!isValidEmail(input.value)) {
            isValid = false;
            message = 'Kérjük, adjon meg egy érvényes e-mail címet.';
          }
          break;
          
        case 'tel':
          if (!isValidPhone(input.value)) {
            isValid = false;
            message = 'Kérjük, adjon meg egy érvényes telefonszámot.';
          }
          break;
      }
      
      // Hossz ellenőrzése
      if (input.hasAttribute('minlength') && input.value.length < parseInt(input.getAttribute('minlength'))) {
        isValid = false;
        const minLength = input.getAttribute('minlength');
        message = `Legalább ${minLength} karakter szükséges.`;
      }
      
      if (input.hasAttribute('maxlength') && input.value.length > parseInt(input.getAttribute('maxlength'))) {
        isValid = false;
        const maxLength = input.getAttribute('maxlength');
        message = `Maximum ${maxLength} karakter engedélyezett.`;
      }
    }
    
    // Select mező specifikus ellenőrzés
    if (input.tagName === 'SELECT' && input.hasAttribute('required') && !input.value) {
      isValid = false;
      message = 'Kérjük, válasszon egy opciót.';
    }
    
    // Hibaállapot beállítása
    updateFieldError(input, isValid, message);
    
    return { isValid, message };
  }
  
  function updateFieldError(input, isValid, message) {
    const errorElement = document.getElementById(input.id + '-error');
    
    input.setAttribute('aria-invalid', isValid ? 'false' : 'true');
    
    if (isValid) {
      input.classList.remove('field-error');
      if (errorElement) {
        errorElement.style.display = 'none';
        errorElement.textContent = '';
      }
    } else {
      input.classList.add('field-error');
      if (errorElement) {
        errorElement.style.display = 'block';
        errorElement.textContent = message;
      }
    }
  }
  
  function clearFieldError(input) {
    updateFieldError(input, true, '');
  }
  
  function handleFormErrors(errors) {
    // Hibák összesítő megjelenítése
    displayErrorSummary(errors);
    
    // Fókusz az első hibás mezőre
    if (errors.length > 0) {
      const firstErrorField = errors[0].element;
      
      // Smooth scroll az első hibához
      firstErrorField.scrollIntoView({ 
        behavior: 'smooth', 
        block: 'center' 
      });
      
      // Fókusz beállítása kis késleltetéssel
      setTimeout(() => {
        firstErrorField.focus();
      }, 300);
      
      // Képernyőolvasó értesítése
      announceToScreenReader(`${errors.length} hibát találtunk az űrlapban. A kurzor az első hibás mezőre került.`);
    }
  }
  
  function displayErrorSummary(errors) {
    const errorSummary = document.getElementById('error-summary');
    const errorList = document.getElementById('error-summary-list');
    
    if (errors.length > 0) {
      errorList.innerHTML = '';
      
      errors.forEach(error => {
        const li = document.createElement('li');
        const link = document.createElement('a');
        
        link.href = `#${error.field}`;
        link.textContent = `${error.fieldName}: ${error.message}`;
        link.addEventListener('click', function(e) {
          e.preventDefault();
          error.element.focus();
          error.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
        });
        
        li.appendChild(link);
        errorList.appendChild(li);
      });
      
      errorSummary.style.display = 'block';
      errorSummary.scrollIntoView({ behavior: 'smooth', block: 'start' });
      
      // Fókusz az error summary-ra
      setTimeout(() => {
        errorSummary.focus();
      }, 300);
    } else {
      errorSummary.style.display = 'none';
    }
  }
  
  function submitForm() {
    isSubmitting = true;
    submitButton.disabled = true;
    submitStatus.textContent = 'Üzenet küldése folyamatban...';
    
    // Szimuláció: űrlap küldése
    setTimeout(() => {
      isSubmitting = false;
      submitButton.disabled = false;
      submitStatus.textContent = '';
      
      // Sikeres küldés
      announceToScreenReader('Üzenet sikeresen elküldve!');
      
      // Success üzenet megjelenítése
      const successMessage = document.createElement('div');
      successMessage.className = 'success-message';
      successMessage.setAttribute('role', 'alert');
      successMessage.innerHTML = `
        <h3>Üzenet sikeresen elküldve!</h3>
        <p>Köszönjük megkeresését. Munkatársunk 24 órán belül válaszol.</p>
      `;
      
      form.parentNode.insertBefore(successMessage, form);
      form.style.display = 'none';
      
      successMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });
      successMessage.focus();
      
    }, 2000);
  }
  
  function getFieldLabel(input) {
    const label = form.querySelector(`label[for="${input.id}"]`);
    if (label) {
      return label.textContent.replace(/\s*\*\s*$/, '').trim();
    }
    return input.name || input.id;
  }
  
  function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
  
  function isValidPhone(phone) {
    const phoneRegex = /^[\+]?[0-9\s\-\(\)]{6,}$/;
    return phoneRegex.test(phone);
  }
  
  function announceToScreenReader(message) {
    const announcement = document.createElement('div');
    announcement.setAttribute('role', 'status');
    announcement.setAttribute('aria-live', 'polite');
    announcement.className = 'visually-hidden';
    announcement.textContent = message;
    document.body.appendChild(announcement);
    
    setTimeout(() => {
      if (announcement.parentNode) {
        document.body.removeChild(announcement);
      }
    }, 1000);
  }
});
</script>

<style>
.contact-form {
  max-width: 700px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.form-header h2 {
  margin: 0 0 1.5rem 0;
  color: #495057;
}

.error-summary {
  background: #f8d7da;
  border: 2px solid #f5c6cb;
  border-radius: 6px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  outline: none;
}

.error-summary:focus {
  outline: 3px solid #dc3545;
  outline-offset: 2px;
}

.error-summary h3 {
  margin: 0 0 0.5rem 0;
  color: #721c24;
  font-size: 1.2em;
}

.error-summary p {
  margin: 0.5rem 0;
  color: #721c24;
}

.error-summary ul {
  margin: 0.5rem 0 0 0;
  padding-left: 1.5rem;
}

.error-summary li {
  margin-bottom: 0.5rem;
}

.error-summary a {
  color: #721c24;
  font-weight: 500;
  text-decoration: none;
}

.error-summary a:hover, .error-summary a:focus {
  text-decoration: underline;
  outline: 2px solid #721c24;
  outline-offset: 1px;
}

.required-indicator {
  color: #dc3545;
  font-weight: bold;
}

.character-count {
  font-size: 0.9em;
  color: #6c757d;
  text-align: right;
  margin-top: 0.25rem;
}

.character-count.warning {
  color: #856404;
  font-weight: bold;
}

.success-message {
  background: #d4edda;
  border: 2px solid #c3e6cb;
  border-radius: 6px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  outline: none;
}

.success-message:focus {
  outline: 3px solid #28a745;
  outline-offset: 2px;
}

.success-message h3 {
  margin: 0 0 0.5rem 0;
  color: #155724;
}

.success-message p {
  margin: 0;
  color: #155724;
}

/* További stílusok az előző példából... */
</style>

Magyarázat: A rendszer automatikusan az első hibás mezőre irányítja a fókuszt, hibák összesítőt jelenít meg, és programozott kapcsolatot biztosít az aria-invalid és aria-describedby attribútumokkal.

3. Világos és konkrét hibaüzenetek leírásokkal

<!-- BEJELENTKEZÉSI ŰRLAP - Specifikus hibaüzenetekkel -->
<form class="login-form" novalidate>
  <div class="login-header">
    <h2>Bejelentkezés</h2>
    <p>Adja meg felhasználónevét és jelszavát a belépéshez.</p>
  </div>
  
  <div class="form-group">
    <label for="login-username">Felhasználónév vagy e-mail cím</label>
    <input 
      type="text" 
      id="login-username" 
      name="username" 
      required
      autocomplete="username"
      aria-describedby="login-username-error"
    >
    <div id="login-username-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="login-password">Jelszó</label>
    <div class="password-container">
      <input 
        type="password" 
        id="login-password" 
        name="password" 
        required
        autocomplete="current-password"
        aria-describedby="login-password-error"
      >
      <button 
        type="button" 
        class="password-toggle"
        aria-label="Jelszó megjelenítése"
        onclick="togglePasswordVisibility('login-password')"
      >
        <span class="toggle-text">Mutat</span>
      </button>
    </div>
    <div id="login-password-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <div class="checkbox-group">
      <input type="checkbox" id="remember-me" name="remember">
      <label for="remember-me">Emlékezzen rám ezen az eszközön</label>
    </div>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-login">Bejelentkezés</button>
    <a href="/jelszo-visszaallitas" class="forgot-password">Elfelejtette jelszavát?</a>
  </div>
  
  <!-- Általános hiba üzenetek -->
  <div id="login-general-error" class="general-error" role="alert" style="display: none;"></div>
</form>

<!-- FIZETÉSI ŰRLAP - Komplex validációval -->
<form class="payment-form" novalidate>
  <div class="payment-header">
    <h2>Fizetési adatok</h2>
    <p>Minden mezőt pontosan töltsön ki a biztonságos fizetéshez.</p>
  </div>
  
  <div class="form-group">
    <label for="card-number">Bankkártya száma</label>
    <input 
      type="text" 
      id="card-number" 
      name="cardNumber" 
      required
      maxlength="19"
      pattern="[0-9\s]{13,19}"
      autocomplete="cc-number"
      aria-describedby="card-number-help card-number-error"
      placeholder="1234 5678 9012 3456"
    >
    <div id="card-number-help" class="field-help">
      16 számjegy, szóközökkel vagy anélkül
    </div>
    <div id="card-number-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-row">
    <div class="form-group form-group-half">
      <label for="expiry-date">Lejárati dátum</label>
      <input 
        type="text" 
        id="expiry-date" 
        name="expiryDate" 
        required
        maxlength="5"
        pattern="[0-9]{2}/[0-9]{2}"
        autocomplete="cc-exp"
        aria-describedby="expiry-date-help expiry-date-error"
        placeholder="MM/YY"
      >
      <div id="expiry-date-help" class="field-help">
        HH/ÉÉ formátumban
      </div>
      <div id="expiry-date-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group form-group-half">
      <label for="cvv">CVC/CVV kód</label>
      <input 
        type="text" 
        id="cvv" 
        name="cvv" 
        required
        maxlength="4"
        pattern="[0-9]{3,4}"
        autocomplete="cc-csc"
        aria-describedby="cvv-help cvv-error"
        placeholder="123"
      >
      <div id="cvv-help" class="field-help">
        3-4 számjegy a kártya hátulján
      </div>
      <div id="cvv-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
  </div>
  
  <div class="form-group">
    <label for="cardholder-name">Kártyatulajdonos neve</label>
    <input 
      type="text" 
      id="cardholder-name" 
      name="cardholderName" 
      required
      autocomplete="cc-name"
      aria-describedby="cardholder-name-help cardholder-name-error"
      placeholder="Ahogy a kártyán szerepel"
    >
    <div id="cardholder-name-help" class="field-help">
      Pontosan úgy, ahogy a bankkártyán szerepel
    </div>
    <div id="cardholder-name-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-pay">
      Fizetés: <span class="amount">15.990 Ft</span>
    </button>
  </div>
</form>

<script>
// Specifikus hibaüzenetek definiálása
const ErrorMessages = {
  // Bejelentkezési hibák
  login: {
    usernameRequired: 'Kérjük, adja meg felhasználónevét vagy e-mail címét.',
    usernameInvalid: 'A felhasználónév vagy e-mail cím formátuma helytelen.',
    passwordRequired: 'Kérjük, adja meg jelszavát.',
    passwordTooShort: 'A jelszó túl rövid. Legalább 6 karakter szükséges.',
    invalidCredentials: 'Hibás felhasználónév vagy jelszó. Kérjük, ellenőrizze adatait és próbálja újra.',
    accountLocked: 'A fiók átmenetileg zárolva van túl sok sikertelen bejelentkezési kísérlet miatt. Próbálja újra 15 perc múlva.',
    accountDisabled: 'Ez a fiók inaktív. Kérjük, vegye fel a kapcsolatot az ügyfélszolgálattal.',
  },
  
  // Fizetési hibák
  payment: {
    cardNumberRequired: 'Kérjük, adja meg bankkártyájának számát.',
    cardNumberInvalid: 'A bankkártya száma érvénytelen. Kérjük, ellenőrizze és adja meg újra (16 számjegy).',
    cardNumberUnsupported: 'Ez a kártyatípus nem támogatott. Kérjük, használjon Visa vagy Mastercard kártyát.',
    expiryRequired: 'Kérjük, adja meg a kártya lejárati dátumát.',
    expiryInvalid: 'A lejárati dátum formátuma helytelen. Használja a HH/ÉÉ formátumot (pl. 12/25).',
    expiryExpired: 'Ez a bankkártya már lejárt. Kérjük, használjon érvényes kártyát.',
    cvvRequired: 'Kérjük, adja meg a CVC/CVV kódot.',
    cvvInvalid: 'A CVC/CVV kód érvénytelen. 3-4 számjegyű kód szükséges.',
    cardholderRequired: 'Kérjük, adja meg a kártyatulajdonos nevét.',
    cardholderInvalid: 'A kártyatulajdonos neve csak betűket és szóközöket tartalmazhat.',
    processingError: 'Hiba történt a fizetés feldolgozása során. Kérjük, próbálja újra.'
  }
};

// Bejelentkezési űrlap kezelése
document.querySelector('.login-form').addEventListener('submit', function(event) {
  event.preventDefault();
  
  const username = document.getElementById('login-username');
  const password = document.getElementById('login-password');
  const generalError = document.getElementById('login-general-error');
  
  let hasErrors = false;
  
  // Felhasználónév validáció
  if (!username.value.trim()) {
    showFieldError('login-username', ErrorMessages.login.usernameRequired);
    hasErrors = true;
  } else if (!isValidUsernameOrEmail(username.value)) {
    showFieldError('login-username', ErrorMessages.login.usernameInvalid);
    hasErrors = true;
  } else {
    clearFieldError('login-username');
  }
  
  // Jelszó validáció
  if (!password.value) {
    showFieldError('login-password', ErrorMessages.login.passwordRequired);
    hasErrors = true;
  } else if (password.value.length < 6) {
    showFieldError('login-password', ErrorMessages.login.passwordTooShort);
    hasErrors = true;
  } else {
    clearFieldError('login-password');
  }
  
  if (hasErrors) {
    // Fókusz az első hibás mezőre
    const firstError = this.querySelector('input[aria-invalid="true"]');
    if (firstError) {
      firstError.focus();
    }
    return;
  }
  
  // Szimulált bejelentkezési kísérlet
  simulateLogin(username.value, password.value, generalError);
});

// Fizetési űrlap kezelése
document.querySelector('.payment-form').addEventListener('submit', function(event) {
  event.preventDefault();
  
  const fields = {
    cardNumber: document.getElementById('card-number'),
    expiryDate: document.getElementById('expiry-date'),
    cvv: document.getElementById('cvv'),
    cardholderName: document.getElementById('cardholder-name')
  };
  
  let hasErrors = false;
  
  // Bankkártya szám validáció
  const cardNumber = fields.cardNumber.value.replace(/\s/g, '');
  if (!cardNumber) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberRequired);
    hasErrors = true;
  } else if (!isValidCardNumber(cardNumber)) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberInvalid);
    hasErrors = true;
  } else if (!isSupportedCardType(cardNumber)) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberUnsupported);
    hasErrors = true;
  } else {
    clearFieldError('card-number');
  }
  
  // Lejárati dátum validáció
  const expiry = fields.expiryDate.value;
  if (!expiry) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryRequired);
    hasErrors = true;
  } else if (!isValidExpiryFormat(expiry)) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryInvalid);
    hasErrors = true;
  } else if (isCardExpired(expiry)) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryExpired);
    hasErrors = true;
  } else {
    clearFieldError('expiry-date');
  }
  
  // CVV validáció
  const cvv = fields.cvv.value;
  if (!cvv) {
    showFieldError('cvv', ErrorMessages.payment.cvvRequired);
    hasErrors = true;
  } else if (!isValidCVV(cvv)) {
    showFieldError('cvv', ErrorMessages.payment.cvvInvalid);
    hasErrors = true;
  } else {
    clearFieldError('cvv');
  }
  
  // Kártyatulajdonos validáció
  const cardholderName = fields.cardholderName.value.trim();
  if (!cardholderName) {
    showFieldError('cardholder-name', ErrorMessages.payment.cardholderRequired);
    hasErrors = true;
  } else if (!isValidCardholderName(cardholderName)) {
    showFieldError('cardholder-name', ErrorMessages.payment.cardholderInvalid);
    hasErrors = true;
  } else {
    clearFieldError('cardholder-name');
  }
  
  if (hasErrors) {
    const firstError = this.querySelector('input[aria-invalid="true"]');
    if (firstError) {
      firstError.focus();
    }
    return;
  }
  
  // Fizetés feldolgozása
  processPayment();
});

// Segédfüggvények
function showFieldError(fieldId, message) {
  const field = document.getElementById(fieldId);
  const errorElement = document.getElementById(fieldId + '-error');
  
  field.setAttribute('aria-invalid', 'true');
  field.classList.add('field-error');
  errorElement.style.display = 'block';
  errorElement.textContent = message;
}

function clearFieldError(fieldId) {
  const field = document.getElementById(fieldId);
  const errorElement = document.getElementById(fieldId + '-error');
  
  field.setAttribute('aria-invalid', 'false');
  field.classList.remove('field-error');
  errorElement.style.display = 'none';
  errorElement.textContent = '';
}

function isValidUsernameOrEmail(value) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const usernameRegex = /^[a-zA-Z0-9_]{3,}$/;
  return emailRegex.test(value) || usernameRegex.test(value);
}

function isValidCardNumber(cardNumber) {
  return cardNumber.length === 16 && /^[0-9]+$/.test(cardNumber);
}

function isSupportedCardType(cardNumber) {
  // Visa: 4-gyel kezdődik, Mastercard: 5-tel kezdődik
  return cardNumber.startsWith('4') || cardNumber.startsWith('5');
}

function isValidExpiryFormat(expiry) {
  return /^[0-9]{2}\/[0-9]{2}$/.test(expiry);
}

function isCardExpired(expiry) {
  const [month, year] = expiry.split('/').map(Number);
  const now = new Date();
  const currentYear = now.getFullYear() % 100;
  const currentMonth = now.getMonth() + 1;
  
  return year < currentYear || (year === currentYear && month < currentMonth);
}

function isValidCVV(cvv) {
  return /^[0-9]{3,4}$/.test(cvv);
}

function isValidCardholderName(name) {
  return /^[a-zA-ZáéíóöőúüűÁÉÍÓÖŐÚÜŰ\s]{2,}$/.test(name);
}

function simulateLogin(username, password, generalErrorElement) {
  // Szimulált bejelentkezési logika
  setTimeout(() => {
    const randomOutcome = Math.random();
    
    if (randomOutcome < 0.3) {
      // Sikertelen bejelentkezés
      generalErrorElement.textContent = ErrorMessages.login.invalidCredentials;
      generalErrorElement.style.display = 'block';
    } else if (randomOutcome < 0.4) {
      // Zárolt fiók
      generalErrorElement.textContent = ErrorMessages.login.accountLocked;
      generalErrorElement.style.display = 'block';
    } else {
      // Sikeres bejelentkezés
      alert('Sikeres bejelentkezés!');
    }
  }, 1000);
}

function processPayment() {
  alert('Fizetés feldolgozása... (szimulált)');
}

function togglePasswordVisibility(fieldId) {
  const field = document.getElementById(fieldId);
  const button = field.nextElementSibling;
  const toggleText = button.querySelector('.toggle-text');
  
  if (field.type === 'password') {
    field.type = 'text';
    button.setAttribute('aria-label', 'Jelszó elrejtése');
    toggleText.textContent = 'Rejt';
  } else {
    field.type = 'password';
    button.setAttribute('aria-label', 'Jelszó megjelenítése');
    toggleText.textContent = 'Mutat';
  }
}
</script>

<style>
.login-form, .payment-form {
  max-width: 500px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.password-container {
  position: relative;
}

.password-toggle {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  background: #f8f9fa;
  border: 1px solid #ced4da;
  border-radius: 4px;
  padding: 0.25rem 0.5rem;
  cursor: pointer;
  font-size: 0.8em;
}

.form-row {
  display: flex;
  gap: 1rem;
}

.form-group-half {
  flex: 1;
}

.general-error {
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  padding: 1rem;
  margin-top: 1rem;
  color: #721c24;
}

.btn-login, .btn-pay {
  width: 100%;
  padding: 0.75rem;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
}

.btn-pay {
  background: #28a745;
  font-size: 1.1em;
  font-weight: bold;
}

.amount {
  background: rgba(255, 255, 255, 0.2);
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
}

.forgot-password {
  display: block;
  text-align: center;
  margin-top: 1rem;
  color: #007bff;
  text-decoration: none;
}
</style>

Magyarázat: A hibaüzenetek konkrétak és konstruktívak, világosan megmondják, mi a probléma és hogyan javítható ki. Kerülik a homályos üzeneteket, mint „Érvénytelen bevitel”.

Rossz gyakorlatok

Hibaüzenetek nem kapcsolódnak a beviteli mezőkhöz

<!-- HIBÁS: A hibaüzenet nincs programozottan kapcsolva -->
<form>
  <label for="phone">Telefonszám:</label>
  <input id="phone" type="tel" required>
  <!-- HIBA: Nincs aria-describedby vagy más kapcsolat -->
  <span style="color: red;">Kérjük, adja meg telefonszámát.</span>
</form>

<!-- HIBÁS: Hibaüzenet távol a mezőtől -->
<form>
  <div class="form-group">
    <label for="email">E-mail:</label>
    <input id="email" type="email" required>
  </div>
  
  <div class="form-group">
    <label for="password">Jelszó:</label>
    <input id="password" type="password" required>
  </div>
  
  <!-- HIBA: Hibaüzenetek messze a mezőktől, nincs kapcsolat -->
  <div class="errors">
    <p>Az e-mail cím érvénytelen.</p>
    <p>A jelszó túl rövid.</p>
  </div>
</form>

Probléma: A képernyőolvasók nem tudják azonosítani, hogy melyik hibaüzenet melyik mezőhöz tartozik, mert nincs programozott kapcsolat közöttük.

Csak szín használata hibák jelzésére

<!-- HIBÁS: Csak vizuális jelzés színnel -->
<form>
  <label for="zip">Irányítószám:</label>
  <!-- HIBA: Csak a piros szegély jelzi a hibát -->
  <input id="zip" type="text" style="border: 2px solid red;" required>
  <!-- Nincs szöveges hibaüzenet -->
</form>

<!-- HIBÁS: Ikonok szöveg nélkül -->
<form>
  <label for="username">Felhasználónév:</label>
  <div class="input-container">
    <input id="username" type="text" required>
    <!-- HIBA: Csak vizuális ikon, nincs szöveges információ -->
    <span class="error-icon" style="color: red;">❌</span>
  </div>
</form>

<!-- HIBÁS: Háttérszín változás szöveg nélkül -->
<form>
  <label for="age">Életkor:</label>
  <!-- HIBA: Csak a rózsaszín háttér jelzi a hibát -->
  <input id="age" type="number" style="background-color: #ffcccc;" required>
</form>

Probléma: A színvak vagy gyengénlátó felhasználók nem érzékelik a színváltozásokat, és nincs alternatív módja a hibák azonosításának.

Nem jelenik meg hibaüzenet

<!-- HIBÁS: Néma validáció -->
<form>
  <label for="required-field">Kötelező mező:</label>
  <input id="required-field" type="text" required>
  <button type="submit">Küldés</button>
  
  <script>
    // HIBA: Csak console.log, nincs felhasználói visszajelzés
    document.querySelector('form').addEventListener('submit', function(e) {
      const input = document.getElementById('required-field');
      if (!input.value) {
        e.preventDefault();
        console.log('Hiba: Kötelező mező üres'); // Felhasználó nem látja!
      }
    });
  </script>
</form>

<!-- HIBÁS: Alert popup-ok -->
<form>
  <label for="email-bad">E-mail:</label>
  <input id="email-bad" type="email" required>
  <button type="submit">Küldés</button>
  
  <script>
    // HIBA: Alert popup nem akadálymentes és zavaró
    document.querySelector('form').addEventListener('submit', function(e) {
      const email = document.getElementById('email-bad');
      if (!email.value) {
        e.preventDefault();
        alert('E-mail cím megadása kötelező!'); // Rossz UX
      }
    });
  </script>
</form>

<!-- HIBÁS: Rejtett hibaüzenetek -->
<form>
  <label for="password-bad">Jelszó:</label>
  <input id="password-bad" type="password" required>
  <!-- HIBA: Hibaüzenet létezik, de sosem válik láthatóvá -->
  <div id="password-error" style="display: none; visibility: hidden;">
    Jelszó szükséges
  </div>
  <button type="submit">Küldés</button>
</form>

Probléma: A felhasználók nem kapnak visszajelzést a hibákról, így nem tudják, mit javítsanak ki.

Fókusz a küldés gombon marad hiba után

<!-- HIBÁS: Rossz fókusz kezelés -->
<form id="bad-focus-form">
  <div>
    <label for="name-bad">Név:</label>
    <input id="name-bad" type="text" required>
    <div id="name-bad-error" style="display: none; color: red;"></div>
  </div>
  
  <div>
    <label for="email-bad-focus">E-mail:</label>
    <input id="email-bad-focus" type="email" required>
    <div id="email-bad-focus-error" style="display: none; color: red;"></div>
  </div>
  
  <button type="submit" id="submit-bad">Küldés</button>
</form>

<script>
document.getElementById('bad-focus-form').addEventListener('submit', function(e) {
  e.preventDefault();
  
  const nameField = document.getElementById('name-bad');
  const emailField = document.getElementById('email-bad-focus');
  const submitButton = document.getElementById('submit-bad');
  
  let hasErrors = false;
  
  // Hibák ellenőrzése
  if (!nameField.value) {
    document.getElementById('name-bad-error').textContent = 'Név kötelező';
    document.getElementById('name-bad-error').style.display = 'block';
    hasErrors = true;
  }
  
  if (!emailField.value) {
    document.getElementById('email-bad-focus-error').textContent = 'E-mail kötelező';
    document.getElementById('email-bad-focus-error').style.display = 'block';
    hasErrors = true;
  }
  
  if (hasErrors) {
    // HIBA: Fókusz a küldés gombon marad
    submitButton.focus(); // Rossz! Nem segít a hibák megtalálásában
    
    // HIBA: Nincs scroll a hibákhoz
    // A felhasználó nem tudja, hol vannak a hibák
  }
});
</script>

<!-- HIBÁS: Nincs hibák összesítő -->
<form id="no-summary-form">
  <!-- Sok mező... -->
  <div>
    <label for="field1">Mező 1:</label>
    <input id="field1" type="text" required>
  </div>
  <div>
    <label for="field2">Mező 2:</label>
    <input id="field2" type="text" required>
  </div>
  <div>
    <label for="field3">Mező 3:</label>
    <input id="field3" type="text" required>
  </div>
  <div>
    <label for="field4">Mező 4:</label>
    <input id="field4" type="text" required>
  </div>
  <div>
    <label for="field5">Mező 5:</label>
    <input id="field5" type="text" required>
  </div>
  
  <button type="submit">Küldés</button>
  
  <!-- HIBA: Nincs hibák összesítő a tetején -->
  <!-- Hosszú űrlapnál a felhasználó nem látja az összes hibát -->
</form>

Probléma: A billentyűzetes felhasználók és képernyőolvasó használók nehezen találják meg és javítják ki a hibákat, ha a fókusz nem a hibás mezőre kerül.

Források

Röviden a szabványpontról

A WCAG 2.2 Success Criterion 3.2.6 (Consistent Help) megköveteli, hogy ha egy weboldal-sorozaton belül segítség mechanizmus áll rendelkezésre, akkor annak minden oldalon ugyanazon a relatív pozícióban kell megjelennie. Ez vonatkozik a kapcsolatfelvételi adatokra, humán kapcsolattartási lehetőségekre, önkiszolgáló opciókra és teljes automatizált kapcsolatfelvételi mechanizmusokra.

Cél: Biztosítani, hogy a felhasználók könnyen megtalálják a segítséget, amikor szükségük van rá, függetlenül attól, hogy a webhely melyik oldalán tartózkodnak. A konzisztens elhelyezés különösen fontos azoknak a felhasználóknak, akiknek nehézségeik vannak az új információk feldolgozásával vagy a komplex feladatok elvégzésével.

Mire vonatkozik: Minden olyan webhelyre, amely segítség mechanizmusokat biztosít több oldalon keresztül, beleértve a kapcsolatfelvételi információkat, chat funkciókat, telefonszámokat, e-mail címeket vagy önkiszolgáló súgó rendszereket.

Kiket érint

Elsődleges felhasználók: Kognitív fogyatékossággal élő emberek, akiknek nehézségeik vannak az információk megtalálásával és feldolgozásával. Azok a felhasználók, akik memória problémákkal küzdenek vagy nehezen tanulnak meg új dolgokat, különösen profitálnak a konzisztens segítség elhelyezésből.

Másodlagos előnyök: Minden felhasználó számára javítja a felhasználói élményt, különösen stresszes vagy sürgős helyzetekben, amikor gyors segítségre van szükség. Az idősebb felhasználók és a technológiai újdonságokkal kevésbé jártas emberek szintén előnyt húznak a kiszámítható segítség elhelyezésből.

Tesztelés

  1. Manuális navigációs teszt: Látogass meg több oldalt a webhelyen és ellenőrizd, hogy a segítség mechanizmusok ugyanazon a relatív pozícióban vannak-e (pl. minden oldal jobb felső sarkában)
  2. Képernyőolvasó teszt: Használj képernyőolvasót a segítség elemek megtalálásához különböző oldalakon, és győződj meg róla, hogy mindig ugyanazon a helyen találhatók
  3. Mobileszköz teszt: Ellenőrizd, hogy a segítség mechanizmusok mobil nézetben is konzisztens pozícióban vannak minden oldalon
  4. Felhasználói útvonal teszt: Kövesd végig tipikus felhasználói útvonalakat és dokumentáld, hogy minden oldalon elérhető-e segítség ugyanazon a helyen
  5. Automatizált ellenőrzés: Használj eszközöket a DOM struktúra elemzésére, hogy megbizonyosodj arról, hogy a segítség elemek konzisztens helyen vannak

Jó gyakorlatok

1. Konzisztens kapcsolatfelvételi információk minden oldalon

<!DOCTYPE html>
<html lang="hu">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Főoldal - Példa Weboldal</title>
</head>
<body>
  <header>
    <div class="logo">
      <img src="logo.png" alt="Példa Weboldal logója">
    </div>
    
    <nav role="navigation" aria-label="Főnavigáció">
      <ul class="main-nav">
        <li><a href="/fooldal">Főoldal</a></li>
        <li><a href="/termekek">Termékek</a></li>
        <li><a href="/szolgaltatasok">Szolgáltatások</a></li>
      </ul>
    </nav>
    
    <!-- KONZISZTENS segítség pozíció - header jobb szélén MINDEN oldalon -->
    <div class="help-section" role="region" aria-label="Segítség és támogatás">
      <div class="help-contact">
        <h3 class="visually-hidden">Azonnali segítség</h3>
        
        <!-- Telefonos segítség -->
        <a href="tel:+36123456789" class="help-phone" aria-label="Segítség telefonon: +36 1 234 5678">
          <svg aria-hidden="true" class="help-icon">
            <use href="#phone-icon"></use>
          </svg>
          <span>+36 1 234 5678</span>
        </a>
        
        <!-- Email segítség -->
        <a href="mailto:segitseg@peldaweboldal.hu" class="help-email" aria-label="E-mail segítség: segitseg@peldaweboldal.hu">
          <svg aria-hidden="true" class="help-icon">
            <use href="#email-icon"></use>
          </svg>
          <span>E-mail segítség</span>
        </a>
        
        <!-- Live chat -->
        <button type="button" class="help-chat" aria-label="Élő chat megnyitása" onclick="openLiveChat()">
          <svg aria-hidden="true" class="help-icon">
            <use href="#chat-icon"></use>
          </svg>
          <span>Élő chat</span>
        </button>
        
        <!-- Önkiszolgáló súgó -->
        <a href="/sugo" class="help-self-service" aria-label="Önkiszolgáló súgó megnyitása">
          <svg aria-hidden="true" class="help-icon">
            <use href="#help-icon"></use>
          </svg>
          <span>Súgó</span>
        </a>
      </div>
    </div>
  </header>
  
  <main>
    <h1>Üdvözöljük a főoldalon</h1>
    <p>Itt a főoldal tartalma...</p>
  </main>
  
  <footer>
    <!-- Kiegészítő segítség információk a footer-ben is konzisztensen -->
    <div class="footer-help" role="contentinfo">
      <h3>További segítség</h3>
      <ul class="help-links">
        <li><a href="/gyik">Gyakori kérdések</a></li>
        <li><a href="/felhasznaloi-kezikonyv">Felhasználói kézikönyv</a></li>
        <li><a href="/video-oktatoanyagok">Video oktatóanyagok</a></li>
        <li><a href="/kapcsolat">Kapcsolatfelvétel</a></li>
      </ul>
      
      <div class="business-hours">
        <h4>Telefonos segítség nyitvatartása</h4>
        <p>Hétfő-Péntek: 8:00-18:00<br>
        Szombat: 9:00-15:00<br>
        Vasárnap: zárva</p>
      </div>
    </div>
  </footer>

<!-- TERMÉKEK OLDAL - PONTOSAN UGYANAZ a segítség elhelyezés -->
<!DOCTYPE html>
<html lang="hu">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Termékek - Példa Weboldal</title>
</head>
<body>
  <header>
    <div class="logo">
      <img src="logo.png" alt="Példa Weboldal logója">
    </div>
    
    <nav role="navigation" aria-label="Főnavigáció">
      <ul class="main-nav">
        <li><a href="/fooldal">Főoldal</a></li>
        <li><a href="/termekek" aria-current="page">Termékek</a></li>
        <li><a href="/szolgaltatasok">Szolgáltatások</a></li>
      </ul>
    </nav>
    
    <!-- PONTOSAN UGYANAZ a segítség pozíció és tartalom -->
    <div class="help-section" role="region" aria-label="Segítség és támogatás">
      <div class="help-contact">
        <h3 class="visually-hidden">Azonnali segítség</h3>
        
        <a href="tel:+36123456789" class="help-phone" aria-label="Segítség telefonon: +36 1 234 5678">
          <svg aria-hidden="true" class="help-icon">
            <use href="#phone-icon"></use>
          </svg>
          <span>+36 1 234 5678</span>
        </a>
        
        <a href="mailto:segitseg@peldaweboldal.hu" class="help-email" aria-label="E-mail segítség: segitseg@peldaweboldal.hu">
          <svg aria-hidden="true" class="help-icon">
            <use href="#email-icon"></use>
          </svg>
          <span>E-mail segítség</span>
        </a>
        
        <button type="button" class="help-chat" aria-label="Élő chat megnyitása" onclick="openLiveChat()">
          <svg aria-hidden="true" class="help-icon">
            <use href="#chat-icon"></use>
          </svg>
          <span>Élő chat</span>
        </button>
        
        <a href="/sugo" class="help-self-service" aria-label="Önkiszolgáló súgó megnyitása">
          <svg aria-hidden="true" class="help-icon">
            <use href="#help-icon"></use>
          </svg>
          <span>Súgó</span>
        </a>
      </div>
    </div>
  </header>
  
  <main>
    <h1>Termékeink</h1>
    <p>Itt a termékek oldal tartalma...</p>
  </main>
  
  <!-- Footer segítség is ugyanaz -->
  <footer>
    <div class="footer-help" role="contentinfo">
      <h3>További segítség</h3>
      <ul class="help-links">
        <li><a href="/gyik">Gyakori kérdések</a></li>
        <li><a href="/felhasznaloi-kezikonyv">Felhasználói kézikönyv</a></li>
        <li><a href="/video-oktatoanyagok">Video oktatóanyagok</a></li>
        <li><a href="/kapcsolat">Kapcsolatfelvétel</a></li>
      </ul>
      
      <div class="business-hours">
        <h4>Telefonos segítség nyitvatartása</h4>
        <p>Hétfő-Péntek: 8:00-18:00<br>
        Szombat: 9:00-15:00<br>
        Vasárnap: zárva</p>
      </div>
    </div>
  </footer>

<!-- SVG ikonok -->
<svg style="display: none;">
  <defs>
    <symbol id="phone-icon" viewBox="0 0 24 24">
      <path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z"/>
    </symbol>
    
    <symbol id="email-icon" viewBox="0 0 24 24">
      <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
      <polyline points="22,6 12,13 2,6"/>
    </symbol>
    
    <symbol id="chat-icon" viewBox="0 0 24 24">
      <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
    </symbol>
    
    <symbol id="help-icon" viewBox="0 0 24 24">
      <circle cx="12" cy="12" r="10"/>
      <path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"/>
      <point cx="12" cy="17"/>
    </symbol>
  </defs>
</svg>

<script>
function openLiveChat() {
  // Chat megnyitási logika
  console.log('Chat ablak megnyitása...');
  
  // Képernyőolvasó értesítése
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.className = 'visually-hidden';
  announcement.textContent = 'Chat ablak megnyitva. Várjon egy pillanatot, amíg kapcsolódunk egy támogatási munkatárshoz.';
  document.body.appendChild(announcement);
  
  setTimeout(() => {
    if (announcement.parentNode) {
      document.body.removeChild(announcement);
    }
  }, 3000);
}
</script>

<style>
header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  background: white;
  border-bottom: 1px solid #e9ecef;
}

.logo img {
  height: 40px;
}

.main-nav {
  list-style: none;
  display: flex;
  gap: 2rem;
  margin: 0;
  padding: 0;
}

.main-nav a {
  text-decoration: none;
  color: #495057;
  font-weight: 500;
}

.main-nav a[aria-current="page"] {
  color: #007bff;
  font-weight: bold;
}

/* SEGÍTSÉG SZEKCIÓ - KONZISZTENS POZICIONÁLÁS */
.help-section {
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 8px;
  padding: 1rem;
  min-width: 250px;
}

.help-contact {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  justify-content: center;
}

.help-phone, .help-email, .help-chat, .help-self-service {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.5rem;
  text-decoration: none;
  color: #495057;
  background: white;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  font-size: 0.9em;
  transition: all 0.2s ease;
  cursor: pointer;
}

.help-phone:hover, .help-email:hover, 
.help-chat:hover, .help-self-service:hover,
.help-phone:focus, .help-email:focus, 
.help-chat:focus, .help-self-service:focus {
  background: #e9ecef;
  border-color: #007bff;
  color: #007bff;
  text-decoration: none;
}

.help-icon {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}

.footer-help {
  background: #f8f9fa;
  padding: 2rem;
  margin-top: 2rem;
}

.help-links {
  list-style: none;
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0;
  margin: 1rem 0;
}

.help-links a {
  color: #007bff;
  text-decoration: none;
}

.help-links a:hover, .help-links a:focus {
  text-decoration: underline;
}

.business-hours {
  margin-top: 1rem;
  padding: 1rem;
  background: white;
  border-radius: 4px;
  border: 1px solid #dee2e6;
}

.business-hours h4 {
  margin: 0 0 0.5rem 0;
  color: #495057;
}

.business-hours p {
  margin: 0;
  font-size: 0.9em;
  line-height: 1.4;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Mobil reszponzivitás */
@media (max-width: 768px) {
  header {
    flex-direction: column;
    gap: 1rem;
  }
  
  .help-section {
    width: 100%;
    min-width: auto;
  }
  
  .help-contact {
    justify-content: space-around;
  }
  
  .help-phone span, .help-email span, 
  .help-self-service span {
    display: none;
  }
  
  .help-links {
    flex-direction: column;
    gap: 0.5rem;
  }
}
</style>
</body>
</html>

Magyarázat: A segítség mechanizmusok minden oldalon pontosan ugyanazon a helyen találhatók: a header jobb szélén és a footer-ben, biztosítva, hogy a felhasználók mindig tudják, hol találnak segítséget.

2. Konzisztens mobil segítség gomb

<!-- MOBIL NÉZET - Lebegő segítség gomb minden oldalon ugyanott -->
<div class="mobile-help-fab" role="region" aria-label="Mobil segítség menü">
  <button 
    type="button" 
    class="help-fab-button" 
    aria-expanded="false"
    aria-controls="mobile-help-menu"
    aria-label="Segítség menü megnyitása"
    onclick="toggleMobileHelp()"
  >
    <svg class="help-fab-icon" aria-hidden="true">
      <use href="#help-icon"></use>
    </svg>
    <span class="help-fab-text">Segítség</span>
  </button>
  
  <div 
    id="mobile-help-menu" 
    class="help-fab-menu" 
    role="menu"
    aria-hidden="true"
  >
    <div class="help-menu-header">
      <h3>Hogyan segíthetünk?</h3>
      <button 
        type="button" 
        class="help-menu-close"
        aria-label="Segítség menü bezárása"
        onclick="toggleMobileHelp()"
      >
        <svg aria-hidden="true">
          <use href="#close-icon"></use>
        </svg>
      </button>
    </div>
    
    <ul class="help-menu-options" role="none">
      <li role="none">
        <a 
          href="tel:+36123456789" 
          class="help-menu-item"
          role="menuitem"
          aria-label="Telefonhívás: +36 1 234 5678"
        >
          <svg class="help-item-icon" aria-hidden="true">
            <use href="#phone-icon"></use>
          </svg>
          <div class="help-item-content">
            <span class="help-item-title">Hívjon minket</span>
            <span class="help-item-desc">+36 1 234 5678</span>
          </div>
        </a>
      </li>
      
      <li role="none">
        <button 
          type="button"
          class="help-menu-item"
          role="menuitem"
          aria-label="Élő chat indítása"
          onclick="openMobileLiveChat()"
        >
          <svg class="help-item-icon" aria-hidden="true">
            <use href="#chat-icon"></use>
          </svg>
          <div class="help-item-content">
            <span class="help-item-title">Élő chat</span>
            <span class="help-item-desc">Azonnali segítség</span>
          </div>
        </button>
      </li>
      
      <li role="none">
        <a 
          href="mailto:segitseg@peldaweboldal.hu" 
          class="help-menu-item"
          role="menuitem"
          aria-label="E-mail írása segítségkéréshez"
        >
          <svg class="help-item-icon" aria-hidden="true">
            <use href="#email-icon"></use>
          </svg>
          <div class="help-item-content">
            <span class="help-item-title">E-mail írása</span>
            <span class="help-item-desc">segitseg@peldaweboldal.hu</span>
          </div>
        </a>
      </li>
      
      <li role="none">
        <a 
          href="/sugo" 
          class="help-menu-item"
          role="menuitem"
          aria-label="Önkiszolgáló súgó megnyitása"
        >
          <svg class="help-item-icon" aria-hidden="true">
            <use href="#help-icon"></use>
          </svg>
          <div class="help-item-content">
            <span class="help-item-title">Súgó és GYIK</span>
            <span class="help-item-desc">Önkiszolgáló megoldások</span>
          </div>
        </a>
      </li>
      
      <li role="none">
        <a 
          href="/callback-keres" 
          class="help-menu-item"
          role="menuitem"
          aria-label="Visszahívás kérése"
        >
          <svg class="help-item-icon" aria-hidden="true">
            <use href="#callback-icon"></use>
          </svg>
          <div class="help-item-content">
            <span class="help-item-title">Visszahívás</span>
            <span class="help-item-desc">Mi hívjuk Önt</span>
          </div>
        </a>
      </li>
    </ul>
    
    <div class="help-menu-footer">
      <p class="help-availability">
        <strong>Telefonos elérhetőség:</strong><br>
        H-P: 8:00-18:00, Szo: 9:00-15:00
      </p>
    </div>
  </div>
</div>

<!-- Háttér overlay a menü mögött -->
<div id="help-menu-overlay" class="help-menu-overlay" onclick="toggleMobileHelp()"></div>

<script>
let helpMenuOpen = false;

function toggleMobileHelp() {
  const button = document.querySelector('.help-fab-button');
  const menu = document.getElementById('mobile-help-menu');
  const overlay = document.getElementById('help-menu-overlay');
  
  helpMenuOpen = !helpMenuOpen;
  
  // Állapot frissítése
  button.setAttribute('aria-expanded', helpMenuOpen.toString());
  menu.setAttribute('aria-hidden', (!helpMenuOpen).toString());
  
  // CSS osztályok kezelése
  if (helpMenuOpen) {
    menu.classList.add('help-menu-open');
    overlay.classList.add('overlay-visible');
    document.body.style.overflow = 'hidden'; // Scroll letiltása
    
    // Első menüelemre fókusz
    setTimeout(() => {
      const firstMenuItem = menu.querySelector('.help-menu-item');
      if (firstMenuItem) firstMenuItem.focus();
    }, 100);
  } else {
    menu.classList.remove('help-menu-open');
    overlay.classList.remove('overlay-visible');
    document.body.style.overflow = '';
    
    // Fókusz visszaadása a gombra
    button.focus();
  }
  
  // Képernyőolvasó értesítése
  announceToScreenReader(
    helpMenuOpen ? 'Segítség menü megnyitva' : 'Segítség menü bezárva'
  );
}

function openMobileLiveChat() {
  toggleMobileHelp(); // Menü bezárása
  
  console.log('Mobil chat megnyitása...');
  
  announceToScreenReader('Chat ablak megnyitása. Kérjük, várjon.');
  
  // Chat logika itt...
}

function announceToScreenReader(message) {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.className = 'visually-hidden';
  announcement.textContent = message;
  document.body.appendChild(announcement);
  
  setTimeout(() => {
    if (announcement.parentNode) {
      document.body.removeChild(announcement);
    }
  }, 1000);
}

// Escape billentyű kezelése
document.addEventListener('keydown', function(e) {
  if (e.key === 'Escape' && helpMenuOpen) {
    toggleMobileHelp();
  }
});

// Mobil gomb megjelenítése/elrejtése képernyő méret alapján
function handleMobileHelpVisibility() {
  const helpFab = document.querySelector('.mobile-help-fab');
  const desktopHelp = document.querySelector('.help-section');
  
  if (window.innerWidth <= 768) {
    helpFab.style.display = 'block';
    if (desktopHelp) desktopHelp.style.display = 'none';
  } else {
    helpFab.style.display = 'none';
    if (desktopHelp) desktopHelp.style.display = 'block';
    
    // Ha nyitva volt mobil menü, bezárjuk
    if (helpMenuOpen) {
      toggleMobileHelp();
    }
  }
}

// Eseményfigyelők
window.addEventListener('resize', handleMobileHelpVisibility);
document.addEventListener('DOMContentLoaded', handleMobileHelpVisibility);
</script>

<style>
/* MOBIL SEGÍTSÉG LEBEGŐ GOMB */
.mobile-help-fab {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 1000;
  display: none; /* Alapértelmezetten rejtett, JS-sel szabályozzuk */
}

.help-fab-button {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: #007bff;
  color: white;
  border: none;
  box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
}

.help-fab-button:hover, .help-fab-button:focus {
  background: #0056b3;
  transform: scale(1.05);
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.help-fab-icon {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}

.help-fab-text {
  font-size: 0.7em;
  font-weight: bold;
  margin-top: 2px;
}

/* SEGÍTSÉG MENÜ */
.help-fab-menu {
  position: fixed;
  bottom: 90px;
  right: 20px;
  width: 320px;
  max-width: calc(100vw - 40px);
  background: white;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  transform: translateY(20px) scale(0.9);
  opacity: 0;
  visibility: hidden;
  transition: all 0.3s ease;
  max-height: 70vh;
  overflow-y: auto;
}

.help-fab-menu.help-menu-open {
  transform: translateY(0) scale(1);
  opacity: 1;
  visibility: visible;
}

.help-menu-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  border-bottom: 1px solid #e9ecef;
}

.help-menu-header h3 {
  margin: 0;
  font-size: 1.1em;
  color: #495057;
}

.help-menu-close {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.25rem;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.help-menu-close:hover, .help-menu-close:focus {
  background: #f8f9fa;
}

.help-menu-close svg {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  stroke-width: 2;
}

.help-menu-options {
  list-style: none;
  padding: 0;
  margin: 0;
}

.help-menu-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  text-decoration: none;
  color: #495057;
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.2s ease;
  border-bottom: 1px solid #f8f9fa;
}

.help-menu-item:hover, .help-menu-item:focus {
  background: #f8f9fa;
  text-decoration: none;
  outline: 2px solid #007bff;
  outline-offset: -2px;
}

.help-item-icon {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: #007bff;
  stroke-width: 2;
  flex-shrink: 0;
}

.help-item-content {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.help-item-title {
  font-weight: 600;
  color: #495057;
}

.help-item-desc {
  font-size: 0.9em;
  color: #6c757d;
}

.help-menu-footer {
  padding: 1rem;
  background: #f8f9fa;
  border-top: 1px solid #e9ecef;
  border-radius: 0 0 12px 12px;
}

.help-availability {
  margin: 0;
  font-size: 0.9em;
  color: #6c757d;
  text-align: center;
}

/* OVERLAY */
.help-menu-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.3);
  z-index: 999;
  opacity: 0;
  visibility: hidden;
  transition: all 0.3s ease;
}

.help-menu-overlay.overlay-visible {
  opacity: 1;
  visibility: visible;
}

/* Reszponzív módosítások */
@media (max-width: 480px) {
  .help-fab-menu {
    bottom: 90px;
    left: 20px;
    right: 20px;
    width: auto;
  }
}

@media (min-width: 769px) {
  .mobile-help-fab {
    display: none !important;
  }
}
</style>

Magyarázat: Mobil eszközökön a segítség mindig ugyanazon a helyen érhető el egy lebegő gomb formájában, amely konzisztens menüt nyit meg minden oldalon.

3. Kontextusspecifikus segítség konzisztens elhelyezéssel

<!-- ŰRLAP OLDAL - Kontextusspecifikus segítség -->
<form class="contact-form">
  <div class="form-section">
    <h2>Kapcsolatfelvételi űrlap</h2>
    
    <!-- KONZISZTENS segítség pozíció minden űrlapnál - jobb felső sarokban -->
    <div class="form-help-section" role="region" aria-label="Űrlap kitöltési segítség">
      <details class="form-help-details">
        <summary class="form-help-toggle" aria-expanded="false">
          <svg class="help-icon" aria-hidden="true">
            <use href="#help-icon"></use>
          </svg>
          Segítség az űrlap kitöltéséhez
        </summary>
        
        <div class="form-help-content">
          <h3>Hogyan töltsem ki az űrlapot?</h3>
          <ul>
            <li>A * jelölt mezők kitöltése kötelező</li>
            <li>Az e-mail címet érvényes formátumban adja meg</li>
            <li>A telefonszám lehet hazai vagy nemzetközi formátumban</li>
            <li>Az üzenet mezőben részletesen írja le kérését</li>
          </ul>
          
          <div class="form-help-contact">
            <h4>További segítség szükséges?</h4>
            <p>Hívja segélyvonalunkat: <a href="tel:+36123456789">+36 1 234 5678</a></p>
            <p>Vagy írjon nekünk: <a href="mailto:urlapsegitech@peldaweboldal.hu">urlapsegitech@peldaweboldal.hu</a></p>
          </div>
        </div>
      </details>
    </div>
  </div>
  
  <div class="form-group">
    <label for="contact-name">Teljes név *</label>
    <input type="text" id="contact-name" name="name" required aria-describedby="name-help">
    <div id="name-help" class="field-help">
      Adja meg vezeték- és keresztnevét
    </div>
  </div>
  
  <div class="form-group">
    <label for="contact-email">E-mail cím *</label>
    <input type="email" id="contact-email" name="email" required aria-describedby="email-help">
    <div id="email-help" class="field-help">
      Például: nev@domain.hu
    </div>
  </div>
  
  <div class="form-group">
    <label for="contact-phone">Telefonszám</label>
    <input type="tel" id="contact-phone" name="phone" aria-describedby="phone-help">
    <div id="phone-help" class="field-help">
      Opcionális. Például: +36 1 234 5678 vagy 06 1 234 5678
    </div>
  </div>
  
  <div class="form-group">
    <label for="contact-subject">Tárgy *</label>
    <select id="contact-subject" name="subject" required aria-describedby="subject-help">
      <option value="">Válasszon témát</option>
      <option value="info">Általános információ</option>
      <option value="support">Technikai támogatás</option>
      <option value="billing">Számlázási kérdés</option>
      <option value="other">Egyéb</option>
    </select>
    <div id="subject-help" class="field-help">
      Válassza ki a legmegfelelőbb kategóriát
    </div>
  </div>
  
  <div class="form-group">
    <label for="contact-message">Üzenet *</label>
    <textarea id="contact-message" name="message" rows="5" required aria-describedby="message-help"></textarea>
    <div id="message-help" class="field-help">
      Részletesen írja le kérését vagy problémáját (minimum 10 karakter)
    </div>
  </div>
  
  <div class="form-group">
    <div class="checkbox-group">
      <input type="checkbox" id="privacy-consent" name="privacy" required>
      <label for="privacy-consent">
        Elfogadom az <a href="/adatvedelem" target="_blank">adatvédelmi tájékoztatót</a> *
      </label>
    </div>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-submit">Üzenet küldése</button>
    <button type="reset" class="btn-reset">Űrlap törlése</button>
  </div>
</form>

<!-- REGISZTRÁCIÓ OLDAL - UGYANEZ a segítség pozíció -->
<form class="registration-form">
  <div class="form-section">
    <h2>Új fiók regisztrációja</h2>
    
    <!-- PONTOSAN UGYANAZ a segítség pozíció -->
    <div class="form-help-section" role="region" aria-label="Regisztrációs segítség">
      <details class="form-help-details">
        <summary class="form-help-toggle" aria-expanded="false">
          <svg class="help-icon" aria-hidden="true">
            <use href="#help-icon"></use>
          </svg>
          Segítség a regisztrációhoz
        </summary>
        
        <div class="form-help-content">
          <h3>Regisztrációs útmutató</h3>
          <ul>
            <li>Válasszon erős jelszót (min. 8 karakter, szám és betű)</li>
            <li>A felhasználónév csak betűket és számokat tartalmazhat</li>
            <li>Valós e-mail címet adjon meg az aktiváláshoz</li>
            <li>A születési dátum a korhatár ellenőrzéshez szükséges</li>
          </ul>
          
          <div class="form-help-contact">
            <h4>Problémák a regisztrációval?</h4>
            <p>Segítség: <a href="tel:+36123456789">+36 1 234 5678</a></p>
            <p>E-mail: <a href="mailto:regisztracio@peldaweboldal.hu">regisztracio@peldaweboldal.hu</a></p>
          </div>
        </div>
      </details>
    </div>
  </div>
  
  <!-- Regisztrációs mezők... -->
  <div class="form-group">
    <label for="reg-username">Felhasználónév *</label>
    <input type="text" id="reg-username" name="username" required aria-describedby="username-help">
    <div id="username-help" class="field-help">
      3-20 karakter, csak betűk és számok
    </div>
  </div>
  
  <!-- További mezők... -->
  
  <div class="form-actions">
    <button type="submit" class="btn-submit">Fiók létrehozása</button>
    <button type="reset" class="btn-reset">Mezők törlése</button>
  </div>
</form>

<style>
.form-section {
  position: relative;
  background: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 8px;
  padding: 1.5rem;
  margin-bottom: 2rem;
}

.form-section h2 {
  margin: 0 0 1rem 0;
  color: #495057;
}

/* KONZISZTENS SEGÍTSÉG POZICIONÁLÁS */
.form-help-section {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 10;
}

.form-help-details {
  position: relative;
}

.form-help-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.9em;
  font-weight: 500;
  transition: all 0.2s ease;
  list-style: none;
}

.form-help-toggle:hover, .form-help-toggle:focus {
  background: #0056b3;
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.form-help-toggle::marker {
  display: none;
}

.help-icon {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}

.form-help-content {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 0.5rem;
  width: 320px;
  max-width: 90vw;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  z-index: 1000;
}

.form-help-content h3 {
  margin: 0 0 1rem 0;
  font-size: 1.1em;
  color: #495057;
}

.form-help-content ul {
  margin: 0 0 1rem 0;
  padding-left: 1.2rem;
}

.form-help-content li {
  margin-bottom: 0.5rem;
  color: #6c757d;
  line-height: 1.4;
}

.form-help-contact {
  border-top: 1px solid #e9ecef;
  padding-top: 1rem;
  margin-top: 1rem;
}

.form-help-contact h4 {
  margin: 0 0 0.5rem 0;
  font-size: 1em;
  color: #495057;
}

.form-help-contact p {
  margin: 0.25rem 0;
  font-size: 0.9em;
}

.form-help-contact a {
  color: #007bff;
  text-decoration: none;
}

.form-help-contact a:hover, .form-help-contact a:focus {
  text-decoration: underline;
}

/* Űrlap stílusok */
.form-group {
  margin-bottom: 1.5rem;
}

.form-group label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: #495057;
}

.form-group input, .form-group select, .form-group textarea {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}

.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

.field-help {
  margin-top: 0.5rem;
  font-size: 0.9em;
  color: #6c757d;
  line-height: 1.4;
}

.checkbox-group {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

.checkbox-group input[type="checkbox"] {
  width: auto;
  margin-top: 0.25rem;
}

.checkbox-group label {
  margin-bottom: 0;
  font-weight: normal;
  cursor: pointer;
}

.form-actions {
  display: flex;
  gap: 1rem;
  margin-top: 2rem;
}

.btn-submit, .btn-reset {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-submit {
  background: #28a745;
  color: white;
}

.btn-submit:hover, .btn-submit:focus {
  background: #218838;
}

.btn-reset {
  background: #6c757d;
  color: white;
}

.btn-reset:hover, .btn-reset:focus {
  background: #545b62;
}

/* Reszponzív */
@media (max-width: 768px) {
  .form-help-section {
    position: static;
    margin-bottom: 1rem;
  }
  
  .form-help-content {
    position: static;
    width: 100%;
    margin-top: 0.5rem;
  }
  
  .form-actions {
    flex-direction: column;
  }
}
</style>

Magyarázat: Minden űrlapnál a segítség konzisztensen a jobb felső sarokban jelenik meg, kontextusspecifikus információkkal és egységes kapcsolatfelvételi opciókkal.

Rossz gyakorlatok

Eltérő segítség pozíciók különböző oldalakon

<!-- FŐOLDAL - Segítség a header-ben -->
<header>
  <nav>...</nav>
  <div class="help-header">
    <a href="/segitseg">Segítség</a>
  </div>
</header>

<!-- TERMÉKEK OLDAL - HIBÁS: Segítség a sidebar-ban -->
<aside class="sidebar">
  <div class="help-sidebar">
    <h3>Segítség</h3>
    <a href="/segitseg">Segítség</a>
  </div>
</aside>

<!-- KAPCSOLAT OLDAL - HIBÁS: Segítség a footer-ben -->
<footer>
  <div class="help-footer">
    <a href="/segitseg">Segítség</a>
  </div>
</footer>

<!-- ŰRLAP OLDAL - HIBÁS: Segítség a tartalom közepén -->
<main>
  <h1>Kapcsolatfelvétel</h1>
  <div class="help-middle">
    <p>Segítségre van szüksége? <a href="/segitseg">Kattintson ide</a></p>
  </div>
  <form>...</form>
</main>

Probléma: A felhasználók nem tudják megjegyezni, hol találják a segítséget, mert minden oldalon más helyen van.

Hiányzó segítség mechanizmusok egyes oldalakon

<!-- FŐOLDAL - Van segítség -->
<div class="help-section">
  <a href="tel:+36123456789">Hívjon minket</a>
  <a href="mailto:help@example.com">E-mail</a>
</div>

<!-- FIZETÉS OLDAL - HIBÁS: Nincs segítség, pedig itt lenne a legfontosabb -->
<form class="payment-form">
  <h1>Fizetési adatok</h1>
  <!-- Nincs segítség elérhetőség! -->
  <input type="text" placeholder="Kártyaszám">
  <input type="text" placeholder="Lejárati dátum">
  <button>Fizetés</button>
</form>

<!-- HIBA OLDAL - HIBÁS: Nincs segítség -->
<div class="error-page">
  <h1>Hiba történt</h1>
  <p>Valami rosszul sült el.</p>
  <!-- Nincs segítség elérhetőség! -->
  <a href="/">Vissza a főoldalra</a>
</div>

Probléma: A kritikus oldalakon (fizetés, hibák) nincs elérhető segítség, pedig ezekben a helyzetekben a leginkább szükséges lenne.

Inkonzisztens segítség tartalom és formátum

<!-- FŐOLDAL - Részletes segítség -->
<div class="help-detailed">
  <h3>Segítség és támogatás</h3>
  <p>Telefonos segítség: +36 1 234 5678</p>
  <p>E-mail: help@example.com</p>
  <p>Nyitvatartás: H-P 8-18</p>
</div>

<!-- TERMÉKEK OLDAL - HIBÁS: Minimalista segítség -->
<div class="help-minimal">
  <a href="tel:+36123456789">📞</a> <!-- Csak ikon -->
</div>

<!-- SZOLGÁLTATÁSOK OLDAL - HIBÁS: Más formátum -->
<div class="help-different">
  <button onclick="openChat()">Chat</button> <!-- Csak chat -->
</div>

<!-- KAPCSOLAT OLDAL - HIBÁS: Redundáns segítség -->
<div class="help-redundant">
  <p>Itt már úgyis kapcsolatot vehet fel velünk.</p> <!-- Nem hasznos -->
</div>

Probléma: A különböző formátumú és részletezettségű segítség zavaró és nem biztosít konzisztens felhasználói élményt.

Források

Iratkozz fel hírlevelünkre!

Amennyiben szeretnél első kézből értesülni az új bejegyzésekről, iratkozz fel hírlevelünkre!

Ez a weboldal sütiket használ a böngészési élmény javítása és a webhely megfelelő működésének biztosítása érdekében. A webhely használatának folytatásával elismeri és elfogadja a sütik használatát.

Összes elfogadása Csak a szükségesek elfogadása