Bir form kullanılabilir olmadan önce kullanıcının her alanın ne istediğini bilmesi gerekir. Görenler için bu bilgi etikettir; ekran okuyucu kullanıcısı için ise etiket programatik olarak alana bağlanmış olduğunda çalışır. axe-core'un label kuralı, formdaki hangi alanların bu bağı kuramamış olduğunu yakalar.
1. label hatası nedir?
Kural, form kontrollerinin (<input>, <select>, <textarea>) erişilebilir bir ada sahip olup olmadığını kontrol eder. Erişilebilir adın gelebileceği dört kaynak vardır:
- Bir
<label>öğesininforniteliğiyle alana bağlanmış olması - Alanın bir
<label>içinde sarmalanmış olması (implicit label) - Alanın
aria-labelniteliği taşıması - Alanın
aria-labelledbyile başka bir öğeye referans vermesi
Bunlardan hiçbiri yoksa label kuralı tetiklenir. Bu, WCAG 2.1 1.3.1 Info and Relationships ve 4.1.2 Name, Role, Value kriterlerinin doğrudan ihlalidir.
2. Neden önemli?
Etiketsiz bir form alanı, klavye veya ekran okuyucu kullanıcısı için duvardır. Tab tuşuyla alana ulaşıldığında ekran okuyucu yalnızca alanın türünü söyler — “edit, blank” — ama o alana ne yazılması gerektiğini bilmez.
Konu erişilebilirlikle de bitmiyor. Etiket-alan bağı, mobil cihazlarda dokunma alanını da büyütür: kullanıcı sadece input'a değil, etiketin de yazılı olduğu alana dokunduğunda alan odaklanır. Bu, küçük checkbox'ları büyük parmaklarla işaretlemeyi mümkün kılar. Yani etiket, sadece engelliler için değil, herkes için kullanılabilirliği artırır.
Formlar bir sitenin para kazanma noktasıdır. Sepete ekle, üye ol, abone ol — hepsi form. Yanlış etiketlenmiş bir form alanı dönüşüm oranını doğrudan düşürür.
3. Hangi durumlarda tetiklenir?
- Etiketsiz alan:
<input type="text" name="email">tek başına; yanında bir<label>yok. - for/id eşleşmesi tutmuyor:
<label for="mail">ama input'taid="email". Görsel olarak yan yana gösterilse de tarayıcı bunu etiket sayar. - Sadece placeholder var, etiket yok: En yaygın anti-pattern. Placeholder kullanıcı yazmaya başladığı anda kaybolur ve geriye hatırlatıcı kalmaz.
- Etiket görünmüyor ama bağlanmış: Geliştirici
display: noneuygulamış. Görsel olarak gizli, ekran okuyucu için de gizli; iki taraf da kaybetti. - Aynı id'ye birden fazla label bağlı: Bu durum WCAG'ın
form-field-multiple-labelskuralını tetikler ve okuyucuda kafa karışıklığı yaratır.
4. Doğru çözümler
4.1 for/id ile bağlama (önerilen)
En güvenli ve en geniş uyumluluğa sahip yöntem. Etiket tıklandığında alan odaklanır; ekran okuyucu doğru sırayla okur.
<input type="email" name="email" placeholder="E-posta"><label for="email">E-posta</label>
<input id="email" type="email" name="email">4.2 Sarmalayan label (implicit)
for/id ilişkisi kurmak istemezseniz <label> içine alanı doğrudan sarabilirsiniz. Daha kısadır ama bazı yardımcı teknolojilerde for/id kadar tutarlı desteklenmez. Yine de geçerli bir yoldur.
<label>
E-posta
<input type="email" name="email">
</label>4.3 Görsel olarak gizli etiket
Tasarım gereği etiketin yazılı görünmesini istemediğiniz durumlarda (arama kutuları gibi), etiketi görsel olarak gizleyin ama erişilebilirlik ağacında bırakın. display:none kullanmayın — o etiketi tamamen kaybeder.
<form role="search">
<label for="search-q" class="sr-only">Sitede ara</label>
<input id="search-q" type="search" name="q" placeholder="Ara…">
<button type="submit" aria-label="Ara">
<svg aria-hidden="true">...</svg>
</button>
</form>
<style>
.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>4.4 Placeholder etiketin yerini almaz
<input type="email" placeholder="E-posta adresiniz"><label for="email">E-posta adresi</label>
<input
id="email"
type="email"
placeholder="[email protected]"
>4.5 Bir grup form alanı (radyo, checkbox)
Birden fazla alanın aynı soruyu yanıtladığı durumlarda her alanın etiketi yetmez — grubun da bir adı olmalıdır. <fieldset> ve <legend> bu işin standart yoludur.
<fieldset>
<legend>Kargo seçeneği</legend>
<label>
<input type="radio" name="ship" value="standard">
Standart (3-5 gün) — Ücretsiz
</label>
<label>
<input type="radio" name="ship" value="express">
Hızlı (1-2 gün) — 49 TL
</label>
</fieldset>4.6 React Hook Form / Formik
React form kütüphaneleri etiketleri otomatik üretmez; htmlFor (React'te for'un karşılığı) ve id'yi sizin eşleştirmeniz gerekir.
function EmailField() {
const { register, formState: { errors } } = useFormContext();
return (
<div>
<label htmlFor="email">E-posta adresi</label>
<input
id="email"
type="email"
aria-invalid={errors.email ? "true" : "false"}
aria-describedby={errors.email ? "email-error" : undefined}
{...register("email", { required: true })}
/>
{errors.email && (
<p id="email-error" role="alert">
E-posta zorunlu.
</p>
)}
</div>
);
}Hata mesajını da aria-describedby ile alana bağlayın. Böylece kullanıcı alana odaklandığında ekran okuyucu hem etiketi hem hatayı sırayla okur.
5. Zorunlu alanlar ve hata mesajları
Bir alanın zorunlu olduğunu sadece kırmızı bir yıldız ile göstermek yetmez; ekran okuyucu bunu duyamaz. required niteliğini ya da aria-required="true"'yi kullanın.
<label for="phone">
Telefon
<span aria-hidden="true" class="text-red-600">*</span>
</label>
<input
id="phone"
type="tel"
required
aria-describedby="phone-help"
>
<p id="phone-help" class="text-sm text-slate-600">
Zorunlu alan
</p>6. Gözden kaçan durumlar
- Çoklu label: Aynı id'ye iki
<label>bağlamak ekran okuyucuda iki sesli okumaya sebep olur. Tek bir kanonik etiket bırakın. - Submit butonu “Gönder” değil: Genel buton metinleri form bağlamını kaçırır. Daha açıklayıcı olmaları gerekir: “Hesabımı oluştur”, “Bültene abone ol”, “Mesajı gönder” gibi.
- Custom select bileşenleri: Kendi yazdığınız dropdown'lar
<select>değildir;role="combobox"vearia-labelledbyile etiketle birlikte tasarlanmaları gerekir. - Date / time pickerlar: Çoğu özel takvim bileşeni gerçek input'u gizler ve görselleri gösterir. Etiket gerçek input'a bağlanmalıdır.
- Captcha alanı: reCAPTCHA gibi widget'lar kendi iframe'leri içinde çalışır; ana sayfanın label kuralından muaftır ama yine de kendi tarafında erişilebilir olmalıdır.
7. Nasıl test edilir?
- Tab ile gezinti: Mouse'u bırakın. Tab tuşuyla forma girip ekran okuyucuya kulak verin. “Edit” veya “blank” gibi tek kelimelik duyumlar varsa o alanın etiketi eksik.
- Etikete tıklama: Etikete tıkladığınızda ilgili alan odaklanmıyorsa for/id eşleşmesi kırılmıştır.
- axe DevTools / Lighthouse: Form alanlarındaki etiket eksikliklerini anında raporlar.
- Keysonar SEO Tools: Site genelinde tüm formları otomatik tarar, label, link-name ve button-name kurallarını birlikte raporlar.
8. Hızlı kontrol listesi
- Her form alanına bir <label>, aria-label veya aria-labelledby bağlandı.
- for/id eşleşmesi her alanda doğru.
- Placeholder, etiketin yerine değil, örnek değer olarak kullanıldı.
- Görsel olarak gizli etiketler için display:none yerine sr-only sınıfı kullanıldı.
- Radyo/checkbox grupları <fieldset>+<legend> ile sarmalandı.
- Zorunlu alanlar required veya aria-required ile işaretlendi (sadece görsel * yetmez).
- Hata mesajları aria-describedby ile alana bağlandı.
- Submit butonları form bağlamını yansıtan açıklayıcı metin taşıyor.
- Custom dropdown / date picker bileşenleri label/role/aria ile erişilebilir hale getirildi.
9. Referanslar
- WCAG 2.1 SC 1.3.1 — Info and Relationships — Level A
- WCAG 2.1 SC 3.3.2 — Labels or Instructions — Level A
- WCAG 2.1 SC 4.1.2 — Name, Role, Value — Level A
- HTML Living Standard — The label element
- WAI-ARIA Authoring Practices: Form patterns