# Acessibilidade — WCAG 2.2 AA é o piso

## Critérios citados no canon

- SC 1.3.1 — Componentes §67, Componentes §68
- SC 1.3.5 — Componentes §61, Componentes §66
- SC 1.4.1 — Componentes §65, Componentes §90
- SC 1.4.3 — Mapa de cobertura §17, Mapa de cobertura §18, Mapa de cobertura §22, Componentes §cabeçalho, Componentes §58, Componentes §64, Componentes §65, Componentes §70, Componentes §71, Componentes §73, Tokens §05-b
- SC 1.4.10 — Componentes §cabeçalho, Componentes §43-b, Componentes §57, Componentes §58, Componentes §64, Componentes §72, Composição §11
- SC 1.4.11 — Mapa de cobertura §11, Mapa de cobertura §15, Mapa de cobertura §16, Mapa de cobertura §20, Mapa de cobertura §22, Componentes §cabeçalho, Componentes §48, Componentes §49, Componentes §56, Componentes §58, Componentes §59, Componentes §60, Componentes §64, Componentes §65, Componentes §66, Componentes §70, Componentes §71, Componentes §76, Composição §06, Composição §07, Dataviz §04d, Tokens §03, Tokens §05-d, Tokens §09
- SC 1.4.12 — Mapa de cobertura §cabeçalho, Mapa de cobertura §22, Componentes §cabeçalho, Componentes §72
- SC 2.1.1 — Mapa de cobertura §22, Componentes §cabeçalho, Componentes §49, Componentes §57, Componentes §64, Componentes §67, Componentes §70
- SC 2.1.2 — Componentes §68
- SC 2.2.1 — Componentes §66
- SC 2.4.3 — Componentes §62
- SC 2.4.11 — Componentes §49
- SC 2.5.7 — Componentes §cabeçalho, Componentes §06, Componentes §48, Componentes §49, Componentes §51, Componentes §57, Componentes §58, Componentes §72, Composição §05, Composição §07d, Composição §08, Composição §11
- SC 2.5.8 — Mapa de cobertura §22, Componentes §cabeçalho, Componentes §51, Componentes §65, Componentes §67, Componentes §70, Componentes §72, Tokens §09
- SC 3.2.2 — Componentes §67
- SC 3.3.1 — Componentes §59
- SC 3.3.2 — Componentes §61
- SC 3.3.7 — Componentes §02, Componentes §61, Componentes §83
- SC 3.3.8 — Mapa de cobertura §04, Componentes §cabeçalho, Componentes §03, Componentes §66
- SC 4.1.2 — Mapa de cobertura §22, Componentes §58, Componentes §62, Componentes §64, Componentes §67, Componentes §68, Componentes §70
- SC 4.1.3 — Componentes §65, Componentes §66, Componentes §67, Componentes §68

## 1.8 Acessibilidade

> Componentes v1.43 · §01 · 1.8 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/01-botao-estavel/#sec-1-8-acessibilidade

### 1.8 Acessibilidade

`<button type="button|submit">` real (nunca div clicável) · foco visível sempre · `aria-busy` no loading · `aria-label` em icon-only · contraste do rótulo AA em todas as variantes/estados (auditado nos tokens: primary 4.60:1, destructive 5.19:1) · não comunicar estado só por cor (disabled também muda cursor e remove hover; loading tem spinner).


## 1.12 Processo de avaliação de hierarquia e qualidade

> Componentes v1.43 · §01 · 1.12 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/01-botao-estavel/#sec-1-12-processo-de-avaliacao-de-hierarquia-e-qualidade-metodo

### 1.12 Processo de avaliação de hierarquia e qualidade (método — vale para qualquer componente; **7 testes** desde a v0.7)

Aplicar SEMPRE que um componente tiver níveis de ênfase; nasceu da revisão do botão (v0.1→v0.2) e vira padrão dos blocos seguintes:

1. **Teste do container 3:1 (WCAG 1.4.11):** o elemento que identifica o controle (preenchimento OU borda) precisa de contraste ≥3:1 contra a superfície. Medir, não estimar.
2. **Teste da escada de mecanismos:** entre níveis adjacentes, muda o MECANISMO (fill → tint+borda → borda → texto), nunca apenas o tom.
3. **Teste do aperto de olhos / escala de cinza:** renderizar o conjunto em grayscale — os níveis continuam distinguíveis? (Remove a muleta do matiz; simula baixa visão e daltonismo.)
4. **Teste do par:** o par mais comum (alta + média ênfase) lado a lado — a hierarquia é óbvia em 1 segundo, à distância de braço?
5. **Regra do um:** 1 alta ênfase por vista; grupos com 1 primário + N iguais de ênfase menor; >3 ações → ghost.
6. **Teste do estado atual (toggles):** alguém que chega AGORA na tela sabe se está ligado ou desligado sem clicar? Se precisa clicar pra descobrir (o "teste WebEx"), reprovou — aplicar a regra dos 3 sinais (§1.5.1).
**Método de pesquisa (v0.9, calibrado com o Rafael em 2026-08-02 sobre o histórico de 5 itens):** todo item roda **NO MÍNIMO 3 rodadas de pesquisa, sempre sequenciais e encadeadas** — cada rodada abre declarando as pontas soltas que herda da anterior e busca APENAS o que a base acumulada ainda não responde (decisões já estáveis não se re-pesquisam). Arco típico observado no histórico: rodada 1 = canon dos design systems (~60–70% das decisões); rodada 2 = prática das grandes empresas e do mercado (confirmações + ~20%); rodada 3 = normas e fora-do-circuito (menor volume, maior gravidade — foi onde entraram WCAG 3.3.8/NIST, ISO 4217, APG spinbutton, WCAG 2.5.7). Rodada extra (4ª+) por gatilho, nunca por rito: crítica do Rafael expondo lacuna (caso 0800/EUA do form-field), decisões instáveis após 3 rodadas, ou domínio de alto risco (dinheiro, dados pessoais, segurança). Uma rodada que não herda pergunta aberta é redundante e não deve acontecer.

**§1.13 — Protocolo de live region SEED (v0.12, transversal):** todo anúncio dinâmico a leitores de tela segue UM padrão (nasceu na busca D4, confirmado no textarea F3, agora governa combobox, upload e switch async): região `role="status"` (polite) + `aria-atomic="true"`, **visualmente oculta e separada do nó visual** (canais independentes — os 4 bugs do GOV.UK vieram de misturar); anúncio com **debounce ~1s** (fala na pausa, não a cada evento); **frase completa e específica** ("14 resultados para 'inversor'", "proposta.pdf enviado"), nunca fragmento; erro que exige ação imediata usa `role="alert"`; **uma live region por componente**. Consumidores: §4, §6, §8 (pending), §11, §14.

**Nota 1.13-b (v0.15 — emenda, dois regimes de anúncio):** o §1.13 nasceu para **atualizações contínuas** (contagem de busca a cada tecla), onde anunciar cada evento defasaria o visual — daí os canais separados + debounce (D4-b). **Mensagens discretas** (toast, alerta injetado, banner dinâmico) são conteúdo novo inserido UMA vez: não há defasagem possível, e o padrão universal correto (Spectrum, React Aria, Radix, Base Web, gov.br) é **o próprio nó visível ser a live region**, injetado já populado. Regra de implementação que a suite cobra nos dois regimes: **o container com `role` precisa existir no DOM ANTES do conteúdo ser inserido** — inserir container e conteúdo juntos faz leitores de tela perderem o anúncio (WCAG 4.1.3, técnica documentada). Sem esta nota, a validação executada do Bloco 3 reprovaria o padrão correto por leitura literal do §1.13. Descartado: exigir canal separado também para mensagens discretas (duplicaria o DOM sem ganho e contraria todo o mercado verificado).

7. **Teste do polegar (v0.7 — mobile):** renderizar o componente em viewport de **360px** (Android BR mais comum) com teclado virtual aberto (altura útil ~450px): todos os alvos ≥44px ou com hit-area estendida documentada; nada essencial coberto pelo teclado; ordem de leitura íntegra em coluna única; texto do campo ≥16px (anti-zoom iOS); zoom 200% não quebra (WCAG 1.4.4). O preview traz a faixa 360px pré-renderizada, como a faixa grayscale.

O preview traz a faixa em escala de cinza pré-renderizada para o teste 3 ser feito a olho.

---



## 2.9 Acessibilidade

> Componentes v1.43 · §02 · 2.9 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/02-form-field-campo-de-formulario-estavel-validado/#sec-2-9-acessibilidade

### 2.9 Acessibilidade

`<label for>` sempre (nunca div-rotulada) · `aria-describedby` na ordem **erro ANTES da ajuda** (`"err-id help-id"` — AUI: o erro é anunciado primeiro) · `aria-invalid` só após validação real, NUNCA no estado inicial (W3C ARIA21), e SEMPRE com valor explícito `"true"` — nunca `toggleAttribute`, que grava o atributo com valor vazio: `[aria-invalid="true"]` do CSS não casa e a borda de erro some (bug flagrado na validação executada de 2026-08-03) · mensagem de erro com prefixo "Erro:" visually-hidden · erro dinâmico anunciado (região `aria-live="assertive"` ou `role="alert"`) · ícones de estado com par textual (1.4.1) · foco: anel, nunca borda engrossada · contraste: TODOS os pares da §2.5 medidos ≥4.5:1 texto e ≥3:1 não-texto · zoom 400%: layout de coluna única resiste · toque ≥44px no md.


## 47.2 Decisões MK1–MK12

> Componentes v1.43 · §47 · 47.2 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/47-pagina-institucional-esqueleto-camada-de/#sec-47-2-decisoes-mk1-mk12

### 47.2 Decisões MK1–MK12

| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| **MK1** | **Quatro tipos de página, cada um com contrato próprio:** **institucional** (quem somos, carreira) · **serviço** (autônoma, ver MK8) · **conteúdo** (artigo, vídeo, case) · **utilitária** (contato, obrigado, erro, legal) | O site atual tem 22 páginas **sem tipagem nenhuma** — mesmo esqueleto e mesmo título para todas, o que é a causa de o título genérico nunca ter incomodado ninguém: não havia onde declarar outro | Contrato único para tudo (produziu exatamente o site atual) · tipagem por assunto em vez de por comportamento (assunto muda, comportamento não) |
| **MK2** | **Toda página declara: um `<h1>` único e REAL · `<title>` próprio · meta descrição própria · URL canônica · `lang="pt-BR"`** | Linha de base: 6 de 22 páginas sem `<h1>` real, e **nenhuma** com título ou descrição próprios. *Classe de defeito a evitar:* **capacidade construída e nunca ligada** — a classe `SetSeo` do site atual estava pronta e nunca foi instanciada, o que é pior que não existir, porque parece resolvido | `<h1>` simulado por classe CSS (quebra 1.3.1 e a hierarquia de leitura) · título por template genérico |
| **MK3** | **Toda `<img>` de conteúdo tem `alt` descritivo; decorativa tem `alt=""` EXPLÍCITO** | 419 tags, zero `alt`, e **nem a distinção** entre conteúdo e decoração sendo feita. O `alt=""` explícito é o que diz ao leitor de tela "pule isto de propósito" — omitir o atributo faz o leitor tentar adivinhar pelo nome do arquivo | `alt` opcional · `alt` gerado do nome do arquivo |
| **MK4** | **Zoom NUNCA é bloqueado.** `maximum-scale` e `user-scalable=no` são proibidos | Está no ar hoje em todas as páginas, e o público massivo é celular. Bloquear zoom não é preferência estética: é impedir alguém de ler | — (não há caso legítimo) |
| **MK5** | **Toda região de navegação é `<nav>` com nome acessível único**, e o nome não contém a palavra "navegação" | Zero `<nav>` no site inteiro. Herda o **SH2** do §46 sem alteração — a página institucional tem cabeçalho, mega menu, trilha e rodapé, todos navegação | `div`/`ul` genérico · `nav` anônimo |
| **MK6** | **Cabeçalho institucional em TRÊS MODOS:** **completo** (institucional e conteúdo: itens de nível 1 expostos + mega menu) · **reduzido** (**serviço**: logo + uma ação primária + gatilho do mega menu, sem os itens de nível 1 expostos) · **mínimo** (utilitária: logo e saída) | **Resolve um conflito medido.** Página sem barra de navegação converte **até 2×** mais em teste controlado, e a recomendação de mercado é remover a navegação da página de conversão. Mas a página de serviço **precisa pertencer ao site** — é isso que evita manter landing pages paralelas, que é o requisito do Rafael. O modo reduzido mantém a navegação a um toque **sem competir com o CTA** | Remover a navegação (a página deixa de pertencer ao site e você mantém duas coisas) · cabeçalho completo na página de serviço (paga o custo de conversão sem necessidade) |
| **MK7** | **Mega menu: *disclosure de links*, nunca `role="menu"`.** Máximo **2 colunas**, cada grupo com título próprio, imagem opcional por item | O **MN1** já decidiu a fronteira semântica das quatro saídas, e o §46 já consome a saída *disclosure*. O teto de 2 colunas vem do inventário: o mega menu atual usa 3 grupos, e o único com 2 colunas é "soluções" — que é o caso real de agrupamento duplo (técnicas + fotovoltaico por segmento) | `role="menu"` (anti-padrão testado, corpus Roselli/APG) · 3+ colunas (o inventário não sustenta a necessidade) |
| **MK8** | **PÁGINA DE SERVIÇO AUTÔNOMA, em cinco movimentos: conversão → dor → prova → escopo → conversão.** Precisa se sustentar para quem cai de anúncio, sem contexto anterior. **Um sinal mínimo de prova acompanha o CTA de topo — um número, não parede de logos.** **O formulário existe UMA VEZ; o CTA do fim é âncora que leva até ele** | Sequência definida pelo Rafael. Dor → prova → escopo é **PAS** (problema, agitação, solução), o mesmo framework que a skill `seed-ds-mensagem` já usa; a conversão vem **antes** porque quem chega de tráfego pago já está no meio do PAS — fazer essa pessoa rolar para achar o formulário friciona quem já está pronto, e quem não está rola e encontra a explicação. O sinal de prova no topo tem número: **contagem de clientes nomeada acima da dobra produziu +22%**, o maior ganho de qualquer formato de prova testado. **Formulário único** porque duplicá-lo cria duas fontes de verdade, duas medições e o erro clássico de a pessoa preencher o de baixo depois que o de cima já falhou | Dor → prova → escopo → conversão (minha proposta inicial: friciona quem já decidiu) · dois formulários na mesma página · parede de logos e metodologia no topo |
| **MK9** | **Variante de PÚBLICO dentro da página de serviço, não página duplicada.** No fotovoltaico o recorte é por **SEGMENTO** — casa, comércio, indústria, área rural | Achado do estudo: **o site atual já resolve exatamente assim**, com quatro páginas por segmento. Segmento é mais operacional que "B2B/B2C" — o visitante se reconhece em "na minha indústria", não em "sou B2B" | Duas páginas B2B/B2C do mesmo serviço (competem entre si no buscador e dobram a manutenção) · uma página tentando falar com os dois ao mesmo tempo |
| **MK10** | **Dados estruturados por TIPO de página:** `Organization` na home e na institucional · **`Service` em cada página de serviço**, com `serviceType`, `areaServed` e `provider` apontando para a `Organization` · `BreadcrumbList` onde há trilha · `FAQPage` **só onde há FAQ real e visível** | O schema.org tem o tipo `Service` exatamente para isto, e página de serviço é onde a maioria subimplementa. A regra do Google é dura: **nunca marcar conteúdo que não está visível ao usuário** — marcação fabricada é pior que marcação ausente | `LocalBusiness` em toda página (dilui o sinal de entidade) · `FAQPage` sem FAQ visível na página |
| **MK11** | **`areaServed` declara MG, ES e BA.** A marcação **nunca afirma sede onde não há** | A orientação é explícita: um negócio não deve fingir ter instalações de onde não opera de fato. A SEED atende três estados a partir de duas bases | Uma `LocalBusiness` por município atendido (fabricaria presença física e é o erro mais comum de marcação local) |
| **MK12** | **Orçamento de peso por página, MEDIDO no gate:** HTML < 50KB · CSS < 60KB · JS < 150KB comprimido · imagens acima da dobra < 200KB. **Metas de campo:** LCP < 2,5s · INP < 200ms · CLS < 0,1. **Toda imagem, vídeo e iframe com dimensão explícita** | Orçamento de referência para mobile, e as metas são os limiares de "bom" avaliados no percentil 75 de usuários reais. Dimensão explícita é o que impede o deslocamento de layout. Linha de base: esqueleto fixo de 2,5 MB e páginas de até 8,8 MB de mídia | Orçamento aspiracional sem medição no gate (não é orçamento, é desejo) |


## 64.1 Os contratos

> Componentes v1.43 · §64 · 64.1 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/64-bancada-e-tema-regras-transversais-bt1-bt6-f7-5/#sec-64-1-os-contratos

### 64.1 Os contratos

| # | Contrato | Por quê |
|---|---|---|
| **BT1** | A barra de controles de **todo artefato produzido** — bancada OU tela — é **fixa no topo**: `position:sticky; top:0`, com faixa de fundo própria e sombra de separação | Verbatim do gate: *"deixe sempre o topo ... sempre travado no topo pra quando rolar a página ele ficar fixo no topo"*. Bancada é comprida por definição — ela imprime todos os estados. Se o seletor de tema rola para fora, avaliar o tema escuro no meio da página exige subir, trocar, descer e reencontrar o lugar |
| **BT2** | Toda **bancada** oferece **seletor de largura de página** — `Total · 1280 · 1024 · 768 · 390px` — limitando o `max-width` do contêiner de conteúdo | Avaliar aperto sem redimensionar a janela. Sem isto, "cabe em 390?" vira uma pergunta que só o autor da suíte consegue responder, e o gate humano fica dependente do placar |
| **BT3** | **O par de estado tem de sobreviver à inversão.** Fundo e texto de um mesmo estado medem **≥ 3,00** de contraste nos DOIS temas | É a formulação MEDÍVEL do GI2. O defeito real nunca é "usou primitiva": é primitiva de um lado e semântico do outro — o par se separa quando um dos dois inverte e o outro não |
| **BT4** | Primitiva de rampa em **superfície de marca** (herói, trilho, selo) é **tolerada** — desde que fundo E texto sejam do mesmo par fixo, e o número seja **impresso** no placar | Superfície de marca é justamente aquilo que não deve inverter. Proibir a primitiva ali seria proibir a marca. Mas tolerar em silêncio faz o achado sumir: o placar imprime `primitiva só em superfície de marca` com o pior par medido |
| **BT5** | **Atributo de CONTRATO não se traduz** — `data-theme="dark"`, não `data-tema="escuro"` | Regra já escrita em §63.7, repetida aqui porque é da mesma fronteira. O idioma do projeto é pt-BR; o gancho do gêmeo de tokens não é escolha nossa |
| **BT6** | Medição de tema roda em **página recém-carregada**, e a suíte declara isso | Número que depende de quais guardas rodaram antes não é medição, é resíduo (a prova está em §64.4) |
| **BT7** | **Nenhuma bancada rola na horizontal a 320px.** O que pode rolar é a parte que exige layout bidimensional — tabela de dados, grade de glifos —, e só dentro de uma **região focável e rotulada** (`role="region"` + `tabindex="0"` + `aria-label`) | **SC 1.4.10 Reflow (AA).** A norma proíbe a **página** rolar nas duas direções e abre exceção nomeada para *"partes do conteúdo que exigem layout bidimensional"*. Mas região rolável **sem foco de teclado não se rola sem mouse**, e sem nome a AT anuncia uma região anônima. Nasceu no rito de promoção de 2026-08-17: **as quatro bancadas novas transbordavam a 320px**, e três delas também a 390 ou 768 — ninguém tinha medido abaixo de 1200 |

> ### SUPERSEDE FORMAL — o estado de VERIFICAÇÃO do BT1 e do BT2 (acrescentado em 2026-08-20, décima parte)
> Esta subseção define os contratos; ela **não** os declara verificados. O estado de verificação de
> cada um, em 2026-08-20, é:
>
> · **BT1 — VERIFICADO POR INSTRUMENTO.** `validacao/guarda-barra-fixa.mjs`, de **alvo de pasta**,
>   mede as quatro cláusulas BT1-a…d em todo `.html` do diretório: **33 PASS · 0 FAIL · 18 [n/a]** a
>   1440×900 **e** a 320×900. **A especificação completa, o censo dos onze reprovados com número, o
>   conserto canônico e a pasta de prova `render-audit/prova-bt1` estão em §74** — que SUPERSEDE
>   qualquer afirmação anterior de que a pendência **BT-P2** estivesse fechada. *A BT-P2 havia sido
>   declarada fechada em 2026-08-17 sobre uma lista escrita à mão de **dez** artefatos, num acervo de
>   **trinta e quatro**; onze reprovavam.*
>
> · **BT2 — NÃO VERIFICADO.** Nenhum instrumento mede se a bancada oferece seletor de largura de
>   página. A `guarda-barra-fixa.mjs` declara explicitamente **não** julgar a composição da barra.
>   Fica como pendência **BT-P3** (§74.9). *Este parágrafo existe para que "BT2 está escrito" nunca
>   volte a ser lido como "BT2 está cumprido" — que é exatamente o erro que a BT-P2 cometeu.*

---

