2.4.7 - Látható fókusz

Röviden a szabványpontról

A WCAG 2.4.7 (Focus Visible) előírja, hogy minden billentyűzettel működtethető felhasználói felület rendelkezzen látható billentyűzet fókusz jelzővel. Ez azt jelenti, hogy amikor a felhasználók billentyűzettel navigálnak az oldalon (pl. Tab billentyű), egyértelműen látható legyen, melyik elem van éppen fókuszban.

Mire vonatkozik: Minden interaktív elemre, mint linkek, gombok, űrlap mezők és egyéni widgetek.

Cél: Biztosítani, hogy a billentyűzetes navigációra támaszkodó felhasználók:

  • Vizuálisan követhessék pozíciójukat az oldalon
  • Ne kerüljenek zavarba
  • Javuljon a használhatóság élményük
  • Egyértelműen lássák az aktív elemet

Kiket érint

Elsődleges felhasználók: Azok, akik mozgáskorlátozottság, látáskárosodás, vagy ideiglenes sérülés miatt nem tudnak egeret használni és erősen támaszkodnak a billentyűzetes navigációra.

Másodlagos előnyök: A látható fókusz segít minden felhasználónak, beleértve a haladó felhasználókat, akik hatékonyabban használják a billentyűparancsokat és a kognitív fogyatékossággal élőket, akik profitálnak az egyértelmű vizuális jelzésekből.

Tesztelés

  1. Billentyűzetes navigációs teszt: Használd a Tab billentyűt az összes interaktív elemen való végignavigáláshoz. Győződj meg róla, hogy a fókusz keret, vagy kiemelés egyértelműen látható minden elemen
  2. Egyéni fókusz stílusok ellenőrzése: Vizsgáld meg az egyéni CSS-sel rendelkező elemeket, hogy rendelkeznek-e látható fókusz stílussal, amely megfelel a kontrasztkövetelményeknek
  3. Képernyőnagyító tesztelés: Nagyítsd fel az oldalt és ellenőrizd, hogy a fókusz jelző látható marad-e és nem vágódik-e le, vagy takarják-e el
  4. Automatizált eszközök: Használj akadálymentességi tesztelő eszközöket, mint az axe DevTools, vagy böngésző bővítmények, amelyek jelentik a hiányzó, vagy nem megfelelő fókusz jelzőket
  5. Több böngészős tesztelés: Ellenőrizd a fókusz láthatóságát több böngészőben és operációs rendszeren, mivel az alapértelmezett fókusz stílusok változhatnak

Jó gyakorlatok

1. Alapértelmezett böngésző fókusz használata

<!-- Jó: Alapértelmezett fókusz körvonal látható -->
<nav>
  <ul>
    <li><a href="/fooldal">Főoldal</a></li>
    <li><a href="/termekek">Termékek</a></li>
    <li><a href="/kapcsolat">Kapcsolat</a></li>
  </ul>
</nav>

<!-- Jó: Űrlap elemek alapértelmezett fókusszal -->
<form>
  <label for="nev">Név:</label>
  <input type="text" id="nev" name="nev">
  
  <label for="email">E-mail:</label>
  <input type="email" id="email" name="email">
  
  <button type="submit">Küldés</button>
</form>

<!-- Jó: Interaktív elemek természetes fókusz sorrenddel -->
<div class="mukodok">
  <button type="button">Szerkesztés</button>
  <button type="button">Törlés</button>
  <a href="/reszletek">Részletek</a>
</div>

Az alapértelmezett böngésző fókusz körvonal használata gyakran elegendő és konzisztens. A böngészők beépített fókusz stílusai megfelelnek az akadálymentességi követelményeknek.

2. Egyéni fókusz stílusok nagy kontraszttal

<!-- Jó: Egyéni gombok látható fókusz stílussal -->
<button class="primary-button">Főművelet</button>
<button class="secondary-button">Másodlagos művelet</button>
<button class="danger-button">Törlés</button>

<style>
/* Alapértelmezett gomb stílusok */
.primary-button {
  background: #0066cc;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
}

.secondary-button {
  background: #6c757d;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
}

.danger-button {
  background: #dc3545;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
}

/* Jó: Nagy kontrasztú fókusz stílusok */
.primary-button:focus {
  outline: 3px solid #ffbf47; /* Narancssárga körvonal */
  outline-offset: 2px;
  box-shadow: 0 0 0 1px #0066cc; /* Belső árnyék */
}

.secondary-button:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
  box-shadow: 0 0 0 1px #6c757d;
}

.danger-button:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
  box-shadow: 0 0 0 1px #dc3545;
}

/* Jó: Linkek egyéni fókusz stílusa */
a:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
  background-color: #e6f3ff;
  text-decoration: none;
}

/* Jó: Űrlap elemek egyéni fókusza */
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus {
  outline: 2px solid #0066cc;
  outline-offset: 1px;
  border-color: #0066cc;
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
}

select:focus {
  outline: 2px solid #0066cc;
  outline-offset: 1px;
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
}

/* Jó: Checkbox és radio egyéni fókusza */
input[type="checkbox"]:focus,
input[type="radio"]:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}
</style>

Ha felülírjuk az alapértelmezett körvonalat, biztosítani kell, hogy látható, nagy kontrasztú stílust alkalmazzunk. A narancssárga, vagy kék színek jól láthatók a legtöbb háttéren.

3. Fókusz egyéni komponenseken

<!-- Jó: Div elemek gombként, tabindex és fókusz stílussal -->
<div role="button" tabindex="0" class="custom-button" onclick="mukodes()">
  Egyéni gomb
</div>

<div role="button" tabindex="0" class="icon-button" onclick="bezaras()">
  <span aria-hidden="true">×</span>
  <span class="sr-only">Bezárás</span>
</div>

<!-- Jó: Egyéni dropdown/accordion komponens -->
<div class="dropdown">
  <button type="button" 
          class="dropdown-toggle" 
          aria-expanded="false"
          aria-haspopup="true">
    Menü megnyitása
  </button>
  <ul class="dropdown-menu" role="menu">
    <li role="none">
      <a href="/profil" role="menuitem">Profil</a>
    </li>
    <li role="none">
      <a href="/beallitasok" role="menuitem">Beállítások</a>
    </li>
    <li role="none">
      <button type="button" role="menuitem" onclick="kijelentkezes()">
        Kijelentkezés
      </button>
    </li>
  </ul>
</div>

<!-- Jó: Tab komponens -->
<div class="tabs" role="tablist">
  <button type="button" 
          role="tab" 
          aria-selected="true" 
          aria-controls="tab1-panel"
          id="tab1">
    Áttekintés
  </button>
  <button type="button" 
          role="tab" 
          aria-selected="false" 
          aria-controls="tab2-panel"
          id="tab2">
    Részletek
  </button>
  <button type="button" 
          role="tab" 
          aria-selected="false" 
          aria-controls="tab3-panel"
          id="tab3">
    Értékelések
  </button>
</div>

<style>
/* Egyéni gomb komponens fókusza */
.custom-button {
  background: #28a745;
  color: white;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
  display: inline-block;
  user-select: none;
}

.custom-button:focus {
  outline: none; /* Alapértelmezett eltávolítása */
  box-shadow: 0 0 0 3px #ffbf47; /* Sárga fényudvar */
  background: #218838; /* Kissé sötétebb háttér */
}

/* Ikon gomb fókusza */
.icon-button {
  background: #6c757d;
  color: white;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 18px;
  font-weight: bold;
}

.icon-button:focus {
  outline: none;
  box-shadow: 0 0 0 3px #ffbf47;
  background: #5a6268;
}

/* Dropdown komponens fókusza */
.dropdown-toggle:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
  box-shadow: 0 0 0 1px #fff, 0 0 0 3px #0066cc;
}

.dropdown-menu a:focus,
.dropdown-menu button:focus {
  outline: none;
  background-color: #0066cc;
  color: white;
}

/* Tab komponens fókusza */
.tabs button[role="tab"]:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
  z-index: 1;
  position: relative;
}

/* Screen reader only szöveg */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
</style>

<script>
// Billentyűzet támogatás egyéni gombokhoz
document.querySelectorAll('[role="button"]').forEach(button => {
  button.addEventListener('keydown', function(e) {
    // Enter vagy Space billentyű kezelése
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      this.click();
    }
  });
});
</script>

Egyéni widgetek esetén (pl. div elemek gombként) add hozzá a tabindex=”0″-t és látható fókusz stílusokat. A JavaScript biztosítja a billentyűzet funkcionalitást.

4. Fókusz láthatóság megőrzése animációk során

<!-- Jó: Animált gombok fókusz megőrzéssel -->
<button class="animated-button">Hover és Focus animáció</button>
<button class="pulse-button">Pulzáló gomb</button>
<button class="loading-button" onclick="loadingStart(this)">
  <span class="button-text">Betöltés indítása</span>
  <span class="loading-spinner" style="display: none;">⏳</span>
</button>

<!-- Jó: Accordions fókusz megtartással -->
<div class="accordion">
  <button type="button" 
          class="accordion-header"
          aria-expanded="false"
          aria-controls="accordion-content-1">
    GYIK - Gyakori kérdések
  </button>
  <div id="accordion-content-1" class="accordion-content" hidden>
    <p>Itt találod a leggyakrabban feltett kérdéseket...</p>
  </div>
</div>

<style>
/* Animált gomb, fókusz megőrzéssel */
.animated-button {
  background: #007bff;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  transition: all 0.3s ease;
  cursor: pointer;
}

.animated-button:hover {
  background: #0056b3;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}

/* Jó: Fókusz látható marad animáció során */
.animated-button:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
  /* Animáció NEM rejti el a fókuszt */
}

.animated-button:focus:hover {
  /* Hover és focus kombinálva */
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
  background: #0056b3;
  transform: translateY(-2px);
}

/* Pulzáló animáció fókusz megtartással */
.pulse-button {
  background: #28a745;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  animation: pulse 2s infinite;
  cursor: pointer;
}

@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.7; }
  100% { opacity: 1; }
}

.pulse-button:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
  /* Animáció nem befolyásolja a fókusz láthatóságát */
}

/* Loading gomb állapotok */
.loading-button {
  background: #6c757d;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
  position: relative;
}

.loading-button.loading {
  pointer-events: none;
  opacity: 0.7;
}

.loading-button:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
}

/* Jó: Loading állapotban is látható a fókusz */
.loading-button.loading:focus {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
}

/* Accordion fókusz stílusok */
.accordion-header {
  width: 100%;
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  padding: 15px;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.accordion-header:hover {
  background: #e9ecef;
}

.accordion-header:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
  background: #e9ecef;
  /* Fókusz látható expansion során is */
}

.accordion-content {
  border: 1px solid #dee2e6;
  border-top: none;
  padding: 15px;
  background: white;
}

/* Smooth animációk fókusz megtartással */
.accordion-content {
  overflow: hidden;
  transition: max-height 0.3s ease;
}

.accordion-content[hidden] {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}

.accordion-content:not([hidden]) {
  max-height: 500px;
}
</style>

<script>
function loadingStart(button) {
  // Fókusz megtartása loading állapotban
  const buttonText = button.querySelector('.button-text');
  const spinner = button.querySelector('.loading-spinner');
  
  button.classList.add('loading');
  buttonText.style.display = 'none';
  spinner.style.display = 'inline';
  
  // Fókusz megmarad az elemen
  button.setAttribute('aria-busy', 'true');
  
  setTimeout(() => {
    button.classList.remove('loading');
    buttonText.style.display = 'inline';
    spinner.style.display = 'none';
    button.removeAttribute('aria-busy');
    // Fókusz még mindig az elemen van
  }, 3000);
}

// Accordion funkcionality
document.querySelectorAll('.accordion-header').forEach(header => {
  header.addEventListener('click', function() {
    const content = document.getElementById(this.getAttribute('aria-controls'));
    const isExpanded = this.getAttribute('aria-expanded') === 'true';
    
    this.setAttribute('aria-expanded', !isExpanded);
    content.hidden = isExpanded;
    
    // Fókusz megmarad a headeren az animáció során
  });
});
</script>

Kerüld a fókusz jelző eltávolítását, vagy elrejtését átmenetek vagy animációk során. A fókusz mindig látható maradjon, még betöltési vagy expandáló állapotokban is.

Helytelen megoldás

Fókusz körvonal eltávolítása helyettesítés nélkül

<!-- Rossz: Fókusz eltávolítása minden elemről -->
<style>
a:focus, 
button:focus, 
input:focus {
  outline: none; /* Rossz: eltávolítja a vizuális fókuszt */
}

/* Vagy általános reset */
* {
  outline: none; /* Nagyon rossz: minden fókusz eltűnik */
}

*:focus {
  outline: 0; /* Ugyanilyen rossz */
}
</style>

Probléma: A fókusz körvonal eltávolítása anélkül, hogy valami más helyettesítené, lehetetlenné teszi a billentyűzetes felhasználók számára a navigációt.

Alacsony kontrasztú fókusz stílusok

<!-- Rossz: Gyenge kontrasztú fókusz stílusok -->
<style>
button:focus {
  outline: 1px solid #ccc; /* Túl halvány */
}

.light-button:focus {
  outline: 1px solid #f0f0f0; /* Szinte láthatatlan */
  background: #fafafa; /* Alig látható változás */
}

a:focus {
  color: #ddd; /* Gyenge szín kontrast */
  text-decoration: underline;
}

input:focus {
  border: 1px solid #e0e0e0; /* Alig különbözik a normál állapottól */
}
</style>

Probléma: A fókusz jelzőnek ki kell tűnnie a háttérből. A halvány színek nem biztosítanak elegendő kontrasztot.

Nem szabványos interaktív elemek billentyűzet fókusz nélkül

<!-- Rossz: Div elemek gombként tabindex nélkül -->
<div class="clickable" onclick="action()">Kattints rám</div>
<span class="button-like" onclick="submit()">Küldés</span>

<!-- Rossz: Egyéni navigáció billentyűzet támogatás nélkül -->
<div class="menu-item" onmouseover="showSubmenu()">Menü</div>
<div class="carousel-button" onclick="nextSlide()">Következő</div>

<!-- Rossz: Hamis linkek -->
<a href="#" onclick="openModal()">Modal megnyitása</a>
<a href="javascript:void(0)" onclick="toggle()">Váltás</a>

Probléma: A div vagy span elemek használata gombként, de tabindex nélkül nem teszi őket billentyűzettel elérhetővé. A hamis linkek (# vagy javascript:) zavaróak.

Fókusz elrejtése egér interakció miatt

<!-- Rossz: Fókusz elrejtése minden esetben -->
<style>
/* Rossz megközelítés: mindenhol elrejti a fókuszt */
.no-focus-outline:focus {
  outline: none;
}

button:active {
  outline: none; /* Rossz: aktív állapotban is kell a fókusz */
}

/* Rossz: CSS-only megoldás ami elrejti a fókuszt */
.button:not(:focus-visible) {
  outline: none; /* Nem minden böngésző támogatja */
}
</style>

<script>
// Rossz: Minden click után fókusz eltávolítása
document.addEventListener('click', function(e) {
  e.target.blur(); // Rossz: billentyűzetes felhasználókat is érint
});

// Rossz: Fókusz elrejtése mouseover-rel
document.addEventListener('mouseover', function(e) {
  if (e.target.matches('button, a, input')) {
    e.target.style.outline = 'none'; // Rossz: billentyűzet fókusz is eltűnik
  }
});
</script>

Probléma: A fókusz elrejtése egér interakció alapján gyakran a billentyűzetes felhasználókat is érinti. A :focus-visible használata jobb, de támogatást igényel.

Dinamikus tartalom fókusz kezelés nélkül

<!-- Rossz: Modal megnyitása fókusz kezelés nélkül -->
<script>
function rossModalMegnyitas() {
  const modal = document.getElementById('modal');
  modal.style.display = 'block';
  // Rossz: nem állítja át a fókuszt a modalra
  // A billentyűzetes felhasználók továbbra is a háttérben navigálnak
}

function rossModalBezaras() {
  const modal = document.getElementById('modal');
  modal.style.display = 'none';
  // Rossz: nem állítja vissza a fókuszt az eredeti elemre
}

// Rossz: Dinamikus tartalom hozzáadása fókusz nélkül
function rossUjTartalomHozzaadas() {
  const container = document.getElementById('content');
  container.innerHTML = `
    <h2>Új tartalom</h2>
    <button>Új gomb</button>
  `;
  // Rossz: nem értesíti a felhasználót a változásról
  // Nem irányítja a fókuszt az új tartalomra
}
</script>

<!-- Rossz: Tab komponens rossz fókusz kezeléssel -->
<script>
function rossTabVáltas(tabId) {
  // Minden tab elrejtése
  document.querySelectorAll('.tab-content').forEach(content => {
    content.style.display = 'none';
  });
  
  // Kiválasztott tab megjelenítése
  document.getElementById(tabId).style.display = 'block';
  
  // Rossz: nem kezeli a fókuszt
  // A billentyűzetes felhasználók nem tudják, hogy váltott a tartalom
}
</script>

Probléma: Dinamikus tartalom megjelenésekor, vagy modal ablakok nyitásakor a fókusz kezelése kritikus. A fókusz megfelelő áthelyezése nélkül a billentyűzetes felhasználók elvesznek.

Fókusz csapdák és korlátlan fókusz mozgás

<!-- Rossz: Modal fókusz csapda nélkül -->
<div class="modal" style="display: block;">
  <div class="modal-content">
    <h2>Modal cím</h2>
    <button>OK</button>
    <button>Mégse</button>
  </div>
</div>
<!-- Rossz: A fókusz ki tud jutni a modalból a háttér elemekre -->

<script>
// Rossz: Végtelen fókusz hurok egyéni komponensben
function rossDropdownNavigation(event) {
  const items = dropdown.querySelectorAll('a');
  let currentIndex = Array.from(items).indexOf(event.target);
  
  if (event.key === 'ArrowDown') {
    currentIndex++;
    // Rossz: nem ellenőrzi a határokat
    items[currentIndex].focus(); // Hiba ha currentIndex >= items.length
  }
}

// Rossz: Fókusz "ugrik" véletlenszerűen
function rossRandomFocus() {
  const buttons = document.querySelectorAll('button');
  const randomButton = buttons[Math.floor(Math.random() * buttons.length)];
  randomButton.focus(); // Váratlan fókusz mozgás
}
</script>

Probléma: A modal ablakokban a fókusz fixálás hiánya lehetővé teszi, hogy a felhasználók a háttér tartalmára navigáljanak. A váratlan fókusz mozgások megzavarják a navigációt.

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