Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Semantik JSX
- 2.1 <div onClick> — tugma emas
- 2.2 Qaysi teg qachon
- 3. Har boshqaruv elementining nomi
- 3.1 Nom (accessible name) nima
- 3.2 Maydonlar: <label htmlFor> va useId
- 3.3 Ikonka tugma: aria-label
- 3.4 Rasmlar: alt
- 4. aria-* React'da
- 4.1 Yozilishi
- 4.2 Forma xatosi: aria-invalid + aria-describedby
- 4.3 ARIA kerak bo'lmagan joy
- 5. Avtomatik tekshiruv — uch qavat
- 5.1 Lint: eslint-plugin-jsx-a11y
- 5.2 Brauzerda: axe
- 5.3 Qo'lda: Bobur va Malika
- 6. Vazifalar qadami: axe va Malika daraxti
- 6.1 Olti holat, axe-core 4.13.0
- 6.2 Malika "ko'radigan" daraxt
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): nima eshitiladi?
- 2-mashq (o'rta): div larni tuzating
- 3-mashq (qiyin): akkordeon
- 4-mashq: Vazifalar qadami
- 9. Real ishda
- Xulosa
- Manbalar
React'da accessibility asoslari: semantik JSX, label, aria va avtomatik tekshiruv
Qisqacha: React accessibility'ni o'zi buzmaydi ham, tuzatmaydi ham — u siz yozgan HTML'ni chizadi. Shuning uchun asosiy qoida HTML'dagidek: to'g'ri teg (
<button>,<nav>,<label>), har boshqaruv elementining nomi bor, rasmlardaalt, holat esaaria-*bilan (aria-expanded,aria-invalid). JSX'dagi farqlar kichik:foro'rnigahtmlFor,aria-*esa chiziqcha bilan qoladi. Xatolarni uch qavatda ushlaysiz: kod yozishda (eslint-plugin-jsx-a11y), brauzerda (axe) va qo'lda — klaviatura va ekran o'quvchi (NVDA) bilan.
Bu darsda
<div onClick>nega tugma emasligini jonli ko'rasiz va uni<button>bilan almashtirasiz.- Har boshqaruv elementiga nom berasiz:
<label htmlFor>,useId,aria-label,aria-labelledby. aria-*atributlarini React'da to'g'ri yozasiz va qachon ARIA kerak emasligini bilasiz.- Uch qavatli tekshiruvni sozlaysiz: lint, axe va qo'lda (Bobur — klaviatura, Malika — NVDA).
vazifalarni axe-core 4.13.0 bilan olti holatda o'lchaysiz va Malika "ko'radigan" daraxtni tekshirasiz.
Oldin bilishingiz kerak: Accessibility daraxti, ARIA asoslari, Formalar I, useId.
1. Nega bu kerak?
Kursdagi ikki do'stimizni eslang. Malika ko'rmaydi — kompyuterda NVDA ekran o'quvchisi bilan ishlaydi, u sahifani ovoz chiqarib o'qiydi. Bobur sichqoncha ishlata olmaydi — faqat klaviatura: Tab, Enter, Space, strelkalar.
Sardor «Bahor» buyurtma sahifasini React'da yozdi. Ko'rinishi chiroyli. Malika sahifani ochdi va eshitdi: "tugma, tugma, tahrirlanadigan matn, tugma". Qaysi tugma nima qiladi — noma'lum. Bobur esa Tab bosib, "Savatga qo'shish" ga umuman yetib bormadi: u <div onClick> edi.
HTML qismida accessibility (qisqasi a11y — "a", 11 harf, "y") nima ekanini va kimga kerakligini o'rgandik. React'da qoidalar o'sha. Lekin komponentlar yangi xavf qo'shadi: bitta noto'g'ri Button komponenti butun ilovadagi 50 ta tugmani buzadi. Teskarisi ham rost — bitta to'g'ri komponent 50 joyni tuzatadi.
Hayotiy o'xshatish: binoga kirish. Zinapoya hamma uchun, pandus esa nogironlar aravachasi uchun. Lekin pandus faqat ular uchun emas: aravacha, chamadon, velosiped bilan kelganlarga ham qulay. Accessibility ham shunday: klaviatura bilan tez ishlaydigan dasturchi, telefonda bir qo'li band odam, quyosh ostida ekranni zo'rg'a ko'radigan haydovchi — hammasi foyda ko'radi.
2. Semantik JSX
2.1 <div onClick> — tugma emas
Eng ko'p uchraydigan a11y xatosi. Ikkalasini yonma-yon sinaymiz:
import { useRef } from "react";
export default function App() {
const divRef = useRef(null);
const buttonRef = useRef(null);
function check() {
divRef.current.focus();
const divOk = document.activeElement === divRef.current;
console.log("div fokusda:", divOk);
buttonRef.current.focus();
const ok = document.activeElement === buttonRef.current;
console.log("button fokusda:", ok);
}
return (
<div>
<div ref={divRef} onClick={() => console.log("savatga")}>
Savatga (div)
</div>
<button ref={buttonRef} onClick={() => console.log("savatga")}>
Savatga (button)
</button>
<button onClick={check}>Tekshirish</button>
</div>
);
}Konsolda:
div fokusda: false
button fokusda: trueEkranda:
Savatga (div)
Savatga (button)Tekshirishdiv ga fokus tushmaydi — Bobur Tab bilan unga yetib bormaydi. Bu bitta muammo. To'liq ro'yxat:
<div onClick> |
<button> |
|
|---|---|---|
| Tab bilan fokus | ||
| Enter va Space bosish | (brauzer click beradi) |
|
| Ekran o'quvchi roli | "matn" | "tugma" |
disabled |
||
| formada | — | type bo'yicha yuboradi |
div ni "tuzatish" mumkin: role="button", tabIndex={0}, onKeyDown da Enter va Space. Lekin bu — beshta qo'shimcha narsa, bittasini unutish oson. <button> ularning hammasini bepul beradi. ARIA'ning birinchi qoidasi shu: native element bor bo'lsa — uni ishlating (ARIA asoslari).
2.2 Qaysi teg qachon
| Ma'no | Teg | Noto'g'ri tanlov |
|---|---|---|
| amal bajaradi | <button> |
<div onClick>, <a href="#"> |
| boshqa sahifaga olib boradi | <a href> |
<button onClick={navigate}> |
| sahifa qismlari | <header>, <nav>, <main>, <footer> |
<div className="header"> |
| sarlavhalar daraxti | <h1>…<h6> (tartib bilan) |
<p className="katta"> |
| ro'yxat | <ul> + <li> |
<div> lar |
| jadval ma'lumot | <table> + <th scope> |
<div> grid |
Bir nozik joy: React komponenti ko'pincha <div> qaytaradi, chunki "nimadir o'rash kerak". Har qo'shimcha <div> zararsiz, lekin semantikasiz. Ortiqcha o'ram kerak bo'lmasa — <>…</> (Fragment). <ul> ichida <li> dan boshqa narsa (masalan, <div> o'ram) — ekran o'quvchi uchun ro'yxat buziladi. axe buni list qoidasi bilan ushlaydi.
3. Har boshqaruv elementining nomi
3.1 Nom (accessible name) nima
Accessibility daraxti darsida ko'rdik: brauzer DOM'dan alohida daraxt quradi, ekran o'quvchi uni o'qiydi. Har tugun uchun uch narsa: rol (tugma, maydon), nom (nimaligi), holat (bosilgan, ochiq, xato). Rol tegdan keladi. Nom esa siz bergan narsadan:
flowchart LR
J["JSX: <button>Saqlash</button>"] --> D["DOM"]
D --> A["a11y daraxti: rol, nom, holat"]
A --> N["NVDA: Saqlash, tugma"]
A --> X["axe: nomi bormi?"]Diagrammaning o'ng tomoniga qarang: Malika ham, axe ham bir xil daraxtni o'qiydi. axe "nomi yo'q" desa — Malika ham hech narsa eshitmaydi. Nom manbalari, ustunlik tartibida:
aria-labelledby— boshqa elementning matni (id bo'yicha).aria-label— to'g'ridan-to'g'ri satr.<label>(maydonlar uchun),alt(rasmlar uchun).- Elementning ichidagi matn (tugma, havola).
title— oxirgi chora, ishonchsiz.
3.2 Maydonlar: <label htmlFor> va useId
JSX'da for — JavaScript'ning band so'zi, shuning uchun atribut nomi htmlFor (JSX aslida JavaScript). Komponent bir sahifada bir necha marta ishlatilsa, id takrorlanadi — useId buni hal qiladi:
import { useId } from "react";
function Field({ label }) {
const id = useId();
return (
<p>
<label htmlFor={id}>{label}</label> <input id={id} />
</p>
);
}
export default function App() {
function check() {
for (const input of document.querySelectorAll("input")) {
console.log(input.id, "→", input.labels[0]?.textContent);
}
}
return (
<form>
<Field label="Ismingiz" />
<Field label="Telefon" />
<button type="button" onClick={check}>
Tekshirish
</button>
</form>
);
}Konsolda:
_r_0_ → Ismingiz
_r_1_ → TelefonEkranda:
Ismingiz
Telefon
Tekshirishinput.labels — brauzer o'zi bog'lagan yorliqlar. Ikkalasida ham to'g'ri yorliq — useId ikki xil id berdi. Yorliqni bosganda maydon fokuslanadi: telefonda kichik maydonga tegish qiyin, katta yorliqqa — oson.
placeholder — yorliq emas. U yozish boshlanganda yo'qoladi, kontrasti past, ba'zi ekran o'quvchilar uni o'qimaydi. vazifalar dagi kirish formasida (42-dars) har maydonda <label> bor.
3.3 Ikonka tugma: aria-label
Matnsiz tugma (, , ) — eng ko'p "nomsiz" xato. Ikki yo'l:
// ✅ aria-label — nom ko'rinmaydi, faqat eshitiladi
<button aria-label="Savatni yopish" onClick={close}>✕</button>
// ✅ ko'rinmas matn — CSS bilan yashirilgan, lekin daraxtda bor
<button onClick={close}>
<span aria-hidden="true">✕</span>
<span className="sr-only">Savatni yopish</span>
</button>Ikkinchi variantda aria-hidden="true" "" belgisini daraxtdan olib tashlaydi. Aks holda NVDA uni "ko'paytirish belgisi" deb o'qishi mumkin. Klass sr-only — ekranda ko'rinmaydigan, lekin o'qiladigan matn klassi (CSS'da accessibility). React + TS IV da vazifalar dagi Tugma ga "nomi bo'lishi shart" qoidasini tur bilan qo'ydik: matnsiz va aria-label siz tugma — tsc xatosi.
3.4 Rasmlar: alt
| Rasm | alt |
|---|---|
| ma'lumot beradi (taom surati menyuda) | alt="Osh — sabzi, go'sht, guruch" |
| bezak (fon naqshi) | alt="" — bo'sh, lekin bor |
| tugma ichida yagona narsa | amalni yozing: alt="Savatga qo'shish" |
alt umuman yo'q bo'lsa, ekran o'quvchi fayl nomini o'qiydi: "osh underscore 2 dot webp". alt="" esa "bu rasmni o'tkazib yubor" degani. React alt siz <img> uchun ogohlantirish bermaydi — buni lint ushlaydi (keyingi bo'lim).
Tekshirib ko'ring: Tugmada ham matn ("Saqlash"), ham
aria-label="Buyurtmani saqlash"bor. Malika nimani eshitadi?
Javob
"Buyurtmani saqlash, tugma". aria-label ichki matndan ustun («Nom (accessible name) nima» bo'limidagi tartib). Bu qulay, lekin xavfli: ko'zi ko'radigan foydalanuvchi "Saqlash" ni ko'radi, ovozli boshqaruv (Voice Control) ishlatadigan esa "Saqlash" deb aytadi — va nom mos kelmasa, tugma topilmaydi. Qoida: aria-label ko'rinadigan matnni o'z ichiga olsin.
4. aria-* React'da
4.1 Yozilishi
React'da deyarli hamma atribut camelCase (tabIndex, className, onClick). aria-* va data-* — istisno: ular HTML'dagidek, chiziqcha bilan. Boolean qiymat satrga aylanadi:
import { useState } from "react";
export default function App() {
const [open, setOpen] = useState(false);
return (
<div>
<button
aria-expanded={open}
aria-controls="menyu-royxat"
onClick={() => {
setOpen(!open);
const b = document.querySelector("button");
const oldin = b.getAttribute("aria-expanded");
console.log("bosishdan oldin:", oldin);
}}
>
Menyu
</button>
<ul id="menyu-royxat" hidden={!open}>
<li>Osh</li>
<li>Manti</li>
</ul>
</div>
);
}Konsolda:
bosishdan oldin: falseEkranda:
Menyu
Osh
Mantiaria-expanded={false} DOM'da "false" satri bo'lib yozildi — ARIA aynan shuni kutadi. Bosilgach — "true" va ro'yxat ochildi. Malika "Menyu, tugma, yig'ilgan" va keyin "yoyilgan" deb eshitadi.
Eng ko'p ishlatiladigan holat atributlari:
| Atribut | Ma'no | Misol |
|---|---|---|
aria-expanded |
ochiq/yopiq | menyu, akkordeon tugmasi |
aria-pressed |
bosilgan/bosilmagan | "Sevimli" tugmasi |
aria-invalid |
maydonda xato | forma validatsiyasi |
aria-describedby |
qo'shimcha izoh id'si | maydon ostidagi xato matni |
aria-current="page" |
joriy sahifa | navigatsiyadagi havola |
aria-hidden="true" |
daraxtdan yashirish | bezak ikonka |
aria-busy |
yangilanmoqda | ro'yxat yuklanayotganda |
aria-busy ni vazifalar da ko'rgansiz: qidiruvda ro'yxat kechiktirib chizilayotganda (useDeferredValue).
4.2 Forma xatosi: aria-invalid + aria-describedby
Xato faqat qizil rang bilan ko'rsatilsa, Malika uni bilmaydi. To'g'ri yo'l — maydonni xato bilan bog'lash:
import { useId, useState } from "react";
export default function App() {
const id = useId();
const [error, setError] = useState(null);
return (
<form
noValidate
onSubmit={(e) => {
e.preventDefault();
setError("Telefon raqamini yozing");
}}
>
<label htmlFor={id}>Telefon</label>{" "}
<input
id={id}
aria-invalid={error !== null ? true : undefined}
aria-describedby={error !== null ? `${id}-xato` : undefined}
/>
<p id={`${id}-xato`} role="status">
{error}
</p>
<button>Buyurtma</button>
</form>
);
}Ekranda:
Telefon
Telefon raqamini yozing
BuyurtmaUch narsa birga ishlaydi:
aria-invalid— maydon holati "xato". Xato yo'q bo'lsa —undefined, ya'ni atribut umuman yo'q (falseemas — tozaroq).aria-describedby— maydonga fokus tushganda Malika nomdan keyin xato matnini ham eshitadi: "Telefon, tahrirlanadigan matn, xato, Telefon raqamini yozing".role="status"—<p>"jonli hudud" (live region): matn o'zgarganda NVDA uni o'zi o'qiydi, fokus siljimasa ham.<p>doim DOM'da turadi, faqat ichi o'zgaradi — jonli hudud oldindan bo'lishi kerak. Batafsil — keyingi darsda.
4.3 ARIA kerak bo'lmagan joy
ARIA — kuchli, lekin xavfli. Noto'g'ri ARIA, ARIA yo'qligidan yomonroq: <button role="link"> — ekran o'quvchi "havola" deydi, Enter bosilsa esa tugma ishlaydi. Uch qoida:
- Native element bo'lsa — ARIA emas, teg:
<button>,<nav>,<dialog>, details tegi. - Native semantikani o'zgartirmang:
<h2 role="button">— yo'q,<h2><button>…</button></h2>— ha. - Hamma interaktiv ARIA element klaviatura bilan ishlashi shart.
role="menu"yozdingizmi — strelkalar ham sizning ishingiz.vazifalardagiMenyu(Headless komponentlar) shuni APG naqshi bo'yicha qilgan.
5. Avtomatik tekshiruv — uch qavat
5.1 Lint: eslint-plugin-jsx-a11y
Eng erta qavat — kod yozayotganda. eslint-plugin-jsx-a11y JSX'ni statik tekshiradi: alt siz <img>, onClick li <div> (klaviaturasiz), <label> siz maydon, noto'g'ri aria-* nomi. Flat config'da (ESLint I) ulanishi:
// eslint.config.js — o'z loyihangizda
import jsxA11y from "eslint-plugin-jsx-a11y";
export default [
// …
{
files: ["src/**/*.{jsx,tsx}"],
...jsxA11y.flatConfigs.recommended,
},
];O'rnatishdan oldin paketning peerDependencies ida ESLint versiyangiz borligini tekshiring (Paket tanlash va baholash). Bu yerda bu muhim. 2026-yil oktyabr holatida npm view eslint-plugin-jsx-a11y version peerDependencies shuni ko'rsatadi: oxirgi versiya — 6.10.2 (2024-yil oktyabr), ruxsat etilgan ESLint — ^3 … ^9. ESLint 10 ro'yxatda yo'q. vazifalar dagi ESLint 10.12.0 bilan npm install to'xtaydi: ERESOLVE unable to resolve dependency tree (sinab ko'rdik). Xatoni --legacy-peer-deps bilan chetlab o'tish mumkin, lekin plagin ESLint 10 da ishlashiga hech kim kafolat bermaydi. Shuning uchun avval paketning GitHub sahifasida yangi versiya chiqqanini tekshiring. Lint'ning chegarasi: u faqat kodni ko'radi. <Tugma> ichida nima chizilishini, aria-labelledby dagi id haqiqatan borligini, rang kontrastini — bilmaydi.
vazifalar da bu plagin yo'q. U yerdagi himoya boshqa: tur darajasida (Tugma nomi — 37-dars) va ish vaqtida — axe va klaviatura ssenariysi. Ikki yondashuv bir-birini to'ldiradi.
5.2 Brauzerda: axe
axe-core — Deque kompaniyasining ochiq kodli a11y tekshiruvchisi. U tayyor sahifadagi haqiqiy a11y daraxtini tekshiradi: nom, kontrast, sarlavhalar tartibi, ARIA'ning to'g'riligi. Uni uch xil ishlatasiz:
- axe DevTools — Chrome kengaytmasi: DevTools'da "axe" paneli, "Scan" tugmasi.
- Lighthouse — Chrome'ga o'rnatilgan, "Accessibility" bo'limi — ichida o'sha axe-core.
- Testlarda — Playwright yoki Vitest bilan avtomatik (Accessibility testlari).
axe ham hammasini topmaydi. Odatda aytiladi: avtomatik vositalar a11y muammolarining taxminan uchdan birini ushlaydi. Qolgani — mantiq: fokus qayerga ketdi, xabar tushunarlimi, tartib to'g'rimi. Buni faqat odam sinaydi.
5.3 Qo'lda: Bobur va Malika
Klaviatura (Bobur). Sichqonchani chetga suring. Tab — oldinga, Shift+Tab — orqaga, Enter/Space — bosish, Esc — yopish. Har qadamda so'rang: fokus ko'rinyaptimi? Tartib mantiqiymi? Hamma joyga yetib bordimmi? Dialogdan chiqa olamanmi?
Ekran o'quvchi (Malika). Windows'da — NVDA (bepul, nvaccess.org). Asosiy tugmalar:
| Tugma | Nima qiladi |
|---|---|
| Ctrl | o'qishni to'xtatadi |
| ↓ | keyingi qatorni o'qiydi |
| Tab | keyingi interaktiv element |
| H | keyingi sarlavha |
| Insert+F7 | sahifadagi havolalar, sarlavhalar ro'yxati |
Birinchi marta g'alati tuyulishi tabiiy — NVDA tez gapiradi. 10 daqiqa yetadi: o'z sahifangizni ko'zingizni yumib "tinglang". macOS'da — VoiceOver (Cmd+F5), telefonda — TalkBack (Android) va VoiceOver (iOS).
Tekshirib ko'ring: axe 0 buzilish ko'rsatdi. Sahifa accessible deyish mumkinmi?
Javob
Yo'q, faqat "avtomatik topiladigan xatolar yo'q" deyish mumkin. axe fokus tartibini, dialog yopilganda fokus qaytishini, xabar ma'nosini, klaviatura tuzog'ini (fokus chiqolmaydigan joy) tekshirmaydi. Shuning uchun vazifalar da axe yonida klaviatura ssenariysi ham bor — «Vazifalar qadami» bo'limida.
6. Vazifalar qadami: axe va Malika daraxti
6.1 Olti holat, axe-core 4.13.0
feat/a11y-otish branch'i — ikki darslik qadam, commit keyingi darsda. Avval o'lchov: bugungi holat qanchalik accessible?
Kanon axe-core 4.13.0 ni (Lighthouse 13.5.0 ichidagi o'sha versiya) build qilingan ilovaga Chrome 155 orqali yukladi. Qoidalar to'plami — WCAG 2.0/2.1/2.2 A va AA hamda "best practice". Olti holat tekshirildi — har birida axe sahifaning o'sha paytdagi ko'rinishini oladi:
- asosiy (3 vazifa)36 qoida
- tahrir rejimi36 qoida
- o'chirish dialogi17 qoida
- statistika + menyu44 qoida
- qorong'i tema36 qoida
- 300 vazifa (virtual)37 qoida
Manba: vazifalar holat 38, natijalar/40-axe-oldin.txt — Chrome 155.0.8059.40, 2026-10-13
Grafikda buzilishlar yo'q — hammasi 0. Ustunlar tekshirilgan qoidalar soni: dialog showModal bilan ochilganda sahifaning qolgan qismi inert bo'ladi — tekshiriladigan elementlar kamayadi, shuning uchun 17. Statistika paneli va menyu ochiq holatda — eng ko'p: jadval, menyu ARIA'si qo'shiladi.
Nega 0? Chunki vazifalar 4-qismdan beri semantik HTML bilan yozilgan. 17-qismda React'ga ko'chganda teglar o'zgarmadi, 18-qismda Menyu APG naqshi bilan qurildi, 37-darsda Tugma nomi tur bilan himoyalandi. a11y — oxirida qo'shiladigan "bezak" emas, har qadamdagi odat.
Ikki halol izoh:
- Birinchi o'lchovda dialog
color-contrast ×4berdi. Sabab — o'lchov dialog ochilish animatsiyasi (0,15 s) paytida olingan edi: shaffoflik hali to'liq emas. Animatsiya tugashi kutilgach — 0. Avtomatik vosita ham noto'g'ri vaqtda o'lchasa, noto'g'ri natija beradi. - "Qo'lda ko'rish kerak" (incomplete) ro'yxati bo'sh emas: statistika paneli + menyu holatida axe
color-contrast ×3(fon rangini aniqlay olmadi — element ustma-ust) vaaria-valid-attr-value ×1ni "tekshira olmadim, odam qarasin" deb belgiladi. Bu buzilish emas — axe o'zi ishonchsiz bo'lgan joylarni sanaydi. Ular qo'lda ko'rildi.
6.2 Malika "ko'radigan" daraxt
axe qoidalarni tekshiradi, lekin "Malika nimani eshitadi?" degan savolga to'g'ridan-to'g'ri javob bermaydi. Buning uchun kanon e2e ssenariysi Chrome'dan to'liq a11y daraxtini oldi (DevTools protokoli, Accessibility.getFullAXTree) va beshta tekshiruv qildi. Haqiqiy natija (natija-18-40.txt):
✅ Malika: nomsiz boshqaruv elementi yo'q (button, checkbox, textbox…): []
✅ Malika: katak nomi — vazifa matni (label): ["Non olish","Sut olish","Kitob o'qish"]
✅ Malika: ro'yxat nomi «Ro'yxat» (aria-labelledby), 3 ta element: [true,3]
✅ Malika: jonli hududlar (role=status) — e'lon, aloqa, qidiruv, statistika holati, forma xatosi: true
✅ Malika: statistika tugmasi — kengaytirilgan (expanded), menyu tugmasi — haspopup: [true,"menu"]Har qator — bugungi bir bo'lim:
- Nomsiz element yo'q — «Har boshqaruv elementining nomi» bo'limi.
- Katak nomi — vazifa matni. Har katak
<label>ichida, demak Malika "Non olish, belgilash katagi, belgilanmagan" deb eshitadi, faqat "katak" emas. - Ro'yxat nomi —
<ul aria-labelledby>sarlavhaga ulangan. Malika "Ro'yxat, ro'yxat, 3 ta element" deb eshitadi. - Jonli hududlar — beshta
role="status": e'lon, aloqa holati, qidiruv natijasi, statistika holati, forma xatosi. - Holatlar —
aria-expandedvaaria-haspopup="menu".
Bu tekshiruvlar e2e'da qoladi: kelajakda kimdir nomsiz tugma qo'shsa — test yiqiladi. Bugun kod o'zgarmadi. Keyingi darsda Bobur ssenariysi va skip link qo'shiladi, commit — o'shanda.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Tab bilan elementga yetib bo'lmaydi | <div onClick> |
<button> |
| NVDA: "tugma" (nomsiz) | ikonka tugmada matn yo'q | aria-label yoki sr-only matn |
| NVDA: "tahrirlanadigan matn" (nomsiz) | placeholder bor, <label> yo'q |
<label htmlFor> |
Invalid DOM property `for`. Did you mean `htmlFor`? |
JSX'da for |
htmlFor |
| ikki maydonga bitta yorliq | qo'lda yozilgan bir xil id |
useId |
| xato faqat qizil rangda | holat bog'lanmagan | aria-invalid + aria-describedby |
role="button" li element Enter'ga javob bermaydi |
ARIA xulq bermaydi | native teg yoki klaviatura ishlovchisi |
axe: color-contrast faqat ba'zan |
animatsiya paytida o'lchandi | animatsiya tugashini kuting |
8. Mashqlar
Mashqlar kurs/mashqlar/18/39-a11y/ da.
1-mashq (oson): nima eshitiladi?
Malika har elementda nomni eshitadi. Nomni yozing (nom yo'q bo'lsa — yo'q).
<button aria-label="Yopish">✕</button>—<button><img src="savat.svg" alt="" /></button>—<label>Telefon <input /></label>dagi maydon —
Yechim
- Yopish —
aria-labelichki "" dan ustun. - yo'q —
alt=""rasmni bezak qildi, tugmada boshqa matn yo'q. Bu xato: tugma ichidagi yagona rasmdaaltamalni aytishi kerak (alt="Savat"). - Telefon —
<label>maydonni o'rab olgan,htmlForsiz ham bog'langan. LekinhtmlFor+idishonchliroq: ba'zi yordamchi texnologiyalar o'ralgan yorliqni yaxshi tanimaydi.
2-mashq (o'rta): div larni tuzating
Quyidagi taom kartasida uch a11y xatosi bor. Toping va tuzating: semantik teglar, nomli tugma, alt.
function DishCard({ onAdd, onFavorite }) {
return (
<div className="karta">
<img src="osh.webp" />
<div className="sarlavha">Osh</div>
<div className="tugma" onClick={onAdd}>Savatga</div>
<button onClick={onFavorite}>♥</button>
</div>
);
}Yechim
function DishCard({ onAdd, onFavorite }) {
return (
<article className="karta">
<img src="osh.webp" alt="Osh — sabzi, go'sht, guruch" />
<h3>Osh</h3>
<button onClick={onAdd}>Savatga</button>{" "}
<button aria-label="Sevimlilarga qo'shish" onClick={onFavorite}>
♥
</button>
</article>
);
}
export default function App() {
return (
<DishCard
onAdd={() => console.log("savatga: Osh")}
onFavorite={() => console.log("sevimli: Osh")}
/>
);
}Konsolda:
savatga: Osh
sevimli: OshEkranda:
Osh
Savatga ♥Uch tuzatish: (1) <img> ga alt — rasm ma'lumot beradi; (2) <div onClick> → <button> — Tab, Enter, rol; (3) "" tugmaga aria-label. Qo'shimcha: sarlavha <div> emas, <h3> (Malika H tugmasi bilan taomdan taomga sakraydi), karta <article> — mustaqil kontent bo'lagi. harakat dagi bos("Sevimlilarga qo'shish") tugmani aria-label bo'yicha topdi — testlar ham Malika kabi nom bo'yicha qidiradi.
3-mashq (qiyin): akkordeon
«Ichimliklar» bo'limi: sarlavhadagi tugma bosilsa, ro'yxat ochiladi yoki yopiladi. Talablar:
- Tugma —
<h2>ichida (<h2 role="button">emas). aria-expandedholatni bildiradi,aria-controls— ro'yxatidsi (useIdbilan).- Yopiq ro'yxat —
hiddenatributi bilan (daraxtdan ham chiqadi). - Bosilganda konsolga
aria-expanded:va yangi qiymat (DOM'dan o'qilgan, render'dan keyin). Ishora: render'dan keyingi DOM —useEffect(useEffect I).
Yechim
import { useEffect, useId, useRef, useState } from "react";
export default function App() {
const [open, setOpen] = useState(false);
const listId = useId();
const buttonRef = useRef(null);
const first = useRef(true);
useEffect(() => {
if (first.current) {
first.current = false;
return;
}
const value = buttonRef.current.getAttribute("aria-expanded");
console.log("aria-expanded:", value);
}, [open]);
return (
<section>
<h2>
<button
ref={buttonRef}
aria-expanded={open}
aria-controls={listId}
onClick={() => setOpen(!open)}
>
Ichimliklar
</button>
</h2>
<ul id={listId} hidden={!open}>
<li>Ko'k choy — 5000 so'm</li>
<li>Kompot — 7000 so'm</li>
</ul>
</section>
);
}Konsolda:
aria-expanded: trueEkranda:
Ichimliklar
Ko'k choy — 5000 so'm
Kompot — 7000 so'mSarlavha <h2> bo'lib qoldi (Malika H bilan topadi), ichidagi <button> — bosiladigan. Atribut hidden yopiq ro'yxatni ko'rinishdan ham, a11y daraxtidan ham olib tashlaydi. first ref — birinchi render'dagi effektni o'tkazib yuborish uchun: bizga faqat bosishdan keyingi qiymat kerak. Bu — HTML'dagi details tegining qo'lda yozilgan varianti. Oddiy holatda details tegi o'zi yetarli (details va summary).
4-mashq: Vazifalar qadami
Loyihada feat/a11y-otish branch'ini oching. npm run build va npm run preview dan keyin uch tekshiruv qiling:
- Lighthouse → Accessibility.
- axe DevTools bilan asosiy ko'rinish va o'chirish dialogi.
- NVDA bilan: ro'yxat sarlavhasiga H bilan o'ting va birinchi vazifa katagi nomini eshiting.
Yechim
- Lighthouse Accessibility — 100 (kanonda yakuniy o'lchov: 97–99 / 100 / 100 / 100,
lighthouse-yakuniy.txt). - axe — 0 buzilish. Dialog uchun animatsiya tugashini kuting (aks holda
color-contrastchiqishi mumkin). - NVDA: "Ro'yxat, sarlavha, 2-daraja", keyin ↓ — "Ro'yxat, 3 ta element", Tab — "Non olish, belgilash katagi, belgilanmagan". Aniq so'zlar NVDA versiyasi va tiliga qarab farq qiladi.
- Kod o'zgarmadi, commit — keyingi darsda.
9. Real ishda
- Qonun. Ko'p davlatlarda davlat va yirik kompaniya saytlari uchun a11y majburiy: AQShda ADA va Section 508, Yevropada European Accessibility Act (2025-yil iyundan). Xorijiy buyurtmachilar uchun ishlasangiz — WCAG 2.2 AA talab qilinadi.
- Kod ko'rigi da a11y — alohida band: "nomi bormi?", "klaviatura bilan ishlaydimi?". Dizayn tizimlari (Radix, React Aria) a11y'ni komponent ichiga yashiradi — shuning uchun ular qadrli (Mantine, Chakra va React Aria).
- Testlar — Testing Library'ning
getByRole("button", { name: "Saqlash" })so'rovi a11y daraxtidan qidiradi: nomsiz tugmani test ham topa olmaydi (RTL I). - Intervyu savollari: "
div onClicknima uchun yomon?", "accessible name qanday hisoblanadi?", "aria-labelvaaria-labelledbyfarqi?", "ARIA'ning birinchi qoidasi?".
Xulosa
- React a11y'ni o'zi bermaydi — semantik teg sizning tanlovingiz: amal —
<button>, o'tish —<a href>. - Har boshqaruv elementining nomi bor:
<label htmlFor>+useId,aria-label,alt;placeholder— yorliq emas. aria-*JSX'da chiziqcha bilan; holat —aria-expanded,aria-invalid+aria-describedby; native element bo'lsa — ARIA emas.- Uch qavat: lint (jsx-a11y), axe (DevTools, Lighthouse, testlar), qo'lda — klaviatura va NVDA.
vazifalar: axe-core 4.13.0, olti holat — 0 buzilish; Malika daraxti — nomsiz element 0, beshta jonli hudud.
Keyingi dars: Fokus boshqaruvi va klaviatura navigatsiyasi — focus trap, fokusni qaytarish, roving tabindex, skip link va inert.
Manbalar
- React: "Accessibility" (
htmlFor,aria-*) — react.dev/reference/react-dom/components/common (2026-10 holatiga, React 19.3) - W3C: WAI-ARIA Authoring Practices Guide (APG) — w3.org/WAI/ARIA/apg
- W3C: WCAG 2.2 — w3.org/TR/WCAG22
- Deque: axe-core — github.com/dequelabs/axe-core
- eslint-plugin-jsx-a11y — github.com/jsx-eslint/eslint-plugin-jsx-a11y
- NV Access: NVDA — nvaccess.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!