Componentes
70. CT1 — o CENTRO DO DESENHO dentro do alvo
seed-componentes.md v1.43 · §70seção 76 de 9870-ct1-o-centro-do-desenho-dentro-do-alvo-regra.md · MD5 c2892400Título completo no canon: 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/cpara (centro nos dois eixos · dois viewports · rótulo fora), e a guarda usaCT1-a/b/cpara (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 · 28cobria 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:noneetransition:noneNÃO cobremscroll-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.mdda 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 |