Web erişilebilirliği: klavyeyle kullanılamayan arayüz bozuktur
Fareyi bırakıp Tab tuşuyla gezinen bir kullanıcı, çoğu arayüzde beş dakika içinde sıkışır. Klavye erişilebilirliğinin en sık kırıldığı beş nokta ve düzeltmeleri.
Bir arayüzün erişilebilir olup olmadığını anlamanın en hızlı yolu ekran okuyucu kurmak değil. Fareyi masadan çekin ve Tab tuşuyla gezinmeyi deneyin. Kendi ürününüzde beş dakika içinde en az bir yere sıkışacaksınız: odak kaybolacak, bir menü açılıp kapanmayacak, modal'ın arkasındaki linklere düşeceksiniz.
Bu bir "erişilebilirlik iyileştirmesi" değil, hata. Klavyeyle kullanılamayan bir kontrol, sadece görme engelli kullanıcı için değil; trackpad'i bozulan, kolu alçıda olan, form doldururken elini klavyeden kaldırmak istemeyen herkes için bozuk.
Bir numaralı sebep: buton olmayan buton
Neredeyse her projede aynı satır var.
<div class="btn" onclick="submitForm()">Kaydet</div>
Bu eleman odak alamaz, Enter ve Space'e yanıt vermez, yardımcı teknolojiye kendini buton olarak tanıtmaz. Üç kusuru da tek satırda düzeltmenin yolu, kusurları tek tek yamamak değil:
<button type="button" onclick="submitForm()">Kaydet</button>
<button> varsayılan olarak odaklanabilir, Enter ve Space ile tetiklenir, doğru rolü bildirir ve form içinde type belirtilmezse gönderim yapar. Bunları elle üretmeye kalkarsanız yazacağınız kod şuna benzer ve hâlâ eksik kalır:
<div class="btn" role="button" tabindex="0"
onclick="submitForm()"
onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();submitForm()}">
Kaydet
</div>
Kural basit: tıklanabilir her şey <button> ya da <a href> olsun. Yeri değiştiren şey link, durum değiştiren şey buton.
İkinci sebep: outline'ı silmek
Tasarım incelemesinde "şu mavi çerçeve çirkin" denir ve şu satır eklenir:
*:focus { outline: none; }
Bu satır, klavye kullanıcısının imlecini görünmez yapar. Odağın nerede olduğunu bilmeden Tab'a basmak, ekranı kapalı bir bilgisayarda gezinmektir.
Çirkin bulunan şey genellikle varsayılan halka, gerekli olan şey ise görünürlük. İkisi ayrılabilir:
:focus:not(:focus-visible) { outline: none; }
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
border-radius: 2px;
}
:focus-visible, tarayıcının kullanıcının klavye mi fare mi kullandığına dair sezgisini kullanır. Fareyle tıklayan kullanıcı halkayı görmez, Tab'a basan görür. Renk ve kalınlığı istediğiniz gibi ayarlayın; kaldırmayın.
Üçüncü sebep: sıra DOM'da değil, CSS'te
Tab sırası, elemanların DOM'daki yazılış sırasını takip eder. order, grid-area, position: absolute ile görsel yerleşimi değiştirdiğinizde odak sırası ekrandaki sırayla ayrışır. Kullanıcı sağ üstteki butondan sol alttaki alana atlar.
Bunu tabindex="1", tabindex="2" yazarak düzeltmeye kalkmayın. Pozitif tabindex değerleri sayfadaki tüm doğal sıranın önüne geçer ve her yeni bileşen eklendiğinde sırayı yeniden elden geçirmek zorunda kalırsınız. Kabul edilebilir iki değer var: 0 (doğal sıraya kat) ve -1 (koda odaklanabilir ama Tab ile ulaşılamaz).
Doğru çözüm, DOM sırasını okuma sırasına göre yazıp yerleşimi CSS'e bırakmak.
Dördüncü sebep: modal, odağı bırakmıyor
Açılan bir katman üç şeyi yapmak zorunda:
- Açılınca odağı içine almak
- Arkasına Tab ile geçilmesini engellemek
- Escape ile kapanmak ve odağı açan öğeye geri vermek
Üçüncüsü en sık atlanan. Modal kapandığında odak <body>'ye düşerse, kullanıcı listenin başına döner ve otuz kez Tab'a basar.
const acan = document.activeElement;
modal.showModal(); // <dialog> kullanıyorsanız
modal.addEventListener('close', () => acan?.focus());
Yerel <dialog> elemanı odak tutmayı ve Escape'i tarayıcıdan alır. Kendi katmanınızı yazıyorsanız, arka planı inert özniteliğiyle devre dışı bırakmak, elle odak tuzağı kurmaktan daha az kırılgan:
document.querySelector('#app').inert = true; // açarken
document.querySelector('#app').inert = false; // kapatırken
Beşinci sebep: özel bileşenlerde ok tuşları yok
Kendi yazdığınız açılır liste, sekme çubuğu ya da otomatik tamamlama alanı, yerel muadilinin klavye davranışını taklit etmek zorunda. Açılır listede Yukarı ve Aşağı seçeneği gezer, Enter seçer, Escape kapatır, Home ve End uçlara gider. Tab'ın tek başına yeterli olduğu bir bileşen yazdıysanız, muhtemelen yerel <select> yeterliydi.
Yerel eleman her zaman ilk tercih olmalı. Yerel elemanı yeniden yazmanın bedeli, çoğu ekipte tasarım özgürlüğünün karşılığından yüksek.
Bir de sessiz tuzak: disabled
disabled özniteliği verilen buton odak alamaz. Formun sonunda gri bir "Gönder" butonu duruyorsa, klavye kullanıcısı ona hiç ulaşamaz ve neden gönderemediğini öğrenemez. Sebebi görünür kılmak istiyorsanız:
<button aria-disabled="true" aria-describedby="neden">Gönder</button>
<p id="neden">Devam etmek için sözleşmeyi onaylayın.</p>
aria-disabled odağı korur, tıklamayı ise kodda engellersiniz.
Otomatik araçlar bu hataların çoğunu görmez
Lighthouse, axe ve benzeri denetleyiciler eksik alt, düşük kontrast, etiketsiz form alanı gibi statik kusurları iyi yakalar. Odak sırası, odak tuzağı, modal kapanışında odağın kaybolması gibi kusurlar etkileşim gerektirdiği için statik denetimden kaçar. Yeşil skor, klavyeyle kullanılabilirlik kanıtı değil.
Test şu: her yeni ekran için fareyi bırakın, Tab ile baştan sona gezin, her adımda odağın nerede olduğunu görebildiğinizi doğrulayın. Beş dakika sürer ve otomatik denetimin göremediği kusurların neredeyse tamamını çıkarır.
Bu hafta yapılacak
Projenizde outline: none aratın.
grep -rn "outline: *none" src/
Çıkan her satırın karşılığında bir :focus-visible kuralı var mı bakın. Yoksa, elinizdeki en yüksek getirili erişilebilirlik düzeltmesi orada duruyor.
- erişilebilirlik
- klavye navigasyonu
- frontend
- HTML