4.1.2 - Név, szerep, érték

Röviden a szabványpontról

A WCAG 2.2 Success Criterion 4.1.2 megköveteli, hogy minden felhasználói felület komponens esetében a név, szerep és érték programozottan meghatározható legyen a kisegítő technológiák számára. Ez azt jelenti, hogy a képernyőolvasók és más kisegítő eszközök képesek azonosítani, hogy mi az elem (szerep), hogyan hívják vagy címkézik (név), és mi a jelenlegi állapota vagy értéke (érték).

Cél: Biztosítani, hogy a kisegítő technológiákra támaszkodó felhasználók hatékonyan érzékelhessék és használhassák a tartalmat, valamint megértsék a felhasználói felület komponensek aktuális állapotát. Ez alapvető követelmény minden interaktív elem hozzáférhetőségéhez.

Mire vonatkozik: Minden interaktív felhasználói felület elemre, mint például gombok, űrlapmezők, csúszkák, jelölőnégyzetek és egyedi vezérlők. A kritérium biztosítja, hogy ezek az elemek teljes mértékben hozzáférhetők legyenek.

Kiket érint

Elsődleges felhasználók: Képernyőolvasót vagy más kisegítő technológiákat használó emberek, akik a programozottan elérhető információkra támaszkodnak a webes tartalmak navigálásához és használatához. Pontos név, szerep és érték nélkül ezek a felhasználók nem tudják megérteni vagy irányítani a felület elemeket.

Másodlagos előnyök: A világos programozási információk javítják a kompatibilitást az automatizált tesztelő eszközökkel és hangvezérlő rendszerekkel, ami hasznos a mozgásszervi vagy kognitív fogyatékossággal élő felhasználók számára is.

Tesztelés

  1. Képernyőolvasó használata: Navigálj a felhasználói felület elemeken és győződj meg róla, hogy a képernyőolvasó helyesen mondja be az elem nevét, szerepét és aktuális értékét
  2. ARIA attribútumok vizsgálata: Ellenőrizd, hogy a szerepek (role), nevek (aria-label, aria-labelledby vagy natív címkézés útján) és állapotok/értékek (pl. aria-checked, aria-valuenow) helyesen vannak-e alkalmazva
  3. Böngésző akadálymentességi eszközök használata: Az eszközök, mint a Chrome DevTools Accessibility panel vagy Firefox Accessibility Inspector megmutatják az elemek hozzáférhető nevét, szerepét és értékét
  4. Automatizált akadálymentességi vizsgálatok futtatása: Használj axe DevTools vagy axe Linter eszközöket a hiányzó vagy helytelen ARIA szerepek, nevek vagy értékek észlelésére
  5. Billentyűzetes tesztelés: Biztosítsd, hogy az interaktív elemek programozottan jelzik állapotváltozásaikat billentyűzetes interakció során

Jó gyakorlatok

1. Natív HTML elemek használata

<!-- NATÍV HTML GOMBOK ÉS ŰRLAPELEMEK -->
<form class="contact-form">
  <div class="form-header">
    <h2>Kapcsolatfelvételi űrlap</h2>
    <p>Töltse ki az alábbi mezőket, hogy felvegyük Önnel a kapcsolatot</p>
  </div>
  
  <!-- Natív gomb - automatikusan rendelkezik "button" szereppel és "Küldés" névvel -->
  <div class="form-group">
    <button type="submit" id="submitBtn" class="btn-primary">
      Küldés
    </button>
  </div>
  
  <!-- Natív szöveges beviteli mező explicit címkével -->
  <div class="form-group">
    <label for="nev">Teljes név</label>
    <input 
      type="text" 
      id="nev" 
      name="nev" 
      required
      aria-describedby="nev-help"
    >
    <span id="nev-help" class="field-help">
      Adja meg teljes nevét (vezetéknév és keresztnév)
    </span>
  </div>
  
  <!-- Natív email mező -->
  <div class="form-group">
    <label for="email">E-mail cím</label>
    <input 
      type="email" 
      id="email" 
      name="email" 
      required
      placeholder="pelda@email.hu"
    >
    <!-- A type="email" automatikusan közli a szerepet -->
  </div>
  
  <!-- Natív jelölőnégyzet -->
  <div class="form-group">
    <label>
      <input 
        type="checkbox" 
        name="hirlevel" 
        value="feliratkozas"
      >
      Szeretnék hírlevelet kapni
    </label>
    <!-- A checkbox automatikusan rendelkezik szereppel és állapottal -->
  </div>
  
  <!-- Natív választó lista -->
  <div class="form-group">
    <label for="orszag">Ország</label>
    <select id="orszag" name="orszag" required>
      <option value="">Válasszon országot</option>
      <option value="hu">Magyarország</option>
      <option value="at">Ausztria</option>
      <option value="de">Németország</option>
    </select>
    <!-- A select elem automatikusan közli szerepét és aktuális értékét -->
  </div>
  
  <!-- Natív rádiógombok -->
  <fieldset>
    <legend>Preferált kapcsolatfelvételi mód</legend>
    <div class="radio-group">
      <label>
        <input type="radio" name="kapcsolat" value="email" checked>
        E-mail
      </label>
      <label>
        <input type="radio" name="kapcsolat" value="telefon">
        Telefon
      </label>
      <label>
        <input type="radio" name="kapcsolat" value="posta">
        Postai levél
      </label>
    </div>
  </fieldset>
</form>

<script>
  // A natív elemek automatikusan kezelik az állapotváltozásokat
  document.getElementById('submitBtn').addEventListener('click', function(e) {
    e.preventDefault();
    
    // A gomb disabled állapota automatikusan közlésre kerül
    this.disabled = true;
    this.textContent = 'Küldés folyamatban...';
    
    // Szimulált küldés
    setTimeout(() => {
      this.disabled = false;
      this.textContent = 'Elküldve!';
      
      // Képernyőolvasó értesítése
      const statusMsg = document.createElement('div');
      statusMsg.setAttribute('role', 'status');
      statusMsg.setAttribute('aria-live', 'polite');
      statusMsg.textContent = 'Az űrlap sikeresen elküldve.';
      document.body.appendChild(statusMsg);
    }, 2000);
  });
</script>

2. Egyedi kapcsoló ARIA attribútumokkal

<!-- EGYEDI KAPCSOLÓ VEZÉRLŐK -->
<div class="settings-panel">
  <h2>Beállítások</h2>
  
  <!-- Egyedi kapcsoló teljes ARIA támogatással -->
  <div class="setting-item">
    <span id="repules-mod-cimke">Repülési mód</span>
    <div 
      role="switch" 
      aria-checked="false" 
      aria-labelledby="repules-mod-cimke"
      tabindex="0" 
      class="custom-switch"
      id="repules-mod-kapcsolo"
    >
      <span class="switch-handle"></span>
    </div>
    <span id="repules-mod-leiras" class="setting-description">
      Kikapcsolja az összes vezeték nélküli kapcsolatot
    </span>
  </div>
  
  <!-- Egyedi jelölőnégyzet ARIA-val -->
  <div class="setting-item">
    <div 
      role="checkbox" 
      aria-checked="false" 
      aria-label="Értesítések engedélyezése"
      aria-describedby="ertesites-leiras"
      tabindex="0" 
      class="custom-checkbox"
      id="ertesites-checkbox"
    >
      <svg class="checkmark" aria-hidden="true" width="18" height="18">
        <path d="M6.61 11.89L3.5 8.78 2.44 9.84 6.61 14l8.95-8.95L14.5 4z" fill="currentColor"/>
      </svg>
    </div>
    <label for="ertesites-checkbox">Értesítések engedélyezése</label>
    <span id="ertesites-leiras" class="setting-description">
      Push értesítések fogadása új üzenetekről
    </span>
  </div>
  
  <!-- Egyedi többállapotú gomb -->
  <div class="setting-item">
    <span id="tema-cimke">Téma választás</span>
    <div 
      role="button" 
      aria-pressed="false"
      aria-labelledby="tema-cimke"
      aria-describedby="tema-allapot"
      tabindex="0" 
      class="theme-toggle"
      id="tema-valto"
    >
      <svg aria-hidden="true" width="24" height="24">
        <circle cx="12" cy="12" r="5"/>
        <line x1="12" y1="1" x2="12" y2="3"/>
        <line x1="12" y1="21" x2="12" y2="23"/>
        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
      </svg>
      <span id="tema-allapot" class="sr-only">Világos téma aktív</span>
    </div>
  </div>
</div>

<script>
  // Repülési mód kapcsoló kezelése
  const repulesModKapcsolo = document.getElementById('repules-mod-kapcsolo');
  
  function toggleRepulesmod() {
    const isChecked = repulesModKapcsolo.getAttribute('aria-checked') === 'true';
    const ujAllapot = !isChecked;
    
    // ARIA állapot frissítése
    repulesModKapcsolo.setAttribute('aria-checked', ujAllapot);
    
    // Vizuális állapot frissítése
    if (ujAllapot) {
      repulesModKapcsolo.classList.add('checked');
    } else {
      repulesModKapcsolo.classList.remove('checked');
    }
    
    // Képernyőolvasó értesítése
    const statusMsg = document.createElement('div');
    statusMsg.setAttribute('role', 'status');
    statusMsg.setAttribute('aria-live', 'polite');
    statusMsg.textContent = `Repülési mód ${ujAllapot ? 'bekapcsolva' : 'kikapcsolva'}`;
    document.body.appendChild(statusMsg);
    
    setTimeout(() => document.body.removeChild(statusMsg), 3000);
  }
  
  repulesModKapcsolo.addEventListener('click', toggleRepulesmod);
  repulesModKapcsolo.addEventListener('keydown', function(e) {
    if (e.key === ' ' || e.key === 'Enter') {
      e.preventDefault();
      toggleRepulesmod();
    }
  });
  
  // Egyedi jelölőnégyzet kezelése
  const ertesitesCheckbox = document.getElementById('ertesites-checkbox');
  
  function toggleErtesites() {
    const isChecked = ertesitesCheckbox.getAttribute('aria-checked') === 'true';
    const ujAllapot = !isChecked;
    
    ertesitesCheckbox.setAttribute('aria-checked', ujAllapot);
    
    if (ujAllapot) {
      ertesitesCheckbox.classList.add('checked');
    } else {
      ertesitesCheckbox.classList.remove('checked');
    }
  }
  
  ertesitesCheckbox.addEventListener('click', toggleErtesites);
  ertesitesCheckbox.addEventListener('keydown', function(e) {
    if (e.key === ' ') {
      e.preventDefault();
      toggleErtesites();
    }
  });
  
  // Téma váltó kezelése
  const temaValto = document.getElementById('tema-valto');
  const temaAllapot = document.getElementById('tema-allapot');
  
  function valtTema() {
    const isPressed = temaValto.getAttribute('aria-pressed') === 'true';
    const ujAllapot = !isPressed;
    
    temaValto.setAttribute('aria-pressed', ujAllapot);
    
    // Állapot szöveg frissítése
    temaAllapot.textContent = ujAllapot ? 'Sötét téma aktív' : 'Világos téma aktív';
    
    // Vizuális változás
    document.body.classList.toggle('dark-theme', ujAllapot);
  }
  
  temaValto.addEventListener('click', valtTema);
  temaValto.addEventListener('keydown', function(e) {
    if (e.key === ' ' || e.key === 'Enter') {
      e.preventDefault();
      valtTema();
    }
  });
</script>

<style>
  .custom-switch {
    width: 48px;
    height: 24px;
    background: #ccc;
    border-radius: 12px;
    position: relative;
    cursor: pointer;
    transition: background 0.3s;
  }
  
  .custom-switch.checked {
    background: #4caf50;
  }
  
  .switch-handle {
    position: absolute;
    width: 20px;
    height: 20px;
    background: white;
    border-radius: 50%;
    top: 2px;
    left: 2px;
    transition: transform 0.3s;
  }
  
  .custom-switch.checked .switch-handle {
    transform: translateX(24px);
  }
  
  .custom-checkbox {
    width: 24px;
    height: 24px;
    border: 2px solid #666;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
  
  .custom-checkbox .checkmark {
    display: none;
  }
  
  .custom-checkbox.checked .checkmark {
    display: block;
  }
  
  .sr-only {
    position: absolute;
    left: -10000px;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
</style>

3. Dinamikus érték frissítések

<!-- DINAMIKUS ÉRTÉKEK ÉS ÁLLAPOTOK -->
<div class="media-controls">
  <h2>Médialejátszó vezérlők</h2>
  
  <!-- Hangerő csúszka dinamikus értékkel -->
  <div class="control-group">
    <label for="hangero" id="hangero-cimke">Hangerő</label>
    <input 
      type="range" 
      id="hangero" 
      min="0" 
      max="100" 
      value="50"
      aria-valuemin="0" 
      aria-valuemax="100" 
      aria-valuenow="50"
      aria-labelledby="hangero-cimke"
      aria-describedby="hangero-ertek"
    >
    <output id="hangero-ertek" for="hangero" aria-live="polite">50%</output>
  </div>
  
  <!-- Lejátszási folyamatjelző -->
  <div class="control-group">
    <span id="lejatszas-cimke">Lejátszási pozíció</span>
    <div 
      role="progressbar" 
      aria-labelledby="lejatszas-cimke"
      aria-valuemin="0" 
      aria-valuemax="300"
      aria-valuenow="0"
      aria-valuetext="0 perc 0 másodperc a 5 percből"
      class="playback-progress"
      id="lejatszas-folyamat"
    >
      <div class="progress-fill" style="width: 0%"></div>
    </div>
    <span id="ido-kijelzo">0:00 / 5:00</span>
  </div>
  
  <!-- Betöltési állapot -->
  <div class="control-group">
    <button 
      type="button" 
      id="toltes-gomb"
      aria-busy="false"
      aria-live="polite"
    >
      Videó betöltése
    </button>
    <div 
      role="status" 
      aria-live="polite" 
      id="toltes-allapot"
      class="loading-status"
    ></div>
  </div>
  
  <!-- Egyedi értékelés komponens -->
  <div class="control-group">
    <span id="ertekeles-cimke">Értékelje ezt a tartalmat</span>
    <div 
      role="slider"
      aria-labelledby="ertekeles-cimke"
      aria-valuemin="1"
      aria-valuemax="5"
      aria-valuenow="3"
      aria-valuetext="3 csillag az 5-ből"
      tabindex="0"
      class="star-rating"
      id="csillag-ertekeles"
    >
      <span class="star" data-rating="1">★</span>
      <span class="star" data-rating="2">★</span>
      <span class="star" data-rating="3">★</span>
      <span class="star" data-rating="4">☆</span>
      <span class="star" data-rating="5">☆</span>
    </div>
  </div>
</div>
<script>
  // Hangerő csúszka dinamikus frissítése
  const hangeroSlider = document.getElementById('hangero');
  const hangeroErtek = document.getElementById('hangero-ertek');
  
  hangeroSlider.addEventListener('input', function() {
    const ertek = this.value;
    
    // ARIA érték frissítése
    this.setAttribute('aria-valuenow', ertek);
    
    // Kijelző frissítése
    hangeroErtek.textContent = ertek + '%';
    
    // Speciális állapotok
    if (ertek == 0) {
      this.setAttribute('aria-valuetext', 'Némítva');
    } else if (ertek == 100) {
      this.setAttribute('aria-valuetext', 'Maximum hangerő');
    } else {
      this.removeAttribute('aria-valuetext');
    }
  });
  
  // Lejátszási folyamat szimulálása
  let lejatszasIdo = 0;
  const maxIdo = 300; // 5 perc másodpercben
  
  function frissitLejatszas() {
    if (lejatszasIdo <= maxIdo) {
      const folyamat = document.getElementById('lejatszas-folyamat');
      const szazalek = (lejatszasIdo / maxIdo) * 100;
      
      // ARIA értékek frissítése
      folyamat.setAttribute('aria-valuenow', lejatszasIdo);
      
      const percek = Math.floor(lejatszasIdo / 60);
      const masodpercek = lejatszasIdo % 60;
      folyamat.setAttribute('aria-valuetext', 
        `${percek} perc ${masodpercek} másodperc a 5 percből`);
      
      // Vizuális frissítés
      folyamat.querySelector('.progress-fill').style.width = szazalek + '%';
      document.getElementById('ido-kijelzo').textContent = 
        `${percek}:${masodpercek.toString().padStart(2, '0')} / 5:00`;
      
      lejatszasIdo++;
    }
  }
  
  // 1 másodpercenként frissít
  setInterval(frissitLejatszas, 1000);
  
  // Betöltési állapot kezelése
  const toltesGomb = document.getElementById('toltes-gomb');
  const toltesAllapot = document.getElementById('toltes-allapot');
  
  toltesGomb.addEventListener('click', function() {
    // Betöltés indítása
    this.setAttribute('aria-busy', 'true');
    this.disabled = true;
    this.textContent = 'Betöltés...';
    
    toltesAllapot.textContent = 'Videó betöltése folyamatban...';
    
    // Szimulált betöltés
    setTimeout(() => {
      this.setAttribute('aria-busy', 'false');
      this.disabled = false;
      this.textContent = 'Videó betöltve';
      
      toltesAllapot.textContent = 'A videó sikeresen betöltődött.';
    }, 3000);
  });
  
  // Csillag értékelés kezelése
  const csillagErtekeles = document.getElementById('csillag-ertekeles');
  const csillagok = csillagErtekeles.querySelectorAll('.star');
  let aktualisErtekeles = 3;
  
  function frissitCsillagok(ertek) {
    aktualisErtekeles = ertek;
    
    // ARIA frissítése
    csillagErtekeles.setAttribute('aria-valuenow', ertek);
    csillagErtekeles.setAttribute('aria-valuetext', `${ertek} csillag az 5-ből`);
    
    // Vizuális frissítés
    csillagok.forEach((csillag, index) => {
      csillag.textContent = index < ertek ? '★' : '☆';
    });
  }
  
  // Egér interakció
  csillagok.forEach(csillag => {
    csillag.addEventListener('click', function() {
      const ertek = parseInt(this.dataset.rating);
      frissitCsillagok(ertek);
    });
  });
  
  // Billentyűzet navigáció
  csillagErtekeles.addEventListener('keydown', function(e) {
    if (e.key === 'ArrowRight' && aktualisErtekeles < 5) {
      frissitCsillagok(aktualisErtekeles + 1);
    } else if (e.key === 'ArrowLeft' && aktualisErtekeles > 1) {
      frissitCsillagok(aktualisErtekeles - 1);
    } else if (e.key === 'Home') {
      frissitCsillagok(1);
    } else if (e.key === 'End') {
      frissitCsillagok(5);
    }
  });
</script>
<style>
  .playback-progress {
    width: 100%;
    height: 8px;
    background: #e0e0e0;
    border-radius: 4px;
    overflow: hidden;
    margin: 8px 0;
  }
  
  .progress-fill {
    height: 100%;
    background: #2196f3;
    transition: width 0.3s;
  }
  
  .star-rating {
    font-size: 24px;
    cursor: pointer;
  }
  
  .star {
    color: #ffd700;
    padding: 0 4px;
  }
  
  .star:hover {
    transform: scale(1.2);
  }
  
  .loading-status {
    margin-top: 8px;
    color: #666;
    font-style: italic;
  }
</style>

Helytelen megoldások

Kerülendő megoldások:

  • Hiányzó szerep és név az egyedi vezérlőn: Egyedi elemek használata szerep és hozzáférhető név nélkül
  • Csak vizuális szöveg használata programozási név nélkül: A hozzáférhető név hiányozhat, ha a szöveg nincs megfelelően hivatkozva
  • ARIA állapot dinamikus frissítésének hiánya: A kisegítő technológiák felhasználói nem tudják, hogy a vezérlő állapota megváltozott
  • Helytelen vagy hiányzó ARIA attribútumok: Rossz szerepek vagy hiányzó értékek használata

1. Hiányzó szerep és név egyedi vezérlőn

<!-- ROSSZ GYAKORLAT - Hiányzó szerep és név -->
<div tabindex="0" onclick="toggle()">
  Kapcsoló
</div>
<!-- Nincs role, nincs hozzáférhető név -->
<!-- Képernyőolvasók nem tudják azonosítani mint kapcsolót -->

<script>
  function toggle() {
    // Funkció van, de nem kommunikálja mi történik
    event.target.classList.toggle('active');
  }
</script>

2. Csak vizuális szöveg használata programozási név nélkül

<!-- ROSSZ GYAKORLAT - Csak vizuális szöveg -->
<div role="button" tabindex="0" onclick="performAction()">
  <span>Kattints ide</span>
</div>
<!-- Nincs aria-label vagy aria-labelledby a szövegre hivatkozva -->
<!-- A képernyőolvasó nem biztos, hogy felismeri a szöveget mint nevet -->

<!-- ROSSZ GYAKORLAT - Ikonos gomb szöveg nélkül -->
<button type="button" onclick="deleteItem()">
  <svg width="16" height="16">
    <path d="M3 6l3 3 6-6"/>
  </svg>
</button>
<!-- Nincs aria-label vagy szöveges tartalom -->

3. ARIA állapot dinamikus frissítésének hiánya

<!-- ROSSZ GYAKORLAT - Állapot nem frissül -->
<div 
  role="checkbox" 
  aria-checked="false" 
  tabindex="0" 
  onclick="toggleCheck()"
>
  Feltételek elfogadása
</div>

<script>
  function toggleCheck() {
    // A vizuális állapot változik, de aria-checked NEM frissül!
    event.target.classList.toggle('checked');
    // Hiányzik: event.target.setAttribute('aria-checked', 'true');
  }
</script>

<!-- ROSSZ GYAKORLAT - Dinamikus értékek nem frissülnek -->
<div 
  role="slider" 
  aria-valuemin="0" 
  aria-valuemax="100" 
  aria-valuenow="50"
  tabindex="0"
>
  <div class="slider-handle"></div>
</div>

<script>
  // A vizuális pozíció változik, de aria-valuenow nem frissül
  function updateSlider(newValue) {
    // Hiányzik az ARIA frissítés
    document.querySelector('.slider-handle').style.left = newValue + '%';
  }
</script>

4. Hiányzó űrlap címkék

<!-- ROSSZ GYAKORLAT - Hiányzó címke -->
<input type="text" placeholder="Név">
<!-- Csak placeholder, nincs label vagy aria-label -->

<!-- ROSSZ GYAKORLAT - Társítatlan címke -->
<label>E-mail cím</label>
<input type="email">
<!-- A label nincs társítva az input-tal -->

<!-- ROSSZ GYAKORLAT - Rejtett címke megfelelő hivatkozás nélkül -->
<span style="display: none;">Jelszó</span>
<input type="password">
<!-- A rejtett szöveg nem érhető el -->

5. Helytelen ARIA szerepek használata

<!-- ROSSZ GYAKORLAT - Helytelen ARIA használat -->
<div role="link" onclick="submit()">Küldés</div>
<!-- Link szerep gomb funkciónál - félrevezető -->

<!-- ROSSZ GYAKORLAT - Felesleges role natív elemeken -->
<button role="button">Gomb</button>
<!-- A natív button már rendelkezik button szereppel -->

<!-- ROSSZ GYAKORLAT - Ütköző szerepek -->
<input type="checkbox" role="button">
<!-- A checkbox és button szerepek ütköznek -->

6. Dinamikus tartalom értesítés nélkül

<!-- ROSSZ GYAKORLAT - Dinamikus tartalom értesítés nélkül -->
<div id="eredmeny"></div>
<button onclick="loadResult()">Eredmény betöltése</button>

<script>
  function loadResult() {
    // Az eredmény megjelenik, de nincs aria-live
    document.getElementById('eredmeny').textContent = 'Sikeres művelet!';
    // Hiányzik: role="status" vagy aria-live="polite"
  }
</script>

<!-- ROSSZ GYAKORLAT - Hibaüzenetek értesítés nélkül -->
<form>
  <input type="email" id="email-input">
  <div id="error-message" style="display: none; color: red;"></div>
</form>

<script>
  function showError(message) {
    const errorDiv = document.getElementById('error-message');
    errorDiv.textContent = message;
    errorDiv.style.display = 'block';
    // Hiányzik: aria-live vagy role="alert"
  }
</script>

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