---
fonte: 01-canonicos/seed-componentes.md
versao_da_fonte: v1.43
secao: 08
titulo: "Switch — `estável` · validado pelo Rafael em 2026-08-03 (preview v0.13 + suite de 63 testes)"
sequencia: 10 de 98
bytes_do_corpo: 6764
md5_do_corpo: 91466a2faf0d71f68c9783f0eab3e095
gerado_por: 06-validacao/geradores/gen-camada-ia.py
nota: fatia GERADA — o corpo abaixo é byte a byte o trecho do canônico; edite o canônico, nunca esta fatia. Canônico inteiro em https://ds.seed.eng.br/01-canonicos/seed-componentes.md
---
## 8. Switch — `estável` · validado pelo Rafael em 2026-08-03 (preview v0.13 + suite de 63 testes)

> **Base (3 rodadas, 2026-08-02):** R1 — NN/g toggles (efeito imediato, nunca com submit), UX Movement (sinal só no ON; system state × contextual state), UXTweak. R2 — M3 ("the preferred way to adjust settings on mobile"), iOS HIG, padrões de pending async. R3 — APG switch role, Roselli (mixed inválido), TalkBack/checkbox+role. Decisões K1–K6 conferidas pelo Rafael no consolidado de 2026-08-02.

### 8.1 Papel, fronteiras e a exceção declarada

Binário de AÇÃO com efeito IMEDIATO: liga e a coisa acontece (notificação ativada, módulo habilitado). **Exceção formal ao G4 (§7.2):** o G4 proíbe mudança de contexto ao selecionar VALOR; o switch é AÇÃO por contrato — o efeito imediato é a identidade (NN/g), e por isso ele NUNCA aparece em formulário com botão salvar (lá o binário é checkbox — régua §7.1 emendada). Filtros de busca/lista são estado contextual → checkbox, não switch.

### 8.2 Decisões K1–K6

| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| K1 | Efeito imediato sempre; proibido em form com submit | NN/g: misturar = usuário não sabe se salvou | Switch "que espera o salvar" (é um checkbox fantasiado) |
| K2 | Async: estado `pending` com spinner NO próprio switch; falha → REVERTE + toast com erro | Silêncio ou delay sem sinal = loop de reenvio | Confirmar otimista sem reverter (mente o estado) |
| K3 | Sinal de cor só no ON (`--seed-switch-on-bg` = action-primary); OFF neutro (cinza), nunca vermelho; nunca "ON/OFF" escrito no trilho | UX Movement: off colorido vira comando ambíguo; herda os 3 sinais do §1.5.1: cor + posição do thumb + movimento | Verde/vermelho (semáforo em setting é alarme falso) |
| K4 | Rótulo: o que o ON faz, verbo no infinitivo ou substantivo, ≤3 palavras ("Notificações por e-mail", "Modo escuro"), e NUNCA muda com o estado | APG: rótulo que muda quebra a referência do leitor de tela | "Ativado"/"Desativado" como rótulo dinâmico |
| K5 | Implementação: `<input type="checkbox" role="switch">` nativo; Space alterna; `mixed` é INVÁLIDO no role (vira false) | Roselli/APG; checked nativo funciona em todo AT; TalkBack anuncia certo | Div+ARIA do zero (reinventa foco/teclado) |
| K6 | Mobile/app: switch é o controle padrão de settings (M3/iOS); grupos de switch usam fieldset+legend (§2.6b) | Convenção de plataforma | Checkbox em lista de settings de app (estranho ao OS) |

### 8.3 Anatomia, tokens e estados

Trilho 44×24 + thumb 20 (hit area 44×44). Tokens de componente — **todos medidos em 2026-08-26, e a medição mudou dois valores da redação anterior**: `--seed-switch-off-bg` (**light cinza-500 `#788F9D` = 3,38:1 · dark cinza-600 `#617683` = 3,92:1**), `--seed-switch-on-bg` (= `--seed-action-primary`: turquesa-600 `#098475` **4,60:1** light / turquesa-**300** `#66D1C2` 10,16:1 dark), `--seed-switch-thumb` = o thumb DESLIGADO (branco / cinza-100) e **`--seed-switch-thumb-on`** = o thumb LIGADO (= `--seed-text-on-action-primary`: branco / `#00352F`), foco = anel padrão.

> ⚠ **O QUE A MEDIÇÃO ACHOU, e por que a redação anterior não podia achar.** A linha antiga mandava *"3:1 sobre a página: medir na produção do preview"*. A medição foi feita e deu **1,91** para o cinza-300 — reprovando a própria cláusula. Mas o defeito maior é que **medir contra a página é medir metade do componente**: o thumb é BRANCO, e branco sobre cinza-300 dá os mesmos 1,91 — *o thumb sumia dentro do trilho*, e a posição do thumb é o único canal que separa ON de OFF (K3: nunca "ON/OFF" escrito no trilho). **Cinza-500 conserta os dois pares de uma vez** (3,38 contra a página e 3,38 contra o thumb) e é o primeiro degrau da escala que atende — o cinza-400 ainda reprova, com 2,53. Nenhuma cor nova entrou (§14.1).
> ⭐⭐ **E no tema ESCURO quem reprovava era o estado LIGADO, não o desligado** — thumb cinza-100 sobre o trilho ligado dá **2,25**. Ninguém tinha visto porque a cláusula só mandava medir o OFF. A saída também não precisou de cor nova: *"o que vai sobre a superfície de ação"* já é um par medido e canonizado, o `--seed-text-on-action-primary`, e o thumb de um switch ligado é exatamente isso — **7,39:1**. No tema claro os dois thumbs coincidem em branco, então **a regra é uma só, expressa em dois tokens que colapsam no claro**.
> ⚠ **Dois erros de NOME nesta mesma linha, consertados junto:** ela apontava para `--seed-action-primary-bg`, que é o **token fantasma** da FF-P2/FF-P3 — removido do acervo —, e dizia "turquesa-400 sobre dark" quando `action-primary` no escuro é `#66D1C2` = turquesa-**300**. ⭐ *Spec que aponta para nome morto passa em toda guarda de valor, porque não há valor nenhum para conferir.* Estados: off · on · off-hover · on-hover · focus · pending (spinner 14px no thumb, `aria-busy`) · disabled · readonly (§2: exibe sem permitir — borda, não opacidade). Movimento do thumb: 120ms `--seed-ease-out`; `prefers-reduced-motion`: sem animação, posição muda seca.

### 8.4 Microcopy e live region

Rótulo à esquerda, switch à direita (padrão settings). Ajuda opcional abaixo do rótulo explica a CONSEQUÊNCIA ("Você receberá o resumo semanal"). Async: live region §1.13 anuncia "Notificações ativadas" / falha `role="alert"`: "Não foi possível ativar. Tente novamente." — e o switch VOLTA.

### 8.5 Código — essência

```html
<label class="seed-switch-row">
  <span class="seed-switch-label">Notificações por e-mail</span>
  <input type="checkbox" role="switch" class="seed-switch">
</label>
```
```css
.seed-switch{appearance:none;width:44px;height:24px;border-radius:999px;background:var(--seed-switch-off-bg);position:relative;cursor:pointer;transition:background .12s}
.seed-switch::after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:999px;background:var(--seed-switch-thumb);transition:left .12s var(--seed-ease-out)}
.seed-switch:checked{background:var(--seed-switch-on-bg)}
.seed-switch:checked::after{left:22px}
.seed-switch:focus-visible{outline:none;box-shadow:var(--seed-focus-ring)}
@media (prefers-reduced-motion:reduce){.seed-switch,.seed-switch::after{transition:none}}
```

### 8.6 Os 7 testes

1. Trilho off 3:1 sobre página (medir no preview). 2. Estado por cor + POSIÇÃO do thumb + movimento (3 sinais §1.5.1 — o teste WebEx que originou a regra). 3. Grayscale: posição do thumb carrega sozinha. 4. Par off × on distinguível a 1s (posição). 5. Um switch = uma ação. 6. Estado visível sem interação. 7. 44px de alvo; row inteira clicável. ✅

