---
title: 'ARIA Kullanımı'
source: 'https://academia.sh/tr/kurslar/frontend-kalitesi/aria-kullanimi'
course: 'Frontend Kalitesi'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# ARIA Kullanımı

Erişilebilirlik ağacına yazılan rol, durum ve özellik bildirimleri; ad hesabının tamamı, birinci kuralın gerekçesi ve kötüye kullanımın ürettiği somut kırılmalar.

Önceki ders yerel ögelerin ağaca ne bildirdiğini kurdu ve bir boşluk bıraktı: tanımlı
karşılığı olmayan bileşenler. Süzgeç paneli açılıp kapanır, ölçüm tablosunun sütunları
sıralanabilir, uyarı bildirimleri sonradan belirir. Bu davranışların hiçbirinin tek bir
öge karşılığı yoktur; ağaçta doğru görünmeleri için eksik bildirimlerin yazılması gerekir.

Bu bildirimleri yazan küme ARIA'dır. Aynı küme, en sık yanlış kullanılan araçtır ve yanlış
kullanıldığında arayüzü hiç bildirim yazılmamış hâlinden daha kullanılmaz yapar. Ders iki
şeyi birlikte kurar: kümenin ne yaptığını ve neyi bozduğunu.

## ARIA Neyi Değiştirir

ARIA bildirimleri **yalnızca erişilebilirlik ağacını** değiştirir. Bir öznitelik yazmak o
ögeye davranış eklemez: odaklanabilir yapmaz, tuş dinlemez, tıklamayı işlemez, görünümünü
değiştirmez.

Bu tek cümle yanlış kullanımların çoğunun kaynağını açıklar. Bir `div` ögesine düğme rolü
yazan kod, ekranda düğme gibi görünen ve ağaçta düğme olarak bildirilen ama boşluk tuşuna
yanıt vermeyen bir şey üretir. Bildirim ile davranış ayrıştığında ortaya çıkan durum,
bildirim hiç yazılmamış olmasından kötüdür: kullanıcıya düğme olduğu söylenmiş, kullanma
yolu verilmemiştir.

## Rol, Durum, Özellik

Küme üç tür bildirim taşır.

**Rol**, ögenin ne olduğunu söyler ve örtük rolün yerine geçer. Bir kez yazılır, çalışma
sırasında değişmesi beklenmez.

**Durum (state)**, ögenin o andaki hâlidir ve etkileşimle değişir: genişletilmiş mi
(`aria-expanded`), işaretli mi (`aria-checked`), seçili mi (`aria-selected`), meşgul mü
(`aria-busy`), geçersiz mi (`aria-invalid`).

**Özellik (property)**, ögenin daha kalıcı niteliğidir: adı (`aria-label`,
`aria-labelledby`), açıklaması (`aria-describedby`), neyi denetlediği (`aria-controls`),
zorunlu olup olmadığı (`aria-required`), hangi sırayla sıralandığı (`aria-sort`).

Ayrım biçimsel değildir: durum bildirimleri **değiştikçe güncellenmek zorundadır**. Panel
açıldığında `aria-expanded` değeri güncellenmiyorsa ağaç, ekranda görünenle çelişen bir
bilgi taşır. Yanlış bilgi, eksik bilgiden daha maliyetlidir.

## Birinci Kural

ARIA'nın yazım kurallarının birincisi şudur: **gereken anlamı taşıyan yerel bir öge varsa
ARIA kullanılmaz.** `div` ögesine düğme rolü yazmak yerine `button` yazılır; liste rolü
yerine `ul` kullanılır; başlık rolü yerine başlık ögesi kullanılır.

Gerekçesi önceki dersteki sayımdır. Yerel öge rolü, adı, odaklanabilirliği, tuş
davranışını, durum bildirimlerini ve form ilişkisini birlikte getirir; rol bildirimi
yalnızca birincisini getirir. Kalan beşinin elle yazılması hem daha uzun hem de her
gerçekleştirimde yeniden hata yapılabilir bir iştir.

Kalan kurallar bu birincinin uzantısıdır: yerel bir ögenin anlamı zorunlu olmadıkça
değiştirilmez; rol yazılan her etkileşimli öge klavyeyle çalıştırılabilir olmalıdır;
odaklanabilir bir öge ağaçtan gizlenmez; etkileşimli her ögenin bir erişilebilir adı
olmalıdır.

## Ad Hesabının Tamamı

Önceki dersin ad hesabı yerel kaynaklarla sınırlıydı. Sıranın en üstünde ARIA kaynakları
durur ve yerel her şeyi bastırır. Aşağıdaki betik tam sırayı uygular ve aynı işaretleme
kümesi üzerinde kural denetimi çalıştırır.

```js
// aria.mjs — ad hesabinin tamami (ARIA kaynaklari en ustte) ve kotuye kullanim denetimi

const ETKILESIMLI = new Set(["button", "link", "checkbox", "textbox", "searchbox", "spinbutton"]);
const ICERIKTEN = new Set(["button", "link", "heading", "cell", "columnheader", "checkbox", "listitem"]);
const ADA_KAPALI = new Set(["generic", "presentation", "none", "paragraph"]);   // yazardan ad almaz

function rol(d) {
  const o = d.oz ?? {};
  if (o.role) return o.role;
  switch (d.oge) {
    case "a": return o.href === undefined ? "generic" : "link";
    case "button": return "button";
    case "input": return o.type === "checkbox" ? "checkbox" : o.type === "search" ? "searchbox" : "textbox";
    case "table": return "table";
    case "section": return o["aria-label"] || o["aria-labelledby"] ? "region" : "generic";
    case "h2": return "heading";
    default: return "generic";
  }
}

const duz = (s) => s.replace(/\s+/g, " ").trim();

// Icerik metni; aria-hidden alt agaci atlanir
function metin(d, gorunur = true) {
  if (typeof d === "string") return d;
  const o = d.oz ?? {};
  if (gorunur && (o["aria-hidden"] === true || o.hidden)) return "";
  if (d.oge === "img") return o.alt ?? "";
  return duz((d.cocuklar ?? []).map((c) => metin(c, gorunur)).join(" "));
}

// 1 aria-labelledby -> 2 aria-label -> 3 yerel kaynak -> 4 icerik -> 5 son care
function ad(d, belge) {
  const o = d.oz ?? {};
  const r = rol(d);
  const yazardanAlir = !ADA_KAPALI.has(r);

  if (yazardanAlir && o["aria-labelledby"]) {
    const parcalar = o["aria-labelledby"].split(" ").map((k) => belge[k]).filter(Boolean);
    // basvurulan dugum gizli olsa da metni kullanilir
    if (parcalar.length > 0) return [duz(parcalar.map((p) => metin(p, false)).join(" ")), "aria-labelledby"];
  }
  if (yazardanAlir && o["aria-label"]) return [duz(o["aria-label"]), "aria-label"];
  if (["input", "select", "textarea"].includes(d.oge) && belge["label:" + o.id]) {
    return [metin(belge["label:" + o.id]), "label ogesi"];
  }
  if (ICERIKTEN.has(r)) {
    const m = metin(d);
    if (m) return [m, "icerik metni"];
  }
  return ["", "ad yok"];
}

// --- Kuzey Yamac: suzgec paneli ve olcum tablosu bilesenleri -----------------
const BELGE = {
  "baslik": { oge: "h2", cocuklar: ["Süzgeç"] },
  "sayi": { oge: "span", cocuklar: ["3 ölçüt"] },
  "gizli-ad": { oge: "span", oz: { hidden: true }, cocuklar: ["Ölçüm satırını sil"] },
  "label:kod": { oge: "label", cocuklar: ["İstasyon kodu"] },
};

const ORNEK = [
  ['<button>Süz</button>',
   { oge: "button", cocuklar: ["Süz"], odaklanir: true }],
  ['<button aria-label="Kaydet">Ölçümü sil</button>',
   { oge: "button", oz: { "aria-label": "Kaydet" }, cocuklar: ["Ölçümü sil"], odaklanir: true }],
  ['<button aria-labelledby="baslik sayi">Aç</button>',
   { oge: "button", oz: { "aria-labelledby": "baslik sayi" }, cocuklar: ["Aç"], odaklanir: true }],
  ['<button aria-labelledby="yok-boyle">Sil</button>',
   { oge: "button", oz: { "aria-labelledby": "yok-boyle" }, cocuklar: ["Sil"], odaklanir: true }],
  ['<div role="button" tabindex="0">Sil</div>',
   { oge: "div", oz: { role: "button" }, cocuklar: ["Sil"], odaklanir: true }],
  ['<div role="button">Sil</div>',
   { oge: "div", oz: { role: "button" }, cocuklar: ["Sil"], odaklanir: false }],
  ['<input id="kod" aria-label="Kod"> + label',
   { oge: "input", oz: { id: "kod", "aria-label": "Kod" }, odaklanir: true }],
  ['<div aria-label="Süzgeç paneli">…</div>',
   { oge: "div", oz: { "aria-label": "Süzgeç paneli" }, cocuklar: ["3 ölçüt"], odaklanir: false }],
  ['<section aria-label="Süzgeç paneli">…',
   { oge: "section", oz: { "aria-label": "Süzgeç paneli" }, cocuklar: ["3 ölçüt"], odaklanir: false }],
  ['<button aria-hidden="true">Dışa aktar</button>',
   { oge: "button", oz: { "aria-hidden": true }, cocuklar: ["Dışa aktar"], odaklanir: true }],
  ['<button aria-labelledby="gizli-ad">×</button>',
   { oge: "button", oz: { "aria-labelledby": "gizli-ad" }, cocuklar: ["×"], odaklanir: true }],
  ['<table role="presentation">…</table>',
   { oge: "table", oz: { role: "presentation" }, cocuklar: ["Sıcaklık"], odaklanir: false }],
];

console.log("isaretleme".padEnd(51) + "rol".padEnd(14) + "erisilebilir ad".padEnd(22) + "kaynak");
for (const [kaynak, d] of ORNEK) {
  const [a, nereden] = ad(d, BELGE);
  console.log(kaynak.padEnd(51) + rol(d).padEnd(14) + (a === "" ? "(bos)" : '"' + a + '"').padEnd(22) + nereden);
}

console.log("\nkural denetimi:");
let sayi = 0;
for (const [kaynak, d] of ORNEK) {
  const o = d.oz ?? {};
  const r = rol(d);
  const [a] = ad(d, BELGE);
  const gorunen = metin(d);
  const bulgular = [];
  if (o["aria-hidden"] === true && d.odaklanir) bulgular.push("odaklanabilir oge agactan gizlendi");
  if (o["aria-labelledby"] && !o["aria-labelledby"].split(" ").some((k) => BELGE[k]))
    bulgular.push("aria-labelledby gecersiz kimlige basvuruyor");
  if ((o["aria-label"] || o["aria-labelledby"]) && ADA_KAPALI.has(r))
    bulgular.push("yazardan ad almayan rolde ad bildirimi yok sayildi");
  if (ETKILESIMLI.has(r) && !d.odaklanir) bulgular.push("etkilesimli rol odaklanabilir degil");
  if (ETKILESIMLI.has(r) && a === "") bulgular.push("etkilesimli oge adsiz");
  if (["presentation", "none"].includes(o.role) && ["table", "ul", "ol", "li"].includes(d.oge))
    bulgular.push("yerel yapi anlami kaldirildi: satir ve sutun iliskileri agacta yok");
  if (ETKILESIMLI.has(r) && /\p{L}/u.test(gorunen) && a &&
      !a.toLocaleLowerCase("tr").includes(gorunen.toLocaleLowerCase("tr")))
    bulgular.push('gorunen metin ("' + gorunen + '") erisilebilir adin icinde yok');
  for (const b of bulgular) {
    sayi++;
    console.log("  " + kaynak.padEnd(51) + b);
  }
}
console.log(`\n${ORNEK.length} ornekte ${sayi} bulgu`);
```

```
isaretleme                                         rol           erisilebilir ad       kaynak
<button>Süz</button>                               button        "Süz"                 icerik metni
<button aria-label="Kaydet">Ölçümü sil</button>    button        "Kaydet"              aria-label
<button aria-labelledby="baslik sayi">Aç</button>  button        "Süzgeç 3 ölçüt"      aria-labelledby
<button aria-labelledby="yok-boyle">Sil</button>   button        "Sil"                 icerik metni
<div role="button" tabindex="0">Sil</div>          button        "Sil"                 icerik metni
<div role="button">Sil</div>                       button        "Sil"                 icerik metni
<input id="kod" aria-label="Kod"> + label          textbox       "Kod"                 aria-label
<div aria-label="Süzgeç paneli">…</div>            generic       (bos)                 ad yok
<section aria-label="Süzgeç paneli">…              region        "Süzgeç paneli"       aria-label
<button aria-hidden="true">Dışa aktar</button>     button        (bos)                 ad yok
<button aria-labelledby="gizli-ad">×</button>      button        "Ölçüm satırını sil"  aria-labelledby
<table role="presentation">…</table>               presentation  (bos)                 ad yok

kural denetimi:
  <button aria-label="Kaydet">Ölçümü sil</button>    gorunen metin ("Ölçümü sil") erisilebilir adin icinde yok
  <button aria-labelledby="baslik sayi">Aç</button>  gorunen metin ("Aç") erisilebilir adin icinde yok
  <button aria-labelledby="yok-boyle">Sil</button>   aria-labelledby gecersiz kimlige basvuruyor
  <div role="button">Sil</div>                       etkilesimli rol odaklanabilir degil
  <div aria-label="Süzgeç paneli">…</div>            yazardan ad almayan rolde ad bildirimi yok sayildi
  <button aria-hidden="true">Dışa aktar</button>     odaklanabilir oge agactan gizlendi
  <button aria-hidden="true">Dışa aktar</button>     etkilesimli oge adsiz
  <table role="presentation">…</table>               yerel yapi anlami kaldirildi: satir ve sutun iliskileri agacta yok

12 ornekte 8 bulgu
```

Betikteki `odaklanir` alanı, ögenin gerçekten odak alıp almadığını temsil eder; `div`
satırlarında `tabindex` yazılıp yazılmamasının karşılığıdır.

## Bulguların Okunması

**Ad bildirimi görünen metni bastırır.** İkinci satırda düğmenin üzerinde "Ölçümü sil"
yazıyor, ağaçta adı "Kaydet". Konuşarak arayüz kullanan bir kişi gördüğü metni söyler ve
söylediği ad hiçbir ögeye uymaz. 2.5.3 ölçütü tam da bunu yasaklar: görünen etiket metni,
erişilebilir adın içinde geçmelidir. Aynı kırılma yedinci satırda daha sinsi biçimde
oluşuyor — alanın bir `label` ögesi var, ama `aria-label` bildirimi onu bastırdığı için
ekranda "İstasyon kodu" yazan alanın adı "Kod".

**Geçersiz başvuru sessizce düşer.** Dördüncü satırda `aria-labelledby` var olmayan bir
kimliğe işaret ediyor; hesap o adımı atlıyor ve ad içerik metninden geliyor. Sonuç bu
örnekte zararsız görünür, ancak asıl bildirim yok sayılmıştır. İçeriği olmayan bir simge
düğmesinde aynı hata adsız bir düğme bırakır.

**Gizli düğüme yapılan başvuru geçerlidir.** On birinci satır bunu gösteriyor: gizlenmiş
bir `span` içindeki metin, ona başvuran düğmenin adı oluyor. Ekranda yalnız bir çarpı
işareti duran silme düğmesine ad vermenin tanımlı yolu budur.

**Yazardan ad almayan roller vardır.** Sekizinci satırda bir `div` ögesine yazılan
`aria-label` yok sayılıyor; dokuzuncu satırda aynı bildirim `section` ögesine yazıldığında
hem ögenin rolünü bölge yapıyor hem de ad üretiyor. Ad bildirimi ögeye değil, ögenin
rolüne yazılır.

**Odaklanabilir bir öge ağaçtan gizlenemez.** Onuncu satır iki bulgu birden üretiyor:
düğme odak alıyor ama ağaçta yok. Klavyeyle gezen bir kullanıcı odağın kaybolduğu bir
durakla karşılaşır; sekme tuşu bir yere gider, orada hiçbir şey bildirilmez.

**Yapı anlamını kaldırmak veriyi kaldırır.** Son satırda tablonun rolü sunumsala
çevrilmiş. Düzen amacıyla kullanılan bir tabloda bu doğru karardır; veri taşıyan ölçüm
tablosunda satır ve sütun ilişkileri ağaçtan tamamen düşer ve hücreler arasında dolaşan
kullanıcı hangi sütunda olduğunu öğrenemez.

## Süzgeç Panelinde Doğru Kullanım

Aşağıdaki işaretleme, yerel ögeyle karşılanmayan iki yapıyı kuralına uygun bildirir:
açılıp kapanan panel ve sıralanabilir sütun.

```html
<h2 id="suzgec-basligi">Süzgeç</h2>
<button aria-expanded="false" aria-controls="suzgec-govde">
  Süzgeç ölçütleri
</button>
<div id="suzgec-govde" hidden>
  <label for="suz-kod">İstasyon kodu</label>
  <input id="suz-kod" type="search">
  <p><label><input type="checkbox" name="uyari"> Yalnız uyarılı ölçümler</label></p>
</div>

<table aria-labelledby="tablo-basligi">
  <caption id="tablo-basligi">Günlük ölçümler</caption>
  <thead>
    <tr>
      <th scope="col" aria-sort="descending">
        <button>Tarih</button>
      </th>
      <th scope="col" aria-sort="none"><button>Sıcaklık</button></th>
    </tr>
  </thead>
</table>
```

Üç karar dikkat gerektirir. Açma denetimi bir `button` ögesidir; rol yazılmamıştır, çünkü
gereken rol zaten vardır. `aria-expanded` durumu panel her açılıp kapandığında
güncellenmelidir; sabit kalırsa ağaç yanlış bilgi taşır. Sıralama durumu başlık hücresine
yazılır, hücrenin içindeki düğmeye değil: sıralanan şey sütundur, düğme yalnızca sıralamayı
başlatan denetimdir.

## Özet

- ARIA bildirimleri yalnızca erişilebilirlik ağacını değiştirir; davranış, odaklanabilirlik
  ya da görünüm eklemez.
- Rol ögenin ne olduğunu, durum o andaki hâlini, özellik daha kalıcı niteliğini bildirir;
  durum bildirimleri etkileşimle birlikte güncellenmek zorundadır.
- Birinci kural, gereken anlamı taşıyan yerel öge varken ARIA kullanmamaktır; yerel öge
  rolle birlikte ad, odak, tuş davranışı ve durumları da getirir.
- Ad hesabında ARIA kaynakları yerel kaynakları bastırır; görünen metinle erişilebilir adın
  ayrışması 2.5.3 ölçütünü kaldırır.
- Geçersiz kimlik başvurusu sessizce düşer, gizli düğüme yapılan başvuru geçerlidir ve
  `aria-label` yazardan ad almayan rollerde yok sayılır.
- Odaklanabilir bir ögeyi ağaçtan gizlemek ve veri tablosunun yapı anlamını kaldırmak,
  bildirim yazmamaktan daha ağır kırılmalar üretir.

## Sonraki Adım

Kural denetiminin bir satırı bu derste açılmadan bırakıldı: düğme rolü yazılmış ama odak
almayan öge. Bildirimin doğru olması yetmez, ögenin klavyeyle çalıştırılabilir olması da
gerekir ve bu iki gereklilik ayrı ölçütlerde tanımlıdır. Sonraki ders arayüzün tamamına
klavyeden bakar: hangi ögelerin sekme sırasında bulunduğunu, sıranın nasıl hesaplandığını,
açılan bir panelin odağı nasıl kuşattığını ve kuşatmanın hangi noktada tuzağa dönüştüğünü
ele alır.
