Teu progresso
0 / 83 módulos0%
Estágio 03 · 03-17
Bloqueado02-02 cobriu accessibility como design e implementation. Mas conhecimento sem testing automation vira regressão silenciosa. Dev adiciona componente novo, esquece aria-label, PR aprova, deploy, screen reader user reclama meses depois, pior, abandona produto sem reclamar. Sem CI gate, a11y degrada continuamente.
Tooling automatizado não pega tudo (estimativas: ~30-40% de issues detectáveis automaticamente; resto exige usuário humano e screen reader). Mas pega muito do baixo-pendente fácil, alt text faltando, contrast ruim, label-input mismatch, aria-* errado, focus order broken, lang missing. Combinar automated + manual + user testing é o standard.
Compliance importa: WCAG 2.1 AA é referência mínima; AAA quando contexto pede. Lawsuits ADA crescem ano a ano (Domino's vs Robles, 2019; várias outras). EU Web Accessibility Directive 2016 exige público sites nivelarem. Tooling como Lighthouse, axe, Pa11y, Wave, screen readers (VoiceOver, NVDA, JAWS) compõem stack.
Este módulo é a11y como prática de engineering: tools automatizados, integração CI, manual audits estruturados, screen reader testing real, WCAG conformance cobrança, accessibility audit reports, e como evangelizar dentro do time. Plus os patterns de componentes complexos (modals, comboboxes, data grids) que tooling automatizado não pega.
WCAG (Web Content Accessibility Guidelines):
Padrão prático: AA. AAA para subset (medical, government).
Compliance documentation: VPAT (Voluntary Product Accessibility Template), accessibility statement.
ARIA (Accessible Rich Internet Applications) suplementa HTML quando native não cobre:
role: papel semântico (button, dialog, menu).aria-label/aria-labelledby: nome acessível.aria-describedby: descrição extra.aria-expanded, aria-selected, aria-checked, aria-current: estado.aria-live, aria-atomic: anúncios dinâmicos.aria-hidden: esconde de AT (Assistive Tech).Primeiro princípio do ARIA: não use ARIA. Se existe HTML element que faz, use HTML. <button> > <div role="button">. ARIA wrong é pior que sem ARIA.
Linearizam DOM em árvore acessível. User navega via:
Tools:
Cada um interpreta ARIA ligeiramente diferente. Test with at least 2.
Cobrem ~30-40% issues:
Ofício real: integrar 1 motor (axe) em multiple stages (lint → unit → e2e → CI).
eslint-plugin-jsx-a11y para React. Rules:
alt-text: <img> sem alt.anchor-is-valid: <a> sem href.click-events-have-key-events: divs clicáveis sem keyboard.no-noninteractive-element-interactions: button-like em div.label-has-associated-control.aria-props, role-supports-aria-props: ARIA correto.Integrar em lint = catch em PR antes de revisão humana.
jest-axe:
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('button accessible', async () => {
const { container } = render(<MyButton />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Cobre componentes isolados. Catches alt missing, contrast at component level.
@axe-core/playwright:
import AxeBuilder from '@axe-core/playwright';
test('home page accessible', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
Cobre integração e contexto real. Run em CI por route principal.
cypress-axe similar.
lhci runs Lighthouse em CI. Targets configuráveis (accessibility ≥ 95). Falha PR se score cair.
Útil pra dashboards de tendência.
Razões:
Tools: WebAIM Contrast Checker, axe.
Design tokens contra contrast: dark mode pode quebrar; test ambos modes.
Visible focus (CSS :focus-visible). Default styles ok ou customize. Não outline: none sem replacement.
Focus order: tab segue source order. Override com tabindex cuidadoso. tabindex="-1" torna programmatically-focusable, não tabbable.
Focus trap: modais retêm foco até fechar. Lib: focus-trap.
Skip links: "Skip to main content" no início; permite leitura rápida.
Conteúdo que muda dinâmicamente sem reload (toasts, notificações, search results count) precisa anunciado.
<div aria-live="polite" aria-atomic="true">
3 results found
</div>
polite: anuncia quando user pausa. assertive: interrupção (use sparingly).
Modal/Dialog:
role="dialog" + aria-modal="true" + aria-labelledby.Combobox / autocomplete:
role="combobox", aria-expanded, aria-controls.role="listbox", options role="option".Data grid:
role="grid", rows role="row", cells role="gridcell".Toast notifications:
role="status" (polite) ou role="alert" (assertive).ARIA Authoring Practices Guide (APG) tem patterns canônicos. Use.
<label for="..."> cada campo.aria-describedby para help text e errors.aria-invalid="true" em invalid.*) com aria-label ou aria-required.Tooling não pega:
prefers-reduced-motion).Manual audit por release maior. Documente findings.
Pessoas com disabilities testando produto = gold standard.
Recruit:
Diversidade necessária: vision (blind, low-vision), hearing (deaf, hard-of-hearing), motor (limited mobility, voice control), cognitive (dyslexia, ADHD, autism).
Insights únicos: tool-detected issue (low contrast) já corrigida pode ainda ser problemática contextualmente.
Pipeline maduro:
Each step catches different layer. None alone enough.
Audit report:
Remediation tracker: tickets em backlog priorizados. Critical = blocks release; serious = next sprint; etc.
VPAT atualizado periodicamente.
iOS: VoiceOver, Dynamic Type, Switch Control, AssistiveTouch. Android: TalkBack, Magnification, Switch Access.
Native components default a11y melhor que web. RN bridges nem sempre, test.
accessibilityLabel, accessibilityRole, accessibilityHint, accessibilityState em RN.
Underrated. Issues:
WCAG 2.2 e WCAG 3 (draft) expandem cobertura cognitive.
Acessibilidade não é só compliance:
04-16 (product/business): a11y é gate sutil pra B2B enterprise (procurement perguntam).
Senior+ é dono de pipeline a11y multi-layer. Tooling pega o detectável; manual SR + keyboard cobrem o resto. Quem só roda Lighthouse passa 100 com produto inacessível.
Cobertura real automated vs manual (Deque benchmarks + WebAIM Million 2024-2025):
axe-core + Playwright (browser-real, stateful): @axe-core/playwright em E2E suite.
import AxeBuilder from '@axe-core/playwright';
test('orders page has no detectable a11y issues', async ({ page }) => {
await page.goto('/orders');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('create order modal is accessible em open state', async ({ page }) => {
await page.goto('/orders');
await page.getByRole('button', { name: 'Criar pedido' }).click();
const results = await new AxeBuilder({ page })
.include('[role="dialog"]')
.withTags(['wcag2aa', 'wcag22aa'])
.analyze();
expect(results.violations).toEqual([]);
});
axe é snapshot-only — rodar em cada important state (modal aberto, error, loading, empty). Custom override: disableRules(['color-contrast']) quando design intencional, sempre justificado em comment + ticket de revisita. Cost: ~5-10ms por scan; signal/noise alta.
Storybook 8+ a11y addon (@storybook/addon-a11y): roda axe em cada story em background; panel inline mostra violations. Component-level catch antes de E2E. test-storybook --runner playwright roda axe em todas stories em CI.
// OrderCard.stories.ts
export default {
title: 'OrderCard',
component: OrderCard,
parameters: {
a11y: {
config: {
rules: [{ id: 'color-contrast', enabled: true }],
},
},
},
};
export const Default = { args: { status: 'pending' } };
export const Delivered = { args: { status: 'delivered' } };
export const Errored = { args: { status: 'failed', errorMessage: 'Endereço inválido' } };
1 story por state crítico. Disabled rules sempre justified em comment com link WCAG.
Lighthouse CI 12+ gating (@lhci/cli):
// lighthouserc.cjs
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/orders', 'http://localhost:3000/login'],
numberOfRuns: 3,
},
assert: {
assertions: {
'categories:accessibility': ['error', { minScore: 0.95 }],
},
},
},
};
Bloqueia merge se score cai. Pegadinha: Lighthouse 100 NÃO garante WCAG (heurística limitada, não testa keyboard fully). Útil pra detectar regression, não pra certificar.
Manual SR workflow (mandatory pra critical journeys):
Workflow Logística "criar pedido":
aria-live="polite" (sem mover focus).Cadência: SR em PR que altera UI semantics; full audit por release; user testing PWD trimestral.
Keyboard-only navigation: Tab + Shift+Tab + Enter + Space + Arrow + Esc. Sem mouse, sem touchpad.
:focus-visible outline SEMPRE; NEVER outline:none sem replacement.role="status".Logística keyboard journeys: header → search → filters → results table → row actions; modal flows; toast dismiss.
Color contrast automation: axe color-contrast rule cobre WCAG ratios (4.5:1 normal AA, 3:1 large 18pt+ AA, 7:1 AAA). NÃO automated: text on photo backgrounds, dynamic data colors — manual review. Tools design phase: tota11y, axe DevTools, Stark Figma plugin.
CI pipeline Logística completa:
a11y@logistica.example.com; 3rd party audit anual (~$5-15k médio em 2026).Logística thresholds por journey:
Anti-patterns observados:
outline:none sem :focus-visible replacement (keyboard users perdem signaling).position:absolute; top:-9999px permanente; broken).aria-live em região mutável sem polite/assertive value (SR não anuncia changes).color-contrast disabled globally em axe (silently passes; user perde).Cruza com: 02-02 (a11y, ARIA + screen reader patterns); 03-01 (testing, integração Playwright suite); 03-04 (CI/CD, gating axe + Lighthouse); 03-18 (cognitive a11y, complementary); 02-04 (React, Storybook integration).
WCAG 2.2 (W3C Recommendation, 5 Out 2023) consolida-se em 2026 como baseline regulatório. Adiciona 9 success criteria sobre 2.1, removendo apenas 4.1.1 Parsing (obsoleto):
European Accessibility Act (EAA) entrou em vigor 28-Jun-2025. Força WCAG 2.1 AA mínimo para B2C produtos digitais ofertados na UE (e-commerce, banking, transport, ebooks). Penalidades nacionais variam, Alemanha até €100k por violação. EAA não exige 2.2 ainda, mas auditorias usam como state-of-art reference.
/* WCAG 2.2 SC 2.5.8 Target Size (Min) */
button, a.btn, [role="button"], input[type="checkbox"] + label {
min-width: 24px;
min-height: 24px;
}
/* AAA target (recomendado para mobile-first) */
@media (pointer: coarse) {
button, a.btn { min-width: 44px; min-height: 44px; }
}
/* SC 2.4.11 Focus Not Obscured: sticky header + scroll-margin */
:root { --sticky-header-h: 64px; }
:focus-visible {
scroll-margin-top: calc(var(--sticky-header-h) + 8px);
outline: 3px solid #0066cc;
outline-offset: 2px;
}
header.sticky { position: sticky; top: 0; }
WCAG 3.0 continua Working Draft em 2026 (W3C AG WG, último update Mar 2026), target candidate recommendation ~2027. Mudança paradigmática vs 2.x:
Não-normativo ainda. Não usar para compliance em 2026, mas designs maduros já testam contra outcomes (ex: "Visual contrast of text" outcome substitui SC 1.4.3).
axe-core 4.10+ (Set 2025, Deque) é referência industry. Pacotes:
@axe-core/playwright 4.10 — fixture nativa Playwright.@axe-core/react 4.10 — runtime no dev mode, log violations no console.axe-core/cli — headless audit single URL.axe-core — engine puro, configurable.Rules-config: best-practice rules (region, landmark-one-main) ficam OFF em CI por default; opt-in com runOnly: ['wcag2a', 'wcag2aa', 'wcag22aa', 'best-practice']. Custom rules via axe.configure({checks: [...], rules: [...]}). Color-contrast-enhanced rule cobre WCAG AAA 1.4.6.
Cobertura tooling vs manual (Deque benchmarks): axe-core detecta automaticamente ~57% das WCAG issues automatable; manual SR + keyboard restantes 43% (focus order semântico, alt text quality, ARIA misuse contextual, dynamic announcements). Tooling-only é insuficiente. Cross-cite: WebAIM Million 2024 (análise top-1M homepages): 95.9% das homepages têm pelo menos 1 WCAG failure detectable, média 56.8 errors per page — escala do problema, não cobertura de ferramenta.
Playwright a11y testing 1.45+ (Jul 2025) tem fixture native + cross-browser:
// tests/a11y.fixture.ts
import { test as base, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
export const test = base.extend<{ makeAxeBuilder: () => AxeBuilder }>({
makeAxeBuilder: async ({ page }, use) => {
const builder = () => new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
.disableRules(['color-contrast']); // theme-switching false-positive
await use(builder);
},
});
// tests/orders.a11y.spec.ts
import { test } from './a11y.fixture';
import { expect } from '@playwright/test';
test('admin/orders zero WCAG 2.2 AA violations', async ({ page, makeAxeBuilder }) => {
await page.goto('/admin/orders');
await page.getByLabel('Email').fill(process.env.E2E_USER!);
await page.getByLabel('Password').fill(process.env.E2E_PWD!);
await page.getByRole('button', { name: /sign in/i }).click();
await page.waitForURL('/admin/orders');
const results = await makeAxeBuilder().analyze();
expect(results.violations).toEqual([]);
// Snapshot accessibility tree, assert shape
const snap = await page.accessibility.snapshot();
expect(snap?.children?.find(n => n.role === 'main')).toBeDefined();
});
Rodar em WebKit cobre VoiceOver-equivalent rendering (mesma engine). Authenticated route testing exige fixture com pre-auth state (storageState).
Storybook a11y addon 8.x (@storybook/addon-a11y) roda axe per-story. A11y panel mostra violations no sidebar. CI gate via test-storybook --browsers chromium --maxWorkers=2 --testTimeout=30000. Adopters: Shopify Polaris, Atlassian Design System, GitHub Primer rodam mandatory em PR.
Screen reader automation 2026 via @guidepup/guidepup (orchestrate VoiceOver/NVDA via OS-level APIs, não webdriver). Tests rodam em macOS runner (VoiceOver) + Windows runner (NVDA), assert anúncios reais:
import { voiceOver } from '@guidepup/guidepup';
test('save button announces correctly', async () => {
await voiceOver.start();
await voiceOver.navigateToWebContent();
await voiceOver.next(); // até landing no botão
expect(await voiceOver.lastSpokenPhrase()).toContain('Save order, button');
await voiceOver.act();
expect(await voiceOver.lastSpokenPhrase()).toContain('Order saved');
await voiceOver.stop();
});
Adopters reais: Microsoft Edge team (web platform tests), Adobe (Spectrum design system). Custo: macOS GitHub Actions runner é 10x mais caro que Linux ($0.08/min vs $0.008/min); reservar para release-candidate runs, não toda PR.
Pa11y Dashboard / Lighthouse CI são alternativas a axe-only para multi-page crawl. Pa11y útil para non-React legacy multi-page (PHP, Rails server-rendered), gera WCAG impact reports per-route, baseline + drift detection. Lighthouse CI integra a11y score (subset axe-core) com Web Vitals em mesmo pipeline; útil em production deploy gate.
Manual SR + keyboard testing protocol continua não-substituível:
Test matrix: cada release Q tem 1-2h manual SR session por critical flow. Recording: AXE DevTools "share scenario" reproduz steps. WebAIM Screen Reader User Survey #10 (2024): NVDA 65.6%, JAWS 60.5%, VoiceOver desktop 33.5% (multi-select).
CI integration patterns 2026:
@axe-core/playwright em PR check (block merge se WCAG A/AA falhar).test-storybook daily run em main.a11y.violation.severity:critical, a11y.wcag.sc:2.4.11, dashboard de drift por route.Legal landscape 2026: ADA Title III: 8.800 federal filings em 2024 (+7% YoY) e 8.667 em 2025 (-2%) (Seyfarth ADA Title III blog tracker); web accessibility lawsuits especificamente 2.452 em 2024 → 3.117 em 2025 (+27%) (Seyfarth 2025 mid-year report; AudioEye 2025 review). DOJ final rule (24 Abr 2024) adopta WCAG 2.1 AA como standard pra government Title II web/mobile. Section 508 Refresh 2017 obriga federal contractors a WCAG 2.0 AA. EU EAA enforced 28-Jun-2025. UK Equality Act 2010 + Public Sector Bodies Accessibility Regs 2018. Casos referenciais: Domino's vs Robles (SCOTUS 2019, app/site precisa ser acessível), Target $6M settlement (NFB class action 2008). Risk pricing: failure = lawsuit + remediation + brand. Insurance carriers (AIG, Chubb) oferecem cyber/a11y rider.
Mobile a11y deep 2026 — VoiceOver/TalkBack + Voice Control + user preferences detection: mobile a11y é território distinto de web a11y. iOS: VoiceOver (rotor + custom actions), Voice Control (numbers/grid/labels), Switch Control, Guided Access, Reduce Motion, Increase Contrast, Bold Text, Larger Text Type. Android: TalkBack (linear vs explore-by-touch), Switch Access, Voice Access (Google Assistant integrado), Live Caption, font scaling, color inversion, Select to Speak.
Detecção user preferences em runtime:
UIAccessibility.isVoiceOverRunning, UIAccessibility.isReduceMotionEnabled, UIAccessibility.preferredContentSizeCategory — adapt UI quando AT ativo (e.g., simplificar animations, expandir hit-targets).LocalAccessibilityManager.current.isEnabled, LocalDensity + Dynamic Type, Settings.Global.ANIMATOR_DURATION_SCALE = 0 indica reduce motion.AccessibilityInfo.isScreenReaderEnabled(), AccessibilityInfo.isReduceMotionEnabled().@media (prefers-reduced-motion), @media (prefers-contrast), @media (forced-colors: active).Compose semantics modifier patterns:
Modifier.semantics {
contentDescription = "Avatar do courier João"
role = Role.Image
customActions = listOf(
CustomAccessibilityAction("Ligar pro courier") { call(courier); true }
)
}
SwiftUI accessibility traits + custom rotor:
Image("courier_avatar")
.accessibilityLabel("Avatar do courier João")
.accessibilityAddTraits(.isImage)
.accessibilityCustomContent("Avaliação", "4.8 estrelas")
.accessibilityAction(named: "Ligar") { callCourier() }
Manual mobile SR test protocol: TalkBack swipe right (next), Tap (activate), Three-finger swipe up (read all from top), Two-finger swipe right (next via local context); VoiceOver swipe right (next), Double-tap (activate), Two-finger flick down (read all), rotor (twist two fingers) para navigation modes (headings, links, form controls).
Real impact 2026: WCAG 2.2 Target Size (24×24 CSS px min, AA) é peculiarmente difícil em densidade mobile alta — testar em device real, não simulator. EAA (28-Jun-2025) cobre apps mobile B2C explicitly. ADA Title III tendência 2024-2025 (Seyfarth tracker): web/app accessibility filings +27% YoY 2025; pro se plaintiffs +40% (uso de AI tools como ChatGPT/Copilot pra draft complaints).
Logística aplicada: courier dashboard /admin testa axe-playwright em todas rotas autenticadas (orders, drivers, dispatch, settings) via fixture com storageState pré-auth, gate em PR. Storybook a11y panel obrigatório passar para merge em qualquer componente do design system interno. Manual NVDA + VoiceOver run por release no flow "criar order" (create → assign driver → mark delivered). WCAG 2.2 SC 2.5.8 target-size aplicado em map markers (Leaflet/Mapbox custom icons 44x44 CSS px), em swipe-actions de lista de orders aplicar SC 2.5.7 com alternative button "Assign".
Cruza com 03-17 §2.4 (axe basics, agora específico 2.2 rules), 03-17 §2.7 (Playwright a11y intro estendido com fixture + accessibility tree snapshot), 03-04 (CI/CD a11y como deploy gate, não só PR check), 02-02 (a11y fundamentals, POUR + ARIA roots), 03-18 (cognitive a11y, ponte para WCAG 3.0 outcomes equity-focused).
Anti-patterns:
disableRules: ['color-contrast'] em dark/light theme switching, false positives durante transition flicker; rodar contrast em snapshot estático separado.test-storybook não roda em CI, addon mostra UI no dev, gates não existem, regressão silenciosa.outline: none sem replacement, viola SC 2.4.7 + 2.4.13; sempre pair com :focus-visible custom style.Você precisa, sem consultar:
@axe-core/playwright em CI.aria-live polite vs assertive.Fazer a11y audit completo + CI gates da Logística.
A11Y-AUDIT.md com issues classificados (severity + WCAG criterion).eslint-plugin-jsx-a11y configurado em recommended.jest-axe em ≥ 10 componentes UI principais (Button, Modal, Form, Combobox, Toast, etc.).@axe-core/playwright em 5 fluxos críticos (login, criar pedido, tracking, perfil, listagem).lhci configurado em GitHub Actions.prefers-reduced-motion respeitado em animations.docs/A11Y.md com:
outline: none sem replacement.<div onClick> em vez de <button>.jest-axe cobre 10+ componentes.Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1Qual o primeiro princípio do ARIA?
Q2Por que tooling automatizado (axe, Lighthouse) cobre só 30-57% das WCAG issues?
Q3Qual o contrast ratio mínimo WCAG AA para texto normal?
Q4Por que Lighthouse score 100 não garante WCAG conformance?
Q5O que requer WCAG 2.2 SC 2.5.8 Target Size (Minimum, AA)?
Destrava
03-17 é prereq dos seguintes módulos: