Estágio 03 · 03-17
Locked02-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).
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.Destrava
03-17 é prereq dos seguintes módulos: