Erişilebilirlik (A11y) Kontrolleri

WCAG 2.1 AA hedefiyle hizalı, pratikte en sık gözden kaçan erişilebilirlik maddeleri. Erişilebilir arayüz = daha fazla kullanıcı + yasal uyum.
Kritik

Semantik HTML kullan; <div> yığınlarına son

Neden önemli

Ekran okuyucular ve arama motorları sayfa yapısını etiket anlamından çıkarır. <button> yerine <div onclick> kullandığında klavye, enter, rol, focus — hepsini kendin kurmak zorunda kalırsın ve genelde unutulur.

Nasıl uygula

<header>, <nav>, <main>, <article>, <aside>, <footer>. Tıklanabilir → <button> veya <a>. Form kontrolleri → <label>, <fieldset>, <legend>. Liste → <ul>/<ol>/<li>.

Örnek
Yanlış: <div class="btn" onclick=...>Gönder</div>. Doğru: <button type="submit">Gönder</button>.
Kritik

Görsellere anlamlı alt metin yaz

Neden önemli

Ekran okuyucu kullanıcıları, görme engelliler ve görseli yüklenmeyen herkes alt metne bağımlıdır. Eksik alt = içeriğin bir kısmı kayıp.

Nasıl uygula

Bilgi taşıyan görsellerde alt içeriği anlat, estetik/dekoratif olanlarda alt="". Aynı sayfada ikon + metin varsa, ikon aria-hidden="true". Anahtar kelime sıkıştırma değil, faydalı açıklama yaz.

Örnek
Yanlış: <img src="logo.png" alt="logo">. Doğru: <img src="logo.png" alt="Duck — Fikirden mimariye proje sihirbazı"> veya dekoratifse alt="".
Kritik

Renk kontrastı en az 4.5:1 (küçük metin)

Neden önemli

Düşük kontrast, görme yeteneği azalmış kullanıcılar ve güneşli ortamda telefon kullananlar için okunamaz. WCAG AA minimum normal metin 4.5:1, büyük metin 3:1.

Nasıl uygula

Tasarım aşamasında renk paletini kontrast için test et (Figma Contrast, contrast-ratio.com). Gri üzeri açık gri tipografi kaçın. Placeholder metni değeri göstermek için kullanma; #999 kontrast geçmez.

Örnek
Bootstrap 5: text-muted varsayılan #6c757d (bg-white üstünde 4.54:1 — AA geçer). Daha açık rengi AA kaybeder.
Kritik

Görünür focus göstergesi bırak

Neden önemli

Klavye, ekran okuyucu ve Switch kullanıcıları nerede olduğunu focus ring'den görür. 'outline: none' koymak en sık yapılan a11y hatasıdır.

Nasıl uygula

Global outline'ı kaldırma. Özel tasarım istiyorsan :focus-visible ile ring'i yeniden tanımla (kontrast yeterli, kalınlık ≥2px). Fare kullanıcısı için dikkat dağıtmasını istemiyorsan :focus-visible sadece klavyede çalışır.

Örnek
button:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }
Kritik

Tüm etkileşim klavyeyle çalışsın

Neden önemli

Trackpad/fare kullanamayan herkes klavyeye dayanır. Sadece hover'da ortaya çıkan menü, sadece drag ile taşınan kart — herkes için engel.

Nasıl uygula

Tab sırası doğal olmalı. Modal'da focus-trap kur, kapanınca focus'u tetikleyene döndür. Dropdown'lar arrow key + enter ile çalışsın. Escape modal/dropdown'u kapatsın. Drag işlemlerine klavye alternatifi koy.

Örnek
dnd-kit: klavye sensor'ünü etkinleştir → space ile 'tut', arrow ile taşı, space ile 'bırak'.
Yüksek

Form kontrollerinin her zaman etiketi olsun

Neden önemli

Placeholder kaybolur ve etiket yerine geçmez. Label'siz alanda ekran okuyucu 'text field' der ve kullanıcı ne girdiğini bilmez.

Nasıl uygula

<label for="id">...</label> + <input id="id"> veya label içine wrap et. İkon-buton için aria-label. Grup (ör. radio) için <fieldset>/<legend>. Zorunluluğu aria-required ile belirt.

Örnek
<label for="email">E-posta</label><input id="email" type="email" required aria-required="true">
Yüksek

ARIA'yı sadece gerekliyse kullan — doğal HTML önceliği

Neden önemli

'No ARIA is better than bad ARIA.' Yanlış role/state kullanımı erişilebilirliği doğrudan bozar. Çoğu ihtiyaç zaten HTML etiketleriyle karşılanır.

Nasıl uygula

role="button" yerine <button>. Özel pattern'lar (combobox, tablist, tree) için WAI-ARIA Authoring Practices'e uy. Dinamik içeriğe aria-live="polite"/"assertive".

Örnek
Toast bildirimi: <div role="status" aria-live="polite"> içerik </div> ekran okuyucu tarafından okunur.
Orta

Skip link ekle — klavye kullanıcısı menüyü atlayabilsin

Neden önemli

Her sayfada aynı 20-50 menü öğesini tab'layarak geçmek yorucudur. Skip link ana içeriğe tek atlayış verir.

Nasıl uygula

Sayfanın ilk odaklanabilir elemanı 'İçeriğe atla' linki olsun. Normalde görünmez, focus aldığında görünür.

Örnek
<a class="visually-hidden-focusable" href="#main">İçeriğe atla</a> + <main id="main">…</main>
Yüksek

Sadece renge güvenme — durumu ek işaretle belirt

Neden önemli

Renk körü kullanıcılar kırmızı/yeşil farkı göremez. Durum sadece renkle anlatılırsa bu kullanıcılar için bilgi yok demektir.

Nasıl uygula

Hata rengine ek olarak ikon + metin. Zorunlu alana (*) ekle, sadece kırmızı kenarlık yeterli değil. Chart'larda desen/shape variasyonu koy.

Örnek
Form: kırmızı kenarlık + ⚠ ikonu + 'Geçersiz e-posta' metni. Chart: çizgiler sadece renkle değil, dash/dot pattern ile de ayırt.
Orta

Hareket azaltma tercihine saygı göster

Neden önemli

Vestibüler hassasiyeti olan kullanıcılar için agresif parallax/animasyon baş dönmesi yapar. OS'teki 'reduce motion' tercihini takip etmezsen sitenin içinde kalamazlar.

Nasıl uygula

prefers-reduced-motion: reduce media query'si ile animasyonları kısa ya da kapalı yap. Auto-playing video için opt-in iste.

Örnek
@media (prefers-reduced-motion: reduce) { * { animation-duration: .01ms !important; transition-duration: .01ms !important; } }
Orta

Sayfanın ve bölümlerin dilini belirt

Neden önemli

Ekran okuyucu doğru sesleri üretmek için dili bilmek zorundadır. Yanlış lang → İngilizce okunuşla Türkçe metin = kullanıcı için karmaşa.

Nasıl uygula

<html lang="tr"> veya kullanıcının dilini yansıtacak şekilde dinamik. Farklı dilde bir alıntı varsa span lang="en" ile işaretle.

Örnek
Duck Layout'ta <html lang="@L.CurrentCode"> → TR/EN arası geçince doğru ses.
Orta

Medyaya altyazı ve transkript ekle

Neden önemli

Sessiz ortamda veya işitme engelli izleyiciler için altyazı şart. Transkript ise arama motoru ve içeriği hızlıca tarayanlar için değerli.

Nasıl uygula

Video için WebVTT subtitle dosyası (<track kind="captions">). Podcast için metin transkript yayımla. Otomatik üretim (Whisper) kabul edilebilir ama gözden geçir.

Örnek
<video><track kind="captions" src="subs.tr.vtt" srclang="tr" label="Türkçe" default></video>