Web Accessibility
ΠΠ΅Π±-Π΄ΠΎΡΡΡΠΏΠ½ΠΎΡΡΡ (a11y) β ΠΏΡΠ°ΠΊΡΠΈΠΊΠΈ, Π΄Π΅Π»Π°ΡΡΠΈΠ΅ ΡΠ°ΠΉΡ ΠΏΡΠΈΠ³ΠΎΠ΄Π½ΡΠΌ Π΄Π»Ρ Π»ΡΠ΄Π΅ΠΉ Ρ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½Π½ΡΠΌΠΈ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡΠΌΠΈ: Π½Π΅Π·ΡΡΡΠΈΡ , ΡΠ»Π°Π±ΠΎΠ²ΠΈΠ΄ΡΡΠΈΡ , Π»ΡΠ΄Π΅ΠΉ Ρ ΠΌΠΎΡΠΎΡΠ½ΡΠΌΠΈ Π½Π°ΡΡΡΠ΅Π½ΠΈΡΠΌΠΈ, ΠΊΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΡΠΌΠΈ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎΡΡΡΠΌΠΈ.
ΠΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΡΡΠ°Π½Π΄Π°ΡΡ β WCAG 2.2 (Web Content Accessibility Guidelines). Π£ΡΠΎΠ²Π΅Π½Ρ AA β ΠΌΠΈΠ½ΠΈΠΌΡΠΌ Π΄Π»Ρ ΠΊΠΎΠΌΠΌΠ΅ΡΡΠ΅ΡΠΊΠΈΡ ΡΠ°ΠΉΡΠΎΠ².
WCAG 2.2 AA β ΠΊΠ»ΡΡΠ΅Π²ΡΠ΅ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡ
Perceivable (Π²ΠΎΡΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌΠΎΡΡΡ)
- ΠΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΈΠΌΠ΅ΡΡ ΠΎΡΠΌΡΡΠ»Π΅Π½Π½ΡΠΉ
alt(Π½Π΅ βimageβ, Π½Π΅ ΠΏΡΡΡΠΎΠΉ Π΄Π»Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠ²Π½ΡΡ ) - ΠΠ΅ΠΊΠΎΡΠ°ΡΠΈΠ²Π½ΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ:
alt=""ΠΈΠ»ΠΈrole="presentation" - ΠΠΈΠ΄Π΅ΠΎ ΠΈ Π°ΡΠ΄ΠΈΠΎ: ΡΡΠ±ΡΠΈΡΡΡ ΠΈΠ»ΠΈ ΡΠ΅ΠΊΡΡΠΎΠ²Π°Ρ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π°
- ΠΠΎΠ½ΡΠ΅Π½Ρ Π½Π΅ Π·Π°Π²ΠΈΡΠΈΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΡ ΡΠ²Π΅ΡΠ° (1.4.1)
- Π’Π΅ΠΊΡΡ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΡΠ΅ΡΡΡ Π΄ΠΎ 200% Π±Π΅Π· ΠΏΠΎΡΠ΅ΡΠΈ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΠΈ (1.4.4)
- Reflow: ΠΊΠΎΠ½ΡΠ΅Π½Ρ ΡΠΈΡΠ°Π΅ΠΌ ΠΏΡΠΈ ΡΠΈΡΠΈΠ½Π΅ 320 CSS px Π±Π΅Π· Π³ΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ (1.4.10)
- ΠΠΎΠ½ΡΡΠ°ΡΡ ΡΠ΅ΠΊΡΡΠ° ΠΌΠΈΠ½ΠΈΠΌΡΠΌ 4.5:1, ΠΊΡΡΠΏΠ½ΠΎΠ³ΠΎ ΡΠ΅ΠΊΡΡΠ° 3:1 (1.4.3)
- UI-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ ΠΈ Π³ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΡ: ΠΊΠΎΠ½ΡΡΠ°ΡΡ ΠΌΠΈΠ½ΠΈΠΌΡΠΌ 3:1 (1.4.11)
Operable (ΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌΠΎΡΡΡ)
- ΠΡΠ΅ ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ Π΄ΠΎΡΡΡΠΏΠ½Ρ Ρ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ (2.1.1)
- ΠΠ΅Ρ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΠ½ΡΡ Π»ΠΎΠ²ΡΡΠ΅ΠΊ (2.1.2)
- Skip-link βΠΠ΅ΡΠ΅ΠΉΡΠΈ ΠΊ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠΌΡβ β ΠΏΠ΅ΡΠ²ΡΠΉ ΡΠΎΠΊΡΡΠΈΡΡΠ΅ΠΌΡΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ (2.4.1)
- Π£Π½ΠΈΠΊΠ°Π»ΡΠ½ΡΠΉ
<title>Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΌΠ°ΡΡΡΡΡΠ° (2.4.2) - ΠΠΎΠ³ΠΈΡΠ½ΡΠΉ ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΡΠΎΠΊΡΡΠ° (2.4.3)
- [2.2] Focus Not Obscured (2.4.11): ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΠΏΡΠΈ ΡΠΎΠΊΡΡΠ΅ Π½Π΅ ΠΏΠ΅ΡΠ΅ΠΊΡΡΡ sticky-ΡΠ°ΠΏΠΊΠΎΠΉ
- [2.2] Dragging Movements (2.5.7): Π΄Π»Ρ drag-ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ Π΅ΡΡΡ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π° ΡΠ΅ΡΠ΅Π· ΠΊΠ»ΠΈΠΊ
- [2.2] Target Size Minimum (2.5.8): ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ ΠΌΠΈΠ½ΠΈΠΌΡΠΌ 24x24 CSS px
Understandable (ΠΏΠΎΠ½ΡΡΠ½ΠΎΡΡΡ)
<html lang="ru">(3.1.1)- Π€ΡΠ°Π³ΠΌΠ΅Π½ΡΡ Π½Π° Π΄ΡΡΠ³ΠΎΠΌ ΡΠ·ΡΠΊΠ΅ ΠΎΠ±ΡΡΠ½ΡΡΡ
<span lang="en">(3.1.2) - ΠΡΠΈΠ±ΠΊΠΈ ΡΠΎΡΠΌ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΡΠΈΡΠΎΠ²Π°Π½Ρ ΡΠ΅ΠΊΡΡΠΎΠΌ, Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΠ²Π΅ΡΠΎΠΌ (3.3.1)
- ΠΠΎΠ»Ρ ΡΠΎΡΠΌ ΠΈΠΌΠ΅ΡΡ Π²ΠΈΠ΄ΠΈΠΌΡΠ΅ labels (3.3.2)
- [2.2] Accessible Authentication (3.3.8): Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ Π±Π΅Π· ΠΊΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΡΡ ΡΠ΅ΡΡΠΎΠ²
Robust (Π½Π°Π΄ΡΠΆΠ½ΠΎΡΡΡ)
- ΠΠ°Π»ΠΈΠ΄Π½ΡΠΉ HTML β Π½Π΅Ρ Π΄ΡΠ±Π»ΠΈΡΡΡΡΠΈΡ
id(4.1.1) - ΠΠ°ΡΡΠΎΠΌΠ½ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ ΠΈΠΌΠ΅ΡΡ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠ΅ ARIA-ΡΠΎΠ»ΠΈ (4.1.2)
- Π‘ΡΠ°ΡΡΡΠ½ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΡΠ΅ΡΠ΅Π·
role="status"ΠΈΠ»ΠΈaria-live(4.1.3)
ARIA-ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ Π΄Π»Ρ SPA
- Live regions (
aria-live="polite") Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ Π² DOM ΠΏΡΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠ΅, Π½Π΅ ΡΠΎΠ·Π΄Π°Π²Π°ΡΡΡΡ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈ aria-expandedΠ½Π° ΠΊΠ½ΠΎΠΏΠΊΠ°Ρ dropdown/accordionaria-current="page"Π½Π° Π°ΠΊΡΠΈΠ²Π½ΠΎΠΌ ΠΏΡΠ½ΠΊΡΠ΅ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈaria-labelΠ½Π° ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°Ρ Π±Π΅Π· Π²ΠΈΠ΄ΠΈΠΌΠΎΠ³ΠΎ ΡΠ΅ΠΊΡΡΠ° (icon-only ΠΊΠ½ΠΎΠΏΠΊΠΈ)aria-describedbyΡΠ²ΡΠ·ΡΠ²Π°Π΅Ρ hint-ΡΠ΅ΠΊΡΡ ΠΈ ΠΎΡΠΈΠ±ΠΊΠΈ Ρ ΠΏΠΎΠ»ΡΠΌΠΈ ΡΠΎΡΠΌaria-hidden="true"Π½Π° Π΄Π΅ΠΊΠΎΡΠ°ΡΠΈΠ²Π½ΡΡ SVG-ΠΈΠΊΠΎΠ½ΠΊΠ°Ρ- ΠΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ARIA Π³Π΄Π΅ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΡΠ΅ΠΌΠ°Π½ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ HTML
Vue 3 SPA β ΡΠΏΠ΅ΡΠΈΡΠΈΠΊΠ°
ΠΠ±ΡΡΠ²Π»Π΅Π½ΠΈΠ΅ ΡΠΌΠ΅Π½Ρ ΠΌΠ°ΡΡΡΡΡΠ°
SPA ΠΌΠ΅Π½ΡΠ΅Ρ ΡΡΡΠ°Π½ΠΈΡΡ Π±Π΅Π· ΠΏΠ΅ΡΠ΅Π·Π°Π³ΡΡΠ·ΠΊΠΈ β ΡΠΊΡΠΈΠ½ΡΠΈΠ΄Π΅Ρ ΠΌΠΎΠ»ΡΠΈΡ. Π Π΅ΡΠ΅Π½ΠΈΠ΅:
// router.ts
router.afterEach((to) => {
document.title = to.meta.title as string || 'SiteName';
nextTick(() => {
const heading = document.querySelector('h1');
if (heading) {
heading.setAttribute('tabindex', '-1');
heading.focus();
}
const announcer = document.getElementById('route-announcer');
if (announcer) announcer.textContent = to.meta.title as string;
});
});<!-- App.vue β Π²ΡΠ΅Π³Π΄Π° Π² DOM -->
<div id="route-announcer" aria-live="polite" aria-atomic="true" class="sr-only"></div>ΠΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΊΠΎΠ½ΡΠ΅Π½Ρ
- ΠΠ°Π³ΡΡΠ·ΠΊΠ° Π΄Π°Π½Π½ΡΡ
:
aria-busy="true"Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ΅ v-if/v-showΠ΄Π»Ρ live region: ΠΏΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΡΡv-showΠΈΠ»ΠΈ Π΄Π΅ΡΠΆΠ°ΡΡ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Π² DOM- Toast-ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΡ ΡΠ΅ΡΠ΅Π· live region, Π½Π΅ ΡΠ΅ΡΠ΅Π· Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ DOM
ΠΠΎΡΡΡΠΏΠ½ΠΎΡΡΡ Leaflet-ΠΊΠ°ΡΡ
- ΠΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ:
aria-label="ΠΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½Π°Ρ ΠΊΠ°ΡΡΠ° ΠΌΠ°ΡΡΡΡΡΠ°"+role="application" - ΠΠ°ΡΠΊΠ΅ΡΡ:
L.marker([lat, lng], { alt: 'ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΡΠΎΡΠΊΠΈ' }) - ΠΠ½ΠΎΠΏΠΊΠΈ zoom:
aria-label="ΠΡΠΈΠ±Π»ΠΈΠ·ΠΈΡΡ",aria-label="ΠΡΠ΄Π°Π»ΠΈΡΡ" - Π’Π΅ΠΊΡΡΠΎΠ²Π°Ρ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π° ΠΌΠ°ΡΡΡΡΡΠ° (ΡΠΏΠΈΡΠΎΠΊ ΡΠΎΡΠ΅ΠΊ Ρ Π°Π΄ΡΠ΅ΡΠ°ΠΌΠΈ)
- ΠΡΠ»ΠΈ ΠΊΠ°ΡΡΠ° Π΄Π΅ΠΊΠΎΡΠ°ΡΠΈΠ²Π½Π°Ρ:
aria-hidden="true"+tabindex="-1"
Π ΡΡΡΠΊΠΎΡΠ·ΡΡΠ½Π°Ρ ΡΠΏΠ΅ΡΠΈΡΠΈΠΊΠ°
<html lang="ru">ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ β Π±Π΅Π· ΡΡΠΎΠ³ΠΎ ΡΠΊΡΠΈΠ½ΡΠΈΠ΄Π΅Ρ ΠΏΡΠΎΠΈΠ·Π½ΠΎΡΠΈΡ ΠΊΠΈΡΠΈΠ»Π»ΠΈΡΡ Ρ Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΈΠΌ ΠΏΡΠΎΠΈΠ·Π½ΠΎΡΠ΅Π½ΠΈΠ΅ΠΌ- ΠΠ½Π³Π»ΠΎΡΠ·ΡΡΠ½ΡΠ΅ Π²ΡΡΠ°Π²ΠΊΠΈ:
<span lang="en">Strava</span> - ARIA-labels Π½Π° ΡΡΡΡΠΊΠΎΠΌ: βΠΡΠ½ΠΎΠ²Π½Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡβ, βΠΠ°ΠΊΡΡΡΡβ, βΠΠ΅Π½Ρβ
- ΠΠ±Π±ΡΠ΅Π²ΠΈΠ°ΡΡΡΡ:
<abbr title="ΠΊΠΈΠ»ΠΎΠΌΠ΅ΡΡ">ΠΊΠΌ</abbr> - Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅: NVDA Ρ ΡΡΡΡΠΊΠΈΠΌ ΡΠΈΠ½ΡΠ΅Π·Π°ΡΠΎΡΠΎΠΌ (eSpeak/RHVoice)
ΠΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ Π°ΡΠ΄ΠΈΡΠ°
ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ (Π±ΡΠ°ΡΠ·Π΅Ρ)
| ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ | Π§ΡΠΎ Π΄Π΅Π»Π°Π΅Ρ |
|---|---|
| Lighthouse | Chrome DevTools β Lighthouse β Accessibility. Score 0-100, ~50 ΠΏΡΠ°Π²ΠΈΠ» |
| axe DevTools | Π Π°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅ Chrome. ~100 ΠΏΡΠ°Π²ΠΈΠ» axe-core, WCAG 2.0/2.1/2.2 |
| WAVE | wave.webaim.org ΠΈΠ»ΠΈ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅. ΠΠΈΠ·ΡΠ°Π»ΡΠ½ΡΠΉ ΠΎΠ²Π΅ΡΠ»Π΅ΠΉ ΠΈΠΊΠΎΠ½ΠΎΠΊ Π½Π° ΡΡΡΠ°Π½ΠΈΡΠ΅ |
| Accessibility Insights | Π Π°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅ Microsoft. Tab Stops β Π²ΠΈΠ·ΡΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΡΡΠ΄ΠΊΠ° ΡΠΎΠΊΡΡΠ° |
CI-ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ
Playwright + axe-core (ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΡΠΉ):
npm install -D @playwright/test @axe-core/playwrightimport AxeBuilder from '@axe-core/playwright';
test('page a11y', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.exclude('.leaflet-container')
.analyze();
expect(results.violations).toEqual([]);
});ESLint-ΠΏΠ»Π°Π³ΠΈΠ½ (shift-left):
npm install -D eslint eslint-plugin-vuejs-accessibility vue-eslint-parserpa11y (Π±ΡΡΡΡΡΠΉ smoke test):
npx pa11y https://example.com --standard WCAG2AAΠ ΡΡΠ½ΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
VoiceOver (macOS): Cmd+F5, VO+U (Rotor) Π΄Π»Ρ headings/landmarks/links.
Keyboard: Tab/Shift+Tab ΠΏΠΎ Π²ΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΠ΅. ΠΡΠΎΠ²Π΅ΡΠΈΡΡ: Π²ΠΈΠ΄ΠΈΠΌΡΠΉ focus, Π»ΠΎΠ³ΠΈΡΠ½ΡΠΉ ΠΏΠΎΡΡΠ΄ΠΎΠΊ, ΠΌΠΎΠ΄Π°Π»ΠΊΠΈ Ρ focus trap.
ΠΡΠ΄ΠΈΡ RideHub (2026-04-03)
ΠΠ±ΡΠ°Ρ ΠΎΡΠ΅Π½ΠΊΠ°: 4/10. ΠΠ°Π·ΠΎΠ²Π°Ρ ΡΡΡΡΠΊΡΡΡΠ° Π΅ΡΡΡ, ΡΠ΅Π»Π΅Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½Π½ΠΎΠΉ ΡΠ°Π±ΠΎΡΡ Π½Π°Π΄ a11y Π½Π΅ Π±ΡΠ»ΠΎ.
Π§ΡΠΎ Ρ ΠΎΡΠΎΡΠΎ
<html lang="ru">β- Π‘Π΅ΠΌΠ°Π½ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΠ΅Π³ΠΈ
<nav>,<main>,<footer>,<section>β document.titleΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅ΡΡΡ ΠΏΡΠΈ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ (useSeo.ts) βprefers-reduced-motionΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ β- Dark mode Ρ OS preference β
- Viewport Π½Π΅ Π±Π»ΠΎΠΊΠΈΡΡΠ΅Ρ zoom β
- Π¨ΡΠΈΡΡΡ Π² rem/em β
role="dialog"+aria-modal="true"Π½Π° ΠΌΠΎΠ΄Π°Π»ΠΊΠ°Ρ β
ΠΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ (P0)
- Π€ΠΎΡΠΌΡ Π±Π΅Π·
<label>β Π²ΡΠ΅ ΠΏΠΎΠ»Ρ ΠΏΠΎΠ»Π°Π³Π°ΡΡΡΡ Π½Π° placeholder. LoginView, admin ΡΠΎΡΠΌΡ - Focus styles ΡΠ½ΠΈΡΡΠΎΠΆΠ΅Π½Ρ β
outline: noneΠ±Π΅Π· Π·Π°ΠΌΠ΅Π½Ρ. ΠΠ΅Ρ:focus-visible - ΠΡΠΈΠ±ΠΊΠΈ ΡΠΎΡΠΌ Π½Π΅ ΠΎΠ·Π²ΡΡΠΈΠ²Π°ΡΡΡΡ β Π½Π΅Ρ
role="alert", Π½Π΅Ρaria-describedby - ΠΠ΅Ρ skip-link β ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΠ½ΡΠΉ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΏΡΠΎΡ ΠΎΠ΄ΠΈΡ Π²ΡΡ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΡΡΡΠ°Π½ΠΈΡΠ΅
- ΠΠ΅Ρ route announcer β SPA ΠΌΠ΅Π½ΡΠ΅Ρ ΡΡΡΠ°Π½ΠΈΡΡ, ΡΠΊΡΠΈΠ½ΡΠΈΠ΄Π΅Ρ ΠΌΠΎΠ»ΡΠΈΡ
Π‘Π΅ΡΡΡΠ·Π½ΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ (P1)
- ΠΠΎΠ΄Π°Π»ΠΊΠΈ Π±Π΅Π· focus trap β ΡΠΎΠΊΡΡ ΡΡ ΠΎΠ΄ΠΈΡ Π·Π° ΠΏΡΠ΅Π΄Π΅Π»Ρ. ΠΠ΅Ρ Π²ΠΎΠ·Π²ΡΠ°ΡΠ° ΡΠΎΠΊΡΡΠ° Π½Π° ΡΡΠΈΠ³Π³Π΅Ρ
- ΠΠ°ΡΡΡ Π½Π΅Π΄ΠΎΡΡΡΠΏΠ½Ρ β 14 Leaflet-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² Π±Π΅Π·
aria-label <a>Π²ΠΌΠ΅ΡΡΠΎ<button>β LoginView logout, HomeView region selection- Loading states β LoadingSpinner Π±Π΅Π·
aria-live,aria-busy - ΠΠΈΠ·ΠΊΠΈΠΉ ΠΊΠΎΠ½ΡΡΠ°ΡΡ β footer legal ΡΠ΅ΠΊΡΡ ~2.5:1 (Π½ΡΠΆΠ½ΠΎ 4.5:1)
ΠΠ»Π°Π½ ΠΈΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ
Π€Π°Π·Π° 1 (1 Π΄Π΅Π½Ρ, quick wins): skip-link, labels Π½Π° ΡΠΎΡΠΌΡ, :focus-visible, role="alert" Π½Π° ΠΎΡΠΈΠ±ΠΊΠΈ, route announcer.
Π€Π°Π·Π° 2 (2-3 Π΄Π½Ρ): focus trap Π² ΠΌΠΎΠ΄Π°Π»ΠΊΠ°Ρ
, <a> β <button>, aria-label Π½Π° ΠΊΠ°ΡΡΡ, aria-hidden Π½Π° SVG.
Π€Π°Π·Π° 3 (3-5 Π΄Π½Π΅ΠΉ): ΠΊΠΎΠ½ΡΡΠ°ΡΡ, aria-current/aria-expanded Π² Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ, axe-core Π² CI, ESLint-ΠΏΠ»Π°Π³ΠΈΠ½.