Ir ao conteúdo
SEED engenhariaDesign System

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 c2892400

Tí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/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, .avatarinline-flex; .abre-nav, .sino, .avataralign-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 corcolor, 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 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 umlimite 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: smoothbanco-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

Esc