---
title: 'Klavye Erişimi'
source: 'https://academia.sh/tr/kurslar/frontend-kalitesi/klavye-erisimi'
course: 'Frontend Kalitesi'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# Klavye Erişimi

Klavyeyle çalıştırılabilirliğin gerekleri, odak sırasıyla görsel sıranın karşılaştırılması, odak kuşatmasının tuzağa dönüştüğü sınır ve tek karakterli kısayolların koşulu.

Önceki dersin kural denetimi bir satırı açık bıraktı: düğme rolü bildirilmiş ama odak
almayan öge. Bildirim doğruydu, davranış yoktu. Bu ayrım iki farklı ölçütün konusudur ve
ikincisi bu dersin çıkış noktasıdır.

Klavye erişimi yalnızca klavye kullanan bir azınlığın sorunu değildir. Ekran okuyucular
sayfayı klavye arayüzü üzerinden dolaşır; ses komutuyla ve anahtar denetimiyle çalışan
araçlar da aynı arayüzü kullanır. Klavyeyle çalışmayan bir bileşen bu araçların hiçbirinde
çalışmaz. Buradan pratik bir sonuç çıkar: klavye denetimi, yardımcı teknoloji denemesinden
önce yapılabilecek en ucuz ve en geniş kapsamlı sınamadır.

## Ölçüt Ne İster

2.1.1 ölçütü, içeriğin bütün işlevlerinin bir klavye arayüzü üzerinden çalıştırılabilmesini
ister. İfadenin iki ayrıntısı önemlidir. Birincisi "klavye arayüzü" denmesi: fiziksel bir
klavye değil, o arayüzü kullanan her araç kastedilir. İkincisi, ölçütün tek tek tuş
vuruşları için **belirli bir zamanlama** gerektirmemeyi de istemesidir; belirli bir hızda
basılması gereken bir tuş dizisi ölçütü karşılamaz.

Tek istisna, girdinin yolunun kendisinin bilgi taşıdığı işlevlerdir — serbest el çizimi
gibi. Bir haritayı sürükleyerek kaydırmak bu istisnaya girmez, çünkü aynı işlev kaydırma
düğmeleriyle de sağlanabilir.

2.1.2 ölçütü zincirin diğer ucudur: klavyeyle bir bileşene girilebiliyorsa, aynı arayüzle
çıkılabilmelidir. Bu iki ölçüt birlikte bir bütün kurar; birincisi girişi, ikincisi çıkışı
güvence altına alır.

## Odak Sırası Görsel Sırayla Uyuşmalı

2.4.3 ölçütü, odaklanabilir ögelerin anlamı ve çalıştırılabilirliği koruyan bir sırada
gezilmesini ister. Sekme sırası belge sırasından gelir; görsel sıra sunum katmanından
gelir. İkisi ayrıştığında klavye kullanıcısı ekranda ileri geri sıçrar.

Ölçüm listesi sayfasında bu ayrışma iki nedenle oluşuyor: süzgeç paneli işaretlemeye
sonradan, tablodan sonra eklendi ve sunum katmanı onu tablonun üstüne yerleştirdi; dışa
aktarma düğmesine de "önce gelsin" niyetiyle artı bir `tabindex` değeri yazıldı.

```js
// odaksira.mjs — sekme sirasi ile gorsel siranin karsilastirilmasi (2.4.3)

// gorsel: [satir, sutun] — sunum katmaninin yerlestirdigi yer
const SAYFA = [
  { ad: "a.atlama-bagi", belge: 1, tabindex: null, gorsel: [0, 1] },
  { ad: "button#sirala-tarih", belge: 2, tabindex: null, gorsel: [3, 1] },
  { ad: "button#sirala-sicaklik", belge: 3, tabindex: null, gorsel: [3, 2] },
  { ad: "button.sil[T-01]", belge: 4, tabindex: null, gorsel: [4, 1] },
  { ad: "input#suz-kod", belge: 5, tabindex: null, gorsel: [1, 1] },
  { ad: "input#suz-uyari", belge: 6, tabindex: null, gorsel: [1, 2] },
  { ad: "button#suz", belge: 7, tabindex: null, gorsel: [1, 3] },
  { ad: "button#disa-aktar", belge: 8, tabindex: 1, gorsel: [2, 1] },
];

// M14/K04'te kurulan kural: once artan tabindex, sonra belge sirasi
function sekmeSirasi(ogeler) {
  const pozitif = ogeler.filter((o) => (o.tabindex ?? 0) > 0)
    .sort((a, b) => a.tabindex - b.tabindex || a.belge - b.belge);
  const sifir = ogeler.filter((o) => (o.tabindex ?? 0) === 0 || o.tabindex === null)
    .sort((a, b) => a.belge - b.belge);
  return [...pozitif, ...sifir];
}

const gorselSira = (ogeler) =>
  [...ogeler].sort((a, b) => a.gorsel[0] - b.gorsel[0] || a.gorsel[1] - b.gorsel[1]);

function tersDusenler(ogeler) {
  const sekme = sekmeSirasi(ogeler).map((o) => o.ad);
  const gorsel = gorselSira(ogeler).map((o) => o.ad);
  const ciftler = [];
  for (let i = 0; i < gorsel.length; i++) {
    for (let j = i + 1; j < gorsel.length; j++) {
      if (sekme.indexOf(gorsel[i]) > sekme.indexOf(gorsel[j])) ciftler.push([gorsel[i], gorsel[j]]);
    }
  }
  return ciftler;
}

function rapor(baslik, ogeler) {
  console.log(baslik);
  const sekme = sekmeSirasi(ogeler);
  const gorsel = gorselSira(ogeler);
  console.log("  no  sekme sirasi              gorsel sira");
  for (let i = 0; i < sekme.length; i++) {
    console.log(`  ${String(i + 1).padStart(2)}  ${sekme[i].ad.padEnd(24)}  ${gorsel[i].ad}`);
  }
  const ciftler = tersDusenler(ogeler);
  console.log(`  tersine dusen cift sayisi: ${ciftler.length}`);
  for (const [a, b] of ciftler.slice(0, 4)) {
    console.log(`    "${a}" gorselde once, sekmede "${b}" ogesinden sonra`);
  }
}

rapor("suzgec paneli kaynakta tablodan sonra, tabindex=1 var:", SAYFA);

// Duzeltme: suzgec paneli kaynakta yukari alinir, arti tabindex kaldirilir
const DUZELTILMIS = SAYFA.map((o) => {
  const yeni = { ...o, tabindex: null };
  if (o.ad.startsWith("input#suz") || o.ad === "button#suz") yeni.belge = o.belge - 3;
  if (o.ad === "button#disa-aktar") yeni.belge = 5;
  if (o.ad.startsWith("button#sirala") || o.ad.startsWith("button.sil")) yeni.belge = o.belge + 4;
  return yeni;
});

console.log("");
rapor("suzgec paneli kaynakta yukari alindi, tabindex kaldirildi:", DUZELTILMIS);
```

```
suzgec paneli kaynakta tablodan sonra, tabindex=1 var:
  no  sekme sirasi              gorsel sira
   1  button#disa-aktar         a.atlama-bagi
   2  a.atlama-bagi             input#suz-kod
   3  button#sirala-tarih       input#suz-uyari
   4  button#sirala-sicaklik    button#suz
   5  button.sil[T-01]          button#disa-aktar
   6  input#suz-kod             button#sirala-tarih
   7  input#suz-uyari           button#sirala-sicaklik
   8  button#suz                button.sil[T-01]
  tersine dusen cift sayisi: 13
    "a.atlama-bagi" gorselde once, sekmede "button#disa-aktar" ogesinden sonra
    "input#suz-kod" gorselde once, sekmede "button#disa-aktar" ogesinden sonra
    "input#suz-kod" gorselde once, sekmede "button#sirala-tarih" ogesinden sonra
    "input#suz-kod" gorselde once, sekmede "button#sirala-sicaklik" ogesinden sonra

suzgec paneli kaynakta yukari alindi, tabindex kaldirildi:
  no  sekme sirasi              gorsel sira
   1  a.atlama-bagi             a.atlama-bagi
   2  input#suz-kod             input#suz-kod
   3  input#suz-uyari           input#suz-uyari
   4  button#suz                button#suz
   5  button#disa-aktar         button#disa-aktar
   6  button#sirala-tarih       button#sirala-tarih
   7  button#sirala-sicaklik    button#sirala-sicaklik
   8  button.sil[T-01]          button.sil[T-01]
  tersine dusen cift sayisi: 0
```

Sekiz ögelik bir sayfada on üç ters çift oluşuyor. Sayı, iki hatanın çarpımıdır: artı
`tabindex` değeri tek başına yedi çift üretir, kaynak sırasındaki yanlış yerleşim kalanı.
İkinci rapor düzeltmenin yönünü gösteriyor — sıra, sunum katmanında değil **kaynakta**
düzeltilir. Görsel sırayı sunum katmanı belirlediği için, kaynağı ona uydurmaktan başka
tutarlı bir yol yoktur.

Ters çift sayısı bir denetim ölçüsü olarak kullanılabilir: sıfırdan büyük her değer, en az
bir kullanıcının ekranda geriye sıçradığı anlamına gelir. Ölçüm tablosunun her satırındaki
silme düğmesi sekme sırasını uzatıyorsa, Tarayıcı ve Web Platformu kursunda kurulan dönen
sekme durağı düzeni grubu tek durağa indirir; bu, sırayı bozmadan uzunluğu düşürür.

## Odak Kuşatması ve Tuzak

Ölçüm giriş formu bir pencere olarak açıldığında arkasındaki sayfa erişilebilir kalmamalıdır:
sekme tuşu pencerenin içinde dönmelidir. Bu düzene **odak kuşatması** denir ve yanlış
kurulduğunda 2.1.2 ölçütünü kaldıran bir tuzağa dönüşür. Aşağıdaki betik üç kurulumu bir
durum makinesi olarak modelliyor ve her birinden hangi durumların erişilebilir olduğunu
tarayarak denetliyor.

```js
// tuzak.mjs — odak kusatmasinin durum makinesi ve klavye tuzagi denetimi (2.1.2)

const ARKA = ["input#suz-kod", "button#suz", "button#yeni-olcum", "button.sil[T-01]"];
const PENCERE = ["input#tarih", "input#deger", "button#kaydet", "button#kapat"];
const TUSLAR = ["Tab", "Shift+Tab", "Escape"];

// tur: "kusatmasiz" | "cikissiz" | "dogru"
function makine(tur) {
  const cikisVar = tur !== "cikissiz";
  return (d, tus) => {
    if (!d.acik) {
      const i = ARKA.indexOf(d.odak);
      if (tus === "Tab") return { acik: false, odak: ARKA[(i + 1) % ARKA.length] };
      if (tus === "Shift+Tab") return { acik: false, odak: ARKA[(i + ARKA.length - 1) % ARKA.length] };
      return d;
    }
    const halka = tur === "kusatmasiz" ? [...PENCERE, ...ARKA] : PENCERE;
    const i = halka.indexOf(d.odak);
    if (tus === "Tab") return { acik: true, odak: halka[(i + 1) % halka.length] };
    if (tus === "Shift+Tab") return { acik: true, odak: halka[(i + halka.length - 1) % halka.length] };
    if (tus === "Escape" && cikisVar) return { acik: false, odak: "button#yeni-olcum" };
    return d;
  };
}

const anahtar = (d) => (d.acik ? "acik" : "kapali") + "|" + d.odak;

function denetle(tur) {
  const sonraki = makine(tur);
  const baslangic = { acik: true, odak: PENCERE[0] };
  const gorulen = new Map([[anahtar(baslangic), baslangic]]);
  const kuyruk = [baslangic];
  while (kuyruk.length > 0) {
    const d = kuyruk.shift();
    for (const tus of TUSLAR) {
      const y = sonraki(d, tus);
      if (gorulen.has(anahtar(y))) continue;
      gorulen.set(anahtar(y), y);
      kuyruk.push(y);
    }
  }
  const durumlar = [...gorulen.values()];
  return {
    ulasilan: durumlar.length,
    kacak: durumlar.some((d) => d.acik && ARKA.includes(d.odak)),
    cikis: durumlar.some((d) => !d.acik),
  };
}

console.log("kurulum       ulasilan durum  arka plana kacak  klavyeyle cikis  2.1.2");
for (const tur of ["kusatmasiz", "cikissiz", "dogru"]) {
  const s = denetle(tur);
  console.log(
    tur.padEnd(14) +
      String(s.ulasilan).padStart(14) +
      (s.kacak ? "  var" : "  yok").padEnd(18) +
      (s.cikis ? " var" : " yok").padEnd(17) +
      (s.cikis ? "gecti" : "KALDI"),
  );
}

// Dogru kurulumda tus dizisi
console.log("\ndogru kurulumda odak izi:");
let d = { acik: true, odak: PENCERE[0] };
const sonraki = makine("dogru");
console.log("  " + "baslangic".padEnd(18) + " " + "pencere acik".padEnd(14) + " odak: " + d.odak);
for (const tus of ["Tab", "Tab", "Tab", "Tab", "Shift+Tab", "Escape", "Tab"]) {
  d = sonraki(d, tus);
  console.log(`  ${tus.padEnd(18)} ${(d.acik ? "pencere acik" : "pencere kapali").padEnd(14)} odak: ${d.odak}`);
}
```

```
kurulum       ulasilan durum  arka plana kacak  klavyeyle cikis  2.1.2
kusatmasiz                12  var              var             gecti
cikissiz                   4  yok              yok             KALDI
dogru                      8  yok              var             gecti

dogru kurulumda odak izi:
  baslangic          pencere acik   odak: input#tarih
  Tab                pencere acik   odak: input#deger
  Tab                pencere acik   odak: button#kaydet
  Tab                pencere acik   odak: button#kapat
  Tab                pencere acik   odak: input#tarih
  Shift+Tab          pencere acik   odak: button#kapat
  Escape             pencere kapali odak: button#yeni-olcum
  Tab                pencere kapali odak: button.sil[T-01]
```

Üç satır üç ayrı kusuru ayırıyor. **Kuşatmasız** kurulum ölçütü geçiyor — çıkış vardır —
ama pencere açıkken arka plandaki dört durağa odak kaçıyor. Kullanıcı görmediği, üstü
örtülü ögelere odaklanır; bu bir uygunluk ihlali değil, kullanımı bozan bir kusurdur.
**Çıkışsız** kurulum yalnızca dört duruma ulaşıyor: pencerenin dışına çıkan hiçbir yol yok.
Bu, tanımı gereği klavye tuzağıdır ve 2.1.2 ölçütünü kaldırır. **Doğru** kurulumda sekiz
durumun tamamına ulaşılıyor, kaçak yok ve çıkış var.

Odak izinin son iki satırı ayrı bir kuralı gösteriyor: pencere kapandığında odak, pencereyi
açan düğmeye döner. Dönmezse odak belgenin köküne düşer ve kullanıcı dolaşıma en baştan
başlar. Bu davranış Tarayıcı ve Web Platformu kursunda odağı program taşıdığında doğan
sorumluluk olarak kurulmuştu; burada ölçütün gereği hâline geliyor.

Kuşatmanın kendisi de tek bir yolla kurulmaz. Sekme tuşunu yakalayıp odağı halkada
döndürmek bir yoldur; pencere dışındaki içeriği ağaçtan gizlemek ve etkileşim dışı bırakmak
diğeridir. İkincisi yeğlenir, çünkü kuşatmayı yalnızca sekme tuşuna değil, ekran
okuyucunun kendi gezinme komutlarına karşı da kurar — sekme tuşu yakalanmış bir pencerede
kullanıcı yer imi listesinden arka plana geçebilir.

## Odağın Görünürlüğü

Klavyeyle çalışan bir arayüzde odağın nerede olduğu her an görünmelidir; 2.4.7 ölçütü
bunu ister. Odak göstergesini sunum katmanından kaldırmak, ölçütü tek satırda kaldırır.
Gösterge değiştirilebilir: kalınlığı, rengi ve biçimi tasarım kararıdır. Kaldırılamaz.

İki ek koşul vardır. Gösterge, komşu renklerden ayırt edilebilecek kadar karşıtlık
taşımalıdır — bu, metin dışı kontrast ölçütünün konusudur ve karşıtlık dersinde
hesaplanacaktır. Odaklanan öge ayrıca ekranda **örtülü kalmamalıdır**: sayfanın altına
yapışan bir çubuk ya da üstte duran bir başlık, odaklanan ögeyi tamamen kapatıyorsa
gösterge var ama görünmez demektir.

## Kısayollar

Ölçüm listesinde süzgeç alanına atlamak için tek harflik bir kısayol yazmak akla yakın
gelir. 2.1.4 ölçütü bu tür kısayollara üç koşuldan en az birini dayatır: kısayol
kapatılabilmeli, kullanıcı tarafından bir değiştirici tuşla birlikte kullanılacak biçimde
yeniden atanabilmeli, ya da yalnızca ilgili bileşen odaktayken etkin olmalıdır.

Gerekçe iki kullanım biçiminde görünür. Sesle yazan bir kullanıcının çevresindeki konuşma
metne çevrilebilir ve tek harfler kısayol olarak yorumlanır; klavyede tek tuşa basmakta
zorlanan bir kullanıcı istemeden kısayol tetikler. Ölçüm giriş formunda bu, açıklama
alanına yazarken satırların silinmesi anlamına gelir.

Uygulanabilir karar şudur: tek karakterli kısayollar belge geneline değil, odaktaki
bileşene bağlanır. Ölçüm tablosunda ok tuşlarıyla satır gezinmesi tablo odaktayken çalışır;
tablo odakta değilken aynı tuşlar sayfayı kaydırır. Belge geneline bağlanan kısayollar bir
değiştirici tuşla yazılır ve kullanıcıya kapatma yolu bırakılır.

## Özet

- 2.1.1 bütün işlevlerin klavye arayüzünden ve tuş vuruşu zamanlamasına bağlı olmadan
  çalıştırılmasını ister; 2.1.2 girilen her yerden aynı arayüzle çıkılmasını ister.
- Sekme sırası kaynaktan, görsel sıra sunum katmanından gelir; ayrışma tersine düşen
  çiftler üretir ve düzeltme sunum katmanında değil kaynakta yapılır.
- Artı `tabindex` değerleri bütün sıranın önüne geçerek ters çift sayısını tek başına
  yükseltir.
- Odak kuşatması üç kurulumda farklı sonuç verir: kuşatmasız kurulum arka plana kaçak
  bırakır, çıkışsız kurulum klavye tuzağıdır, doğru kurulumda kapanışta odak açan denetime
  döner.
- Odak göstergesi biçim olarak değiştirilebilir ama kaldırılamaz; ayrıca yeterli karşıtlık
  taşımalı ve başka bir ögeyle örtülmemelidir.
- Tek karakterli kısayollar kapatılabilir, yeniden atanabilir ya da yalnızca bileşen
  odaktayken etkin olmalıdır.

## Sonraki Adım

Bu ders odağın nereye gittiğini hesapladı ama odak oraya vardığında kullanıcının **ne
öğrendiğini** sormadı. Pencere açıldığında yeni bir bağlamın açıldığı nasıl bildirilir?
Süzgeç uygulandığında tabloda kaç satır kaldığı, ekranı görmeyen bir kullanıcıya nasıl
ulaşır? Sonraki ders duyuru düzeneklerini ele alır: canlı bölgelerin kibar ve atılgan
ayrımını, duyuruların kuyruklanmasını ve yer imleriyle başlıkların kurduğu gezinme
düzlemlerini.
