---
fonte: 01-canonicos/seed-componentes.md
versao_da_fonte: v1.43
secao: 70
titulo: "CT1 — o CENTRO DO DESENHO dentro do alvo · regra transversal (2026-08-19, estendida na sexta parte do mesmo dia) · `validacao/guarda-centro-alvo.mjs` **180 · 0 · 228** em 2 temas × 4 estados × 2 larguras"
sequencia: 76 de 98
bytes_do_corpo: 19501
md5_do_corpo: c2892400cf5997ec5ad696bf5673d079
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
---
## 70. CT1 — o CENTRO DO DESENHO dentro do alvo · regra transversal (2026-08-19, estendida na sexta parte do mesmo dia) · `validacao/guarda-centro-alvo.mjs` **180 · 0 · 228** em 2 temas × 4 estados × 2 larguras

> **Escrito em 2026-08-19.** Esta seção existe porque **o Rafael viu um defeito que nenhuma das 60+
> guardas do projeto via** — e o viu num anexo de imagem, a olho nu, num botão de 44px.

### 70.1 O achado, e quem o achou

No gate v13.3 o Rafael anexou dois recortes da barra superior do `tela-shell` com **setas vermelhas**
sobre o botão de menu (☰) e sobre o sino, e escreveu:

> *"no anexo 1, acredito que o icone nao esteja centralizado dentro da caixa, pode ver nos que apontei
> com a seta."*

**Estava certo.** Medido: o desenho estava **acima e à esquerda** do centro geométrico do botão.

**A causa era de CSS, e é banal:** o botão declarava `display:flex` **sem** `align-items` e
`justify-content`, ou herdava uma `line-height` de texto que empurrava o `svg` para cima dentro da
caixa de linha. **Cada um desses controles passava em TODAS as guardas existentes**: tinha tamanho de
alvo ≥44px (SC 2.5.8), tinha nome acessível (SC 4.1.2), tinha contraste (SC 1.4.11), tinha foco visível.
*Nenhuma delas mede onde o desenho está DENTRO da caixa.*

> **A lição, e ela é sobre o método, não sobre CSS:** *um acervo com 60 guardas verdes não é um acervo
> medido — é um acervo medido NAQUILO QUE AS GUARDAS OLHAM.* O olho do Rafael cobriu uma dimensão que
> a bateria inteira não cobria. **Toda vez que ele apontar algo com o dedo, a resposta certa não é
> consertar o item: é perguntar qual RÉGUA estava faltando.**

### 70.2 O contrato CT1

| # | Cláusula | Régua |
|---|---|---|
| **CT1-a** | Em controle **de ícone** (o desenho é todo o conteúdo), o **centro da tinta** coincide com o **centro da caixa do controle**, nos dois eixos | distância entre centros ≤ **1,00px**, tolerância **declarada** |
| **CT1-b** | A cláusula vale nos **dois** viewports de referência — 1440px e 390px | um controle pode centrar num e descentrar no outro; medir só um é medir metade |
| **CT1-c** | Controle **com rótulo** ao lado do ícone está **FORA** desta conta | ali o problema é de **composição** (alinhamento entre duas coisas), não de **centro**. Medi-lo por centro reprovaria composição correta |

> ⚠ **DIVERGÊNCIA DE NOMENCLATURA, achada em 2026-08-19 (sexta parte) e declarada aqui em vez de
> consertada em silêncio.** Esta tabela usa `CT1-a/b/c` para *(centro nos dois eixos · dois viewports ·
> rótulo fora)*, e a **guarda** usa `CT1-a/b/c` para *(centro horizontal · centro vertical · o desenho
> cabe na caixa)*. **São dois esquemas de nome para o mesmo contrato**, e as mensagens de erro que o
> operador lê vêm da guarda. **Canônico, a partir de agora, é o esquema DA GUARDA** — porque é o que
> aparece no placar. Renomear a tabela exigiria reescrever as citações do §96 do MANIFESTO, e
> renomeação sem supersede formal é pior que divergência declarada: fica como pendência **CT-P6**
> no §70.7.

### 70.3 A guarda, e os três cortes que ela precisou aprender

`validacao/guarda-centro-alvo.mjs` varre **a pasta** (nunca uma lista), mede **189 controles de ícone**
em 51 arquivos e imprime **522 controles com rótulo descartados** — o descarte sai no placar, porque
*limite que não aparece no placar é truncagem silenciosa*.

> **A PRIMEIRA EXECUÇÃO CONDENOU 37 CONTROLES, E A MAIORIA ESTAVA CERTA.** Isso não foi acidente de
> percurso: foi a guarda medindo a coisa errada. Os três cortes abaixo nasceram **antes de qualquer
> artefato ser tocado** — porque *guarda que condena artefato correto força conserto errado*, e um
> conserto errado num artefato `estável` é muito mais caro do que uma guarda que demora um dia a mais.

| corte | o que ele exclui | por quê |
|---|---|---|
| **conteúdo ≤60% da caixa E texto visível ≤3 caracteres** | `.niv`, que tem rótulo dentro de um `<span>` | ali há texto; não é controle de ícone |
| **caixa aproximadamente quadrada** (razão 0,5–2,0) | barras e faixas largas | numa caixa 10:1 o "centro" horizontal não é requisito de nada |
| **sem `justify-content: space-*`** | `.gatilho`, com `space-between` | quem declara `space-between` está pedindo **distribuição**, não centro — condená-lo seria condenar a intenção declarada |

Depois dos cortes: **15 de 16 casos reais** sobraram, e todos eram defeito de verdade.

### 70.4 O que a execução tocou, e o placar

Bloco CT1 acrescentado a **seis moldes** e **um artefato feito à mão**:

| arquivo | o que passou a valer |
|---|---|
| `shell-template.html` | `.sino`, `.avatar` → `inline-flex`; `.abre-nav`, `.sino`, `.avatar` → `align-items/justify-content: center`, `line-height: 1` |
| `painel-template.html` | `.escala-ctrl button` |
| `site-template.html` | `.b-larg` |
| `banco-data-template.html` | `.cal__nav button` |
| `banco-escolha-template.html` | `.chips-mais` |
| `banco-pessoa-template.html` | `.chip .x` (só `line-height: 1`) |
| `banco-superficies.html` | `.seed-avatar-more` (só `line-height: 1`) |

**PLACAR: 23 PASS · 0 FAIL · 28 `[n/a]`**, a **1440px e a 390px**, 189 controles medidos, **0 fora do
centro**, tolerância 1,00px declarada. Os 28 `[n/a]` são arquivos **sem controle de ícone**, nomeados um
a um no placar.

**Recorte antes/depois nos dois temas, ampliado 8×:** blocos **E1, E2 e E3** da folha
`render-audit/gate-v134/fechamento.html`.

### 70.5 SUPERSEDE de 2026-08-19 (sexta parte) — **CT-P1 e CT-P2 fechadas**: nascem **CT1-d** e **CT1-e**

> **O que este supersede revoga:** a redação anterior do §70.5, que listava CT-P1 e CT-P2 como
> pendências abertas, e a afirmação implícita de que o placar `23 · 0 · 28` cobria o contrato CT1.
> **Ele cobria um oitavo dele** — uma das duas metades de tema, num dos quatro estados.
> **A régua não mudou. O número de vezes que ela é aplicada, sim.**

#### 70.5.1 As duas cláusulas novas

| # | Cláusula | Régua | Como o estado é produzido |
|---|---|---|---|
| **CT1-d** | O centro do desenho vale **nos dois temas** | mesma tolerância de 1,00px | `data-theme` + `prefers-color-scheme`, os dois juntos |
| **CT1-e** | O centro do desenho vale em **`:hover`, `:active` e `:focus-visible`** | mesma tolerância de 1,00px | **interação de verdade**: ponteiro movido até o centro · botão do mouse **pressionado** (e solto **fora** do controle, para o clique não se completar) · tecla `Tab` apertada **uma vez por passe** para fixar a modalidade de teclado do Chromium, seguida de `.focus()` |

**Cada estado é ASSERTADO antes de valer.** A guarda pergunta ao elemento `el.matches(':hover')`,
`el.matches(':active')` e `document.activeElement === el`. Se o estado **não pegou**, a medição
**não vira PASS nem FAIL**: sai `[n/a]` com o motivo. Isso não é zelo — é a diferença entre medir e
supor: alvo coberto por elemento opaco não recebe ponteiro, e `<div role="button">` sem `tabindex`
não recebe foco. Nos dois casos um PASS seria mentira, e um FAIL seria pior.

**`[n/a]` de tema tem régua própria, e ela é COMPORTAMENTAL.** *Tema que o artefato não promete não
se mede.* Antes de medir escuro, a guarda calcula uma **assinatura de cor** — `color`,
`background-color`, `border-top-color` e `outline-color` de **todos** os elementos, reduzidos a um
hash na própria página — sob tema claro e sob tema escuro. **Assinatura idêntica = o artefato não
promete tema escuro** → `[n/a]` nomeado, nunca PASS.
*Alternativa descartada, com o motivo:* `grep` por `[data-theme="dark"]` no arquivo. Descartada
porque **comentário e texto de bancada casam com o regex** (e há lei nesta casa contra comentário que
o próprio instrumento procura), e porque um artefato pode prometer tema **só** por
`prefers-color-scheme`, sem nenhum `data-theme` escrito. *Guarda que pergunta "tem este atributo?"
mede IMPLEMENTAÇÃO; esta pergunta "muda de cor?", que é o contrato.*

**Isenção de componente INATIVO, contada e separada de defeito.** `<button disabled>` não recebe
foco **por contrato** — é a mesma isenção que a SC 1.4.3 e a SC 1.4.11 dão ao componente inativo — e
sai como `[n/a] ISENTO`. Já `<div role="button">` sem `tabindex` que não recebe foco é **defeito de
teclado (SC 2.1.1)** e sai com esse nome. *Somar os dois na mesma linha do censo esconderia um
defeito atrás de uma isenção legítima: motivo agregado esconde causa.*

#### 70.5.2 O placar, separado por tema e por estado

*Placar que não separa o que tem regra diferente mente pela média.* **Duas execuções idênticas em
cada largura** — medida que não é repetida não está verificada.

| tema / estado | PASS | FAIL | `[n/a]` | medições a 1440px | medições a 390px |
|---|---|---|---|---|---|
| claro / repouso | 23 | **0** | 28 | 189 | 188 |
| claro / hover | 23 | **0** | 28 | 189 | 188 |
| claro / ativo | 23 | **0** | 28 | 189 | 188 |
| claro / foco | 23 | **0** | 28 | 187 | 186 |
| escuro / repouso | 22 | **0** | 29 | 182 | 181 |
| escuro / hover | 22 | **0** | 29 | 182 | 181 |
| escuro / ativo | 22 | **0** | 29 | 182 | 181 |
| escuro / foco | 22 | **0** | 29 | 180 | 179 |

**AGREGADO: 180 PASS · 0 FAIL · 228 `[n/a]`** em cada largura · **1.484 candidatos e 1.480 medições
efetivas a 1440px** · **1.476 e 1.472 a 390px** · **4.080 controles com rótulo descartados** a 1440px
e **3.896** a 390px · **367 medições casaram `:focus-visible`** a 1440px, **365** a 390px ·
**0 controles fora do centro** · tolerância 1,00px declarada.

**Nenhum artefato foi alterado.** Não havia o que consertar: o bloco CT1 escrito na quinta parte já
estava certo também no tema escuro e nos três estados de interação. *Isso é FATO medido, e é
diferente de "provavelmente estava certo", que era o que a CT-P1 dizia.*

**Onde estão os `[n/a]`, um por um** — *limite que não aparece no placar é truncagem silenciosa*:

| quantos | o que é | nomeados? |
|---|---|---|
| 28 por passe claro, 29 por passe escuro | arquivos **sem nenhum controle de ícone** (bancadas de token, família de e-mail, `tela-detalhe`, `tela-referencia`) | sim, um a um no placar |
| 5 arquivos | **não prometem tema escuro**: `banco-dataviz-tokens`, `ea-assinatura`, `ea-assinatura-amostra`, `seed-dataviz-di-prova-impressa`, `seed-design-system` | sim. É por isso que o passe escuro tem 1 `[n/a]` a mais e 7 medições a menos: o `seed-design-system` tem 7 controles de ícone |
| 4 em 1.484 | **ISENTOS por componente inativo**: `button.cd__gatilho` de "Vigência do contrato" (`banco-data.html`) e `button` "Página anterior" (`tela-tabela.html`), ambos `disabled`, em 2 temas | sim, com o seletor |
| **0** | **defeitos de teclado** | — |

#### 70.5.3 O 33º defeito de instrumento do catálogo — `scroll-behavior: smooth`

Na **primeira** execução da guarda estendida sobre o acervo, 14 medições saíram `[n/a]` com o motivo
*"o ponteiro não alcança"*: os **7 `button.page-btn` do `seed-design-system.html`** apareciam com
centro em **y = 27108** numa janela de 900px, em dois passes (claro/hover e claro/ativo).

**A causa não era o artefato: era o medidor.** Três arquivos do acervo declaram
`scroll-behavior: smooth` — `banco-navegacao.html`, `seed-design-system.html` e `tela-shell.html`.
Com rolagem suave, `scrollIntoView()` **anima**, e o retângulo lido no instante seguinte ainda é o de
**antes** da rolagem.

> **A lição, e ela é geral: `animation:none` e `transition:none` NÃO cobrem `scroll-behavior`.**
> Ela é uma **terceira** forma de animar, e tem de ser desligada **por nome**. Depois do conserto
> (`html{scroll-behavior:auto!important}` junto com as outras duas), as 14 medições voltaram e os
> passes de claro/hover e claro/ativo subiram de 182 para **189** — o mesmo número do repouso.
> *14 medições perdidas por defeito do medidor teriam ido para o placar como `[n/a]` justificado.*

#### 70.5.4 A prova de que a régua morde — `render-audit/prova-ct1/`

*Guarda que não encontra nada tem de ser provada capaz de encontrar alguma coisa, e guarda reescrita
tem de ser provada capaz de reprovar — uma prova por cláusula.* Nasceram **8 artefatos de prova**,
com dado **sintético** (um `<button>` de 44×44 com um quadrado turquesa de 18×18: nenhum dado de
cliente, nenhum dado pessoal), cada um com um defeito plantado ou um caminho de `[n/a]` a disparar:

| artefato | o que ele carrega | veredito esperado |
|---|---|---|
| `prova-d-escuro.html` | 20px fora do centro **só no tema escuro** | FAIL nos 4 estados de escuro, PASS nos 4 de claro |
| `prova-e-hover.html` | 20px fora **no `:hover`** | FAIL em `hover` **e em `ativo`** — enquanto o botão está pressionado sobre o controle, ele casa `:hover` **e** `:active` |
| `prova-e-ativo.html` | 20px fora **no `:active`** | FAIL só em `ativo` |
| `prova-e-foco.html` | 20px fora **no `:focus-visible`** | FAIL só em `foco`. *Se este passasse, provaria que a modalidade de teclado não foi fixada e que a guarda mede um foco que ninguém vê* |
| `prova-sem-escuro.html` | nenhuma regra de tema | `[n/a]` "não promete tema escuro" |
| `prova-coberto.html` | botão sob um `div` opaco `position:fixed` | `[n/a]` em `hover` e `ativo`; PASS em `repouso` e `foco` |
| `prova-sem-foco.html` | `<div role="button">` sem `tabindex` | `[n/a]` "DEFEITO DE TECLADO (SC 2.1.1)" |
| `prova-inativo.html` | `<button disabled>` | `[n/a]` "ISENTO: componente INATIVO" |

**Placar de referência da pasta de prova: `40 PASS · 12 FAIL · 12 [n/a]`**, idêntico em duas
execuções, com os **4 caminhos de `[n/a]` disparando** (2× ponteiro não produz `:hover` · 2× botão
pressionado não produz `:active` · 2× isento por inativo · 2× defeito de teclado).

> **Ali, FAIL é o resultado desejado — é um teste de teste.** Se essa pasta algum dia voltar
> **0 FAIL**, quem quebrou foi a guarda, não o acervo. O número que importa é **12**.
> O `LEIA-ME.md` da pasta é autossuficiente e explica cada artefato.

#### 70.5.5 Decisões tomadas sem consultar o Rafael, declaradas e separadas

*Fato dele e inferência minha não se misturam no mesmo parágrafo.* As duas foram para o gate visual
numa seção "vete se discordar" da folha `render-audit/gate-v135/fechamento.html`.

| # | Decisão | Alternativa descartada, com o custo MEDIDO |
|---|---|---|
| 1 | **Tolerância continua 1,00px também nos estados de interação** | 2,00px, sob o argumento de que hover e foco mexem em borda. Descartada: **custo de não afrouxar = zero** — 1.480 medições passaram a 1440px e 1.472 a 390px com 1,00px. *Afrouxar sem precisar é comprar cegueira de graça* |
| 2 | **Tema que o artefato não promete sai `[n/a]`, não PASS** | Contar os 5 artefatos sem tema escuro como PASS. O resultado geométrico seria idêntico e o placar ficaria **185 PASS** em vez de 180. Descartada: *medir o que não existe e chamar de aprovado é como um placar aprende a mentir*. Custo: 5 pontos menos de placar e 5 nomes a mais na lista |

### 70.6 Prova de INÉRCIA — a dimensão antiga tem de dar o mesmo número

A guarda foi **reescrita**, e a régua (a função `classifica`, com os três cortes de candidatura, a
caixa de conteúdo, o descarte de satélite e o recorte de 1px de leitor de tela) foi **copiada sem
tocar em uma vírgula**. Motivo: *guarda reescrita que muda de resultado na dimensão antiga não
estendeu nada — trocou de assunto.*

```
TEMA=claro ESTADO=repouso node validacao/guarda-centro-alvo.mjs
→ 23 PASS · 0 FAIL · 28 [n/a] · 189 controles de ícone · 522 com rótulo descartados
```

**Reproduziu na primeira execução, exatamente**, os mesmos números do §70.3 e do §70.4. A guarda
aceita `TEMA=` e `ESTADO=` justamente para que essa prova seja **repetível por quem vier depois**, e
não uma afirmação num registro.

**Bônus da reescrita, e ele conserta um `continue` mudo:** a versão anterior descartava em silêncio
três classes de controle — invisível, minúsculo (lado < 8px) e sem conteúdo visível. Elas agora saem
**nomeadas e contadas** no *censo de descarte*: a 1440px, **4.080** com rótulo · **592** invisíveis ·
**560** minúsculos · **48** sem conteúdo visível. *`continue` mudo é a forma mais barata de mentir
num placar* — e o conserto **não** mexeu em PASS nem em FAIL, só fechou a conta.

### 70.7 Pendências que ficam

| # | Pendência | Por que fica aberta |
|---|---|---|
| **CT-P3** | A guarda mede **um estado por passe**. **Combinação** de estados — foco por teclado **e** ponteiro em cima ao mesmo tempo, que é o caso real de quem navega por `Tab` e depois encosta o mouse — não é medida | Nasce em 2026-08-19. O número de combinações cresce em fatorial e **ainda não há evidência** de que alguma delas mova tinta. Declarada, não esquecida |
| **CT-P4** | Os **três cortes de candidatura** da CT1 (conteúdo ≤60% da caixa · caixa aproximadamente quadrada · sem `justify-content: space-*`) **não têm artefato de prova próprio** em `render-audit/prova-ct1/` | Nasce em 2026-08-19. Eles nasceram porque a guarda condenou 37 controles corretos, mas **o critério em si nunca foi provado capaz de errar** — nem de excluir de menos, nem de excluir demais |
| **CT-P7** | O **eixo de PONTEIRO está em ZERO em todos os outros contratos do acervo.** Contado: dos **52 instrumentos `.mjs`** da pasta `validacao/`, **nenhum** move o ponteiro além da guarda CT1 e do `corta-recorte.mjs`, ambos desta parte. Contraste, tamanho de alvo, cor forçada e reflow medem **só repouso** | Nasce em 2026-08-19, da própria medição que fechou a CT-P2. **O caso mais provável de esconder defeito real é o CONTRASTE em `:hover`**: fundo de hover é uma cor que ninguém deste acervo jamais mediu, e ela pode estar abaixo do piso sem que nenhum placar reclame. *O eixo de FOCO, ao contrário, já era coberto por 20 dos 52 — a afirmação larga de que "nenhum instrumento media estado" foi minha e a medição a revogou no mesmo dia* |
| **CT-P6** | **Divergência de nomenclatura** entre o §70.2 (que chama `CT1-a/b/c` de *centro nos dois eixos · dois viewports · rótulo fora*) e a guarda (que chama `CT1-a/b/c` de *centro horizontal · centro vertical · o desenho cabe*) | Nasce em 2026-08-19. **Canônico é o esquema da guarda**, porque é o que sai no placar que o operador lê. Consertar a tabela exige supersede formal e reescrita das citações do §96 do MANIFESTO — trabalho de meia hora que não muda medida nenhuma, e por isso não passou na frente da guarda de colisão |
| **CT-P5** | Achado de **aparência**, não de medida: `.sino` e `.abre-nav` do `tela-shell` **não têm nenhuma resposta visual ao `:hover`** — medido, `padding`, borda, `transform` e `line-height` saem idênticos em repouso, hover e ativo; só o `outline` do foco muda | Nasce em 2026-08-19, da conferência de md5 dos recortes da folha (três recortes de estado saíram byte-idênticos, e a investigação mostrou que o **fato** é que o CSS não muda nada). **Não viola contrato nenhum hoje.** É decisão de aparência, e aparência é do Rafael: foi ao gate v13.5 como pergunta opcional |

---

