Pular para o conteúdo principal

Acessibilidade Web (a11y) no Cursor: WCAG, ARIA e axe-core

Acessibilidade Web no Cursor

Acessibilidade web (a11y) garante que todos, incluindo pessoas com deficiências, possam perceber, compreender e interagir com sua aplicação. O Cursor, um editor IDE com IA, acelera o desenvolvimento acessível gerando marcação em conformidade com ARIA, executando auditorias automatizadas e aplicando correções WCAG em toda a sua base de código. Este guia aborda rótulos ARIA, conformidade WCAG, testes axe-core, correções de a11y assistidas por IA e testes com leitores de tela.

Pré-requisitos​

Antes de começar, certifique-se de ter o seguinte:

  • Cursor (versão mais recente recomendada) como seu editor IDE
  • Um projeto web (React, Vue ou HTML e JavaScript simples)
  • Node.js 18 ou superior para as ferramentas de teste
  • Familiaridade básica com semântica HTML
dica

Acessibilidade é muito mais barata de construir desde o início do que retrabalhar depois. Abra seu projeto no Cursor desde o início e deixe a IA auditar componentes conforme você os escreve. Corrigir um rótulo ausente durante o desenvolvimento leva segundos; corrigir o mesmo problema em uma aplicação em produção leva semanas.

Compreendendo a Conformidade WCAG​

As Diretrizes de Acessibilidade para Conteúdo Web (WCAG) definem como tornar o conteúdo web acessível. Elas são organizadas em torno de quatro princípios, frequentemente chamados de POUR:

  • Perceptível: informações e componentes da interface do usuário devem ser apresentáveis de formas que os usuários possam perceber.
  • Operável: componentes da interface do usuário e navegação devem ser operáveis.
  • Compreensível: conteúdo e operação da interface do usuário devem ser compreensíveis.
  • Robusto: conteúdo deve ser robusto o suficiente para ser interpretado por tecnologias assistivas.

O WCAG define três níveis de conformidade: A (mínimo), AA (o alvo legal para a maioria das organizações) e AAA (aprimorado). Peça ao Cursor para resumir os requisitos do nível AA para um recurso específico:

Liste os critérios de sucesso WCAG 2.2 Nível AA que se aplicam a
um formulário de checkout em várias etapas com seletores de data e
campos de cartão de crédito.

O Cursor retorna os critérios relevantes e explica como satisfazer cada um com sugestões concretas de código.

Fundamentos de HTML Semântico​

HTML semântico é a base da acessibilidade. O elemento correto muitas vezes elimina a necessidade de ARIA. O Cursor ajuda você a escolher e gerar a marcação correta.

Gerando Componentes Semânticos​

Peça ao Cursor para construir uma barra de navegação com acessibilidade integrada:

Crie uma navegação de site responsiva em React que:
- Use um landmark <nav> com um nome acessível
- Tenha um link de pular para o topo da página
- Alterne um menu mobile com aria-expanded e aria-controls
- Feche com Escape e retorne o foco para o botão de alternância
- Suporte navegação completa por teclado

O Cursor gera o componente:

import { useState, useRef, useEffect } from 'react';

export function SiteNav({ links }: { links: { href: string; label: string }[] }) {
const [open, setOpen] = useState(false);
const toggleRef = useRef<HTMLButtonElement>(null);

useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
setOpen(false);
toggleRef.current?.focus();
}
};
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [open]);

return (
<>
<a href="#main" className="skip-link">Skip to main content</a>
<nav aria-label="Main navigation">
<button
ref={toggleRef}
aria-expanded={open}
aria-controls="mobile-menu"
onClick={() => setOpen((v) => !v)}
>
Menu
</button>
<ul id="mobile-menu" hidden={!open}>
{links.map((link) => (
<li key={link.href}>
<a href={link.href}>{link.label}</a>
</li>
))}
</ul>
</nav>
</>
);
}
dica

A primeira regra do ARIA é: não use ARIA se um elemento HTML nativo existe. Um verdadeiro <button> oferece foco por teclado, ativação com Enter e Space e semântica de leitor de tela gratuitamente. Peça ao Cursor: "Substitua este botão baseado em div por um botão semântico," sempre que você vir onClick em um elemento não interativo.

Rótulos e Papéis ARIA​

Quando o HTML nativo não consegue expressar a semântica necessária, o ARIA preenche a lacuna. O Cursor gera atributos ARIA corretos e avisa sobre erros comuns.

Formulários Acessíveis​

Campos de formulário precisam de rótulos. Peça ao Cursor para gerar um campo de formulário acessível:

Crie um componente FormField reutilizável com:
- Um <label> associado usando htmlFor
- Um atributo aria-required quando obrigatório
- Um link aria-describedby para texto de ajuda e erro
- Mensagens de erro com role="alert"
import { useId } from 'react';

interface FormFieldProps {
label: string;
name: string;
type?: string;
required?: boolean;
error?: string;
helper?: string;
}

export function FormField({ label, name, type = 'text', required, error, helper }: FormFieldProps) {
const fieldId = useId();
const helperId = `${fieldId}-helper`;
const errorId = `${fieldId}-error`;
const describedBy = [helper && helperId, error && errorId].filter(Boolean).join(' ') || undefined;

return (
<div>
<label htmlFor={fieldId}>
{label}
{required && <span aria-hidden="true">*</span>}
</label>
<input
id={fieldId}
name={name}
type={type}
required={required}
aria-required={required}
aria-describedby={describedBy}
aria-invalid={!!error}
/>
{helper && !error && (
<p id={helperId} className="helper-text">{helper}</p>
)}
{error && (
<p id={errorId} role="alert" className="error-text">{error}</p>
)}
</div>
);
}

Regiões Dinâmicas para Atualizações​

Quando o conteúdo é atualizado dinamicamente (notificações, resultados de busca), os leitores de tela precisam de uma região dinâmica para anunciar mudanças. O Cursor gera o padrão:

export function SearchResults({ query, results }: { query: string; results: string[] }) {
return (
<div
aria-live="polite"
aria-atomic="true"
role="status"
>
{results.length === 0 ? (
<p>No results for "{query}".</p>
) : (
<p>{results.length} results found for "{query}".</p>
)}
<ul>
{results.map((r) => <li key={r}>{r}</li>)}
</ul>
</div>
);
}
aviso

Use aria-live="polite" para atualizações não urgentes e aria-live="assertive" apenas para alertas críticos. O uso excessivo de regiões dinâmicas assertivas interrompe os usuários de leitores de tela constantemente e torna sua aplicação inutilizável. Nunca use role="alert" para mensagens de sucesso; reserve-o para erros reais que precisam de atenção imediata.

Testes Automatizados com axe-core​

Ferramentas automatizadas detectam aproximadamente 30 a 40 por cento dos problemas de acessibilidade. axe-core é o padrão da indústria. O Cursor pode integrá-lo à sua suíte de testes e ao pipeline de CI.

Configurando axe-core com Jest​

Peça ao Cursor para adicionar testes automatizados de acessibilidade:

Adicione axe-core ao meu projeto React. Crie um teste Jest que
renderiza o componente SiteNav com Testing Library e executa
as regras do axe.

O Cursor instala as dependências e escreve o teste:

npm install -D @axe-core/react axe-core jest-axe @testing-library/react @testing-library/jest-dom
// src/components/SiteNav.test.tsx
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import { SiteNav } from './SiteNav';

expect.extend(toHaveNoViolations);

const links = [
{ href: '/', label: 'Home' },
{ href: '/about', label: 'About' },
];

describe('SiteNav', () => {
it('has no accessibility violations when closed', async () => {
const { container } = render(<SiteNav links={links} />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('has no accessibility violations when open', async () => {
const { container, getByText } = render(<SiteNav links={links} />);
getByText('Menu').click();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});

Auxiliar de Teste axe Reutilizável​

O Cursor pode gerar um auxiliar reutilizável para que cada teste de componente permaneça consistente:

import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

export async function expectAccessible(component: React.ReactElement) {
const { container } = render(component);
const results = await axe(container, {
rules: {
// Desativar regras que você trata intencionalmente em outro lugar
region: { enabled: false },
},
});
expect(results).toHaveNoViolations();
return results;
}

Use-o em cada teste de componente:

import { expectAccessible } from '../test/expectAccessible';
import { FormField } from './FormField';

it('is accessible', async () => {
await expectAccessible(<FormField label="Email" name="email" required />);
});
dica

Execute a suíte completa do axe antes de cada commit com um script de pacote. Peça ao Cursor: "Adicione um script a11y ao package.json que executa todos os testes axe e falha em violações." Isso impede que regressões cheguem ao seu branch principal.

Correções de Acessibilidade com Assistência de IA​

A IA do Cursor pode escanear um arquivo de componente inteiro, encontrar problemas de acessibilidade e aplicar correções em uma única passagem. Este é um dos fluxos de trabalho a11y mais valiosos.

Auditando um Componente​

Abra um componente no Cursor e execute este prompt no chat de IA:

Audite este componente quanto à conformidade WCAG 2.2 AA. Liste cada
problema com o número da linha, explique por que falha e reescreva
o código com correções. Adicione comentários explicando cada mudança.

O Cursor pode relatar:

  • Linha 12: <div onClick> não é acessível por teclado. Substitua por um <button>.
  • Linha 20: <img> não tem atributo alt. Adicione texto alt descritivo.
  • Linha 28: contraste de cor é 3.1:1, abaixo do mínimo de 4.5:1 para texto normal.
  • Linha 35: diálogo modal não tem armadilha de foco. Usuários podem Tab atrás do diálogo.

O Cursor então aplica as correções:

// Antes
<div className="card" onClick={openDetails}>
<img src={product.image} />
<h3 style={{ color: '#999' }}>{product.name}</h3>
</div>

// Depois
<button
className="card"
onClick={openDetails}
aria-label={`View details for ${product.name}`}
>
<img src={product.image} alt={`${product.name} product photo`} />
<h3 style={{ color: '#595959' }}>{product.name}</h3>
</button>

Gerando uma Armadilha de Foco​

Modais e diálogos precisam de uma armadilha de foco para que usuários de teclado não escapem do diálogo com Tab. Peça ao Cursor:

Adicione uma armadilha de foco a este componente modal. Mova o foco
para o primeiro elemento focalizável ao abrir, capture Tab e
Shift+Tab dentro do diálogo, e retorne o foco ao botão de
acionamento ao fechar.
import { useEffect, useRef } from 'react';

const FOCUSABLE = 'a[href], button:not([disabled]), input, textarea, select, [tabindex]:not([tabindex="-1"])';

export function useFocusTrap(active: boolean) {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (!active || !containerRef.current) return;
const container = containerRef.current;
const elements = container.querySelectorAll<HTMLElement>(FOCUSABLE);
if (elements.length === 0) return;

const first = elements[0];
const last = elements[elements.length - 1];
first.focus();

const onKey = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
};

container.addEventListener('keydown', onKey);
return () => container.removeEventListener('keydown', onKey);
}, [active]);

return containerRef;
}
aviso

Uma armadilha de foco é sem sentido se o HTML subjacente não for acessível por teclado. A armadilha apenas gerencia elementos que são realmente focalizáveis. Se seu diálogo contém apenas elementos <div> com manipuladores onClick, a armadilha de foco não tem nada para focar. Sempre combine a armadilha com elementos semânticos e focalizáveis.

Testes com Leitores de Tela​

Testes automatizados detectam problemas estruturais, mas apenas testes manuais com um leitor de tela revelam a experiência real. O Cursor pode gerar scripts de teste e checklists.

Checklist de Teste Apenas com Teclado​

Peça ao Cursor para gerar um checklist de teste por teclado:

Crie um checklist de teste apenas com teclado para uma aplicação
web cobrindo navegação, formulários, widgets e conteúdo dinâmico.

O Cursor produz:

  1. Navegue pela página com Tab em ordem lógica sem armadilhas.
  2. O link de pular é o primeiro elemento focalizável e pula para o conteúdo principal.
  3. Todos os elementos interativos são acessíveis com Tab e Shift+Tab.
  4. Widgets personalizados (abas, accordions) suportam teclas de seta e Home/End.
  5. Modais capturam o foco e o retornam ao acionador ao fechar.
  6. Indicador de foco visível está presente em todos os elementos focalizáveis.
  7. Enter e Space ativam botões; Enter ativa links.
  8. Formulários são enviados com Enter no botão de envio.

Testando com NVDA, JAWS e VoiceOver​

O Cursor pode gerar notas de teste para cada leitor de tela:

Escreva um guia de início rápido para testar meu app com NVDA no
Windows, VoiceOver no macOS e TalkBack no Android. Inclua o atalho
para iniciar cada leitor e como navegar por cabeçalho e landmark.

O Cursor retorna uma tabela comparativa e um guia de navegação que você pode salvar como arquivo de referência em seu projeto.

Contraste de Cores e Design Visual​

Acessibilidade visual faz parte do quadro WCAG. Baixo contraste é uma das falhas mais comuns.

Verificando Contraste com IA​

Peça ao Cursor para avaliar seus tokens de design:

Aqui estão meus tokens de cor no tailwind.config.js. Verifique cada
par de texto e fundo em relação às proporções de contraste WCAG AA
(4.5:1 para texto normal, 3:1 para texto grande). Sinalize falhas
e sugira correções.

O Cursor lê a configuração, calcula as proporções de contraste e relata falhas:

// Tokens do tailwind.config.js que falham AA
// text-gray-400 on bg-white: 2.85:1 — FALHA
// Correção: use text-gray-600 (#4B5563) para 7.21:1

O Cursor atualiza a configuração:

module.exports = {
theme: {
extend: {
colors: {
'text-secondary': '#4B5563', // era #9CA3AF, agora passa AA
},
},
},
};
dica

Nunca dependa apenas da cor para transmitir significado. Uma borda vermelha ao redor de um campo inválido é invisível para usuários daltônicos que também dependem de leitores de tela. Sempre combine cor com texto ou um ícone. Peça ao Cursor: "Adicione texto e ícones para transmitir estados de erro ao lado da cor," para fazer a correção em todo um formulário.

Acessibilidade no CI​

Verificações automatizadas de a11y pertencem ao seu pipeline de integração contínua. O Cursor pode gerar a configuração.

Lighthouse CI com Categoria a11y​

Peça ao Cursor para adicionar Lighthouse CI:

Adicione Lighthouse CI ao meu workflow do GitHub Actions com a
categoria acessibilidade habilitada e uma pontuação mínima de 0.9.
# .github/workflows/lighthouse.yml
name: Lighthouse
on: [pull_request]

jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run build
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000
uploadArtifacts: true
configPath: .lighthouserc.json
// .lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 0.9 }]
}
},
"collect": {
"startServerCommand": "npm run start",
"startServerReadyPattern": "ready",
"numberOfRuns": 3
}
}
}

Resumo de Melhores Práticas​

Mantenha estes princípios em mente ao construir aplicações acessíveis no Cursor:

  • Prefira HTML semântico em vez de ARIA sempre que um elemento nativo for adequado.
  • Rotule cada entrada com um verdadeiro <label> ou aria-label.
  • Torne tudo operável por teclado; o mouse é opcional.
  • Automatize com axe-core em cada teste de componente.
  • Teste manualmente com pelo menos um leitor de tela.
  • Mantenha contraste de 4.5:1 para texto normal e indicadores de foco visíveis.
  • Execute verificações a11y no CI para que regressões nunca sejam publicadas.

Ao combinar o conhecimento WCAG com a auditoria e geração de código com IA do Cursor, você pode construir interfaces inclusivas que funcionam para todos. Deixe o Cursor criar a marcação e os testes acessíveis, então verifique os resultados com leitores de tela reais antes de publicar.