Ir ao conteúdo
SEED engenhariaDesign System

Componentes

71. HV1 — a RESPOSTA AO PONTEIRO e o PRESSIONADO DISTINTO

seed-componentes.md v1.43 · §71seção 77 de 9871-hv1-a-resposta-ao-ponteiro-e-o-pressionado.md · MD5 dcc00bad

Título completo no canon: HV1 — a RESPOSTA AO PONTEIRO e o PRESSIONADO DISTINTO · regra transversal (2026-08-19, sétima parte) · validacao/guarda-resposta-ponteiro.mjs

Escrito em 2026-08-19. Esta seção existe porque o Rafael recusou uma pergunta que eu não devia ter feito — e a recusa é uma correção de método, não de gosto.

71.1 A pergunta que criou o contrato#

Na folha do gate v13.5 eu registrei um achado e o classifiquei como "decisão de aparência, e aparência é sua": o sino e o botão de menu (☰) da barra superior do tela-shell não mudavam nada quando o mouse passava em cima. O Rafael respondeu:

"me questiono, nao deveria existir uma ação/efeito para cada tipo para existir essa diferenciação? o que o mercado sugere? pesquise e aplique, nao é decisao minha."

A lição, e ela é sobre a fronteira entre gosto e engenharia: eu tratei como preferência uma pergunta que a pesquisa responde. A regra deste projeto já dizia "pergunta que a pesquisa responde não é pergunta" — e eu a violei empurrando para o gate uma decisão que tem canon de mercado convergente, resposta verificável e norma aplicável. Aparência é do Rafael. CONVENÇÃO DE INTERAÇÃO é pesquisa. Distinguir as duas é o trabalho.

71.2 O RITO de três rodadas, com as fontes#

rodada fonte o que ela estabelece
R1 canon Material Design 3 — state layers estado é um véu com opacidade por estado: camadas distintas, não a mesma
R1 canon Adobe Spectrum — states registry hover e down (pressionado) são estados INDEPENDENTES
R1 canon eBay Playbook hover 8%, press 12% — o pressionado é o sobrevoo mais 4%; a mudança tem de ser "subtle and distinct" e "consistent across components"
R2 mercado Sainsbury's Design System seis estados de fundação, com pressed separado de hover, os dois "a darker shade of the brand colour" — mesma direção monotônica
R2 mercado Carbon (IBM) estado interativo tem nome de token de propósito (layer-hover, background-hover, field-hover), não valor solto
R2 stack shadcn/ui — o stack de produção do ERP da SEED define hover: em todas as variantes de botão e não define active:. O stack mais usado do mercado subespecifica o pressionado — e este acervo herdou isso
R3 normas WCAG 2.2 a WCAG NÃO exige estado de sobrevoo: "WCAG does not require user interface components to have hover states"
R3 normas SC 1.4.3 e SC 1.4.11 se o estado existe, a tinta tem de manter o piso de contraste contra o NOVO fundo: ≥4,5 com texto, ≥3,0 se for só ícone

A conclusão separa o que é norma do que é convenção — e é essa separação que o contrato carrega: HV1-a e HV1-b são convenção de mercado, canon convergente em cinco fontes, logo decisão de engenharia. HV1-c é NORMA, e é uma obrigação que só nasce porque se acrescentou o estado: quem cria um fundo novo cria a obrigação de medir o contraste sobre ele. É por isso que as três moram na mesma guarda: separá-las permitiria acrescentar hover e esquecer o contraste dele — que é exatamente a pendência CT-P7, aberta na parte anterior.

71.3 O contrato#

# Cláusula Régua Natureza
HV1-a O controle ativo responde ao ponteiro a captura do elemento sob :hover difere da captura em repouso convenção
HV1-b O pressionado é distinto do sobrevoo a captura sob :active difere da captura sob :hover convenção
HV1-c A tinta sobrevive ao fundo novo color × fundo resolvido ≥ 3,0 (só ícone, SC 1.4.11) ou ≥4,5 (com texto, SC 1.4.3), no sobrevoo e no pressionado norma

Componente INATIVO está ISENTO e sai CONTADO. Controle disabled/aria-disabled não deve responder ao ponteiro — exigir-lhe resposta seria exigir mentira. É a mesma isenção que a SC 1.4.3 e a SC 1.4.11 dão ao componente inativo.

Fronteira declarada, e é DECISÃO, não economia: a guarda roda a 1440px. :hover é capacidade de ponteiro; em aparelho de toque não existe sobrevoo, e a forma correta de tratar isso em CSS é @media (hover: hover). Medir sobrevoo numa janela de 390px de navegador de mesa mediria uma situação que o usuário de celular nunca vive. Cobrir toque é medir outra coisa — o pressionado por toque — e é a pendência HV-P2.

71.4 O JUIZ É O PIXEL — e isso foi comprado com um defeito de instrumento#

A primeira versão da guarda julgava por getComputedStyle: montava uma assinatura com as propriedades que pintam e reprovava quando ela não mudava. Na primeira execução sobre o acervo, a própria guarda se desmentiu: o button.linha-abrir do tela-tabela saiu com assinatura idêntica entre repouso e sobrevoo, e as duas capturas do elemento eram diferentes. A resposta visual estava fora do alcance da assinatura — no fundo do ancestral (tr:hover pinta a linha inteira), em ::before/::after e em descendentes mais fundos que o primeiro nível.

LIÇÃO, e ela vale para toda guarda desta casa: propriedade computada do ELEMENTO não descreve a APARÊNCIA do elemento. O que o usuário vê é a composição — ancestral, pseudo-elemento, descendente, empilhamento. A assinatura mede o que o CSS declara no nó; o contrato fala do que a tela mostra. Quando o contrato é sobre aparência, o juiz é o pixel.

A guarda captura o elemento nos três estados e compara os bytes. A assinatura de estilo continua sendo calculada, mas virou diagnóstico, e conta dois casos que o pixel sozinho não sabe nomear: "declarou e não aparece" (regra de hover que o olho não vê) e "apareceu e não declarou".

O REPOUSO é medido com o ponteiro FORA DA PÁGINA, em (-5, -5), e isso foi verificado: ali document.querySelectorAll(':hover') devolve zero elementos, enquanto o canto inferior esquerdo do tela-shell deixa 2 e o canto superior direito deixa 3. Repouso medido com o ponteiro dentro da página não é repouso.

71.5 O CENSO — o tamanho real do problema, medido#

1.410 controles ATIVOS, 51 arquivos, 2 temas, 1440px, 1.410 medições efetivas, 4.230 capturas:

cláusula reprovações proporção
HV1-a — sem nenhuma resposta ao ponteiro 619 44% dos controles ativos
HV1-b — pressionado indistinguível do sobrevoo 1.350 96%
HV1-c — contraste abaixo do piso no estado novo 90 6% — e estes são violação de norma HOJE

Placar: 0 PASS · 90 FAIL · 12 [n/a]. Nenhum dos 51 arquivos passa. E isso confirma, com número, o que a rodada R2 previu: o stack de mercado subespecifica o pressionado, e o acervo herdou.

As classes mais frequentes, para que a fila de trabalho já esteja pronta: HV1-abutton (174) · button.seed-btn (72) · summary (48) · button.no (26) · button.b-larg (24). HV1-bbutton (196) · button.seed-btn (120) · button.btn (88) · button.icell (68) · summary (48). HV1-cbutton.page-btn (28) · button.btn (22) · button.linha-abrir (20) · button.acc-trigger (12) · button.btn-focus (4) · button.pg-btn (2) · button.fachada (2).

71.6 A GRAMÁTICA — HV1-d, e os dois tokens que nasceram#

Gêmeos de token vão a v1.18. Dois semânticos de propósito, sem nenhum hex novo: os valores são os da escala de superfície que o acervo já pratica.

token claro escuro propósito
--seed-action-neutral-hover #F2F6F9 #141D23 sobrevoo de controle neutro, sobre qualquer superfície neutra
--seed-action-neutral-active #E3EBF0 #0B1419 pressionado de controle neutro — um passo adiante, na mesma direção

Por que NÃO se reusou action-ghost-hover, e o motivo é medido: no tema escuro ele vale #1D272D, que é exatamente surface-raised. Um controle neutro posado sobre superfície elevada — a barra superior do shell — teria hover invisível no escuro. O token não está errado para o propósito dele (botão fantasma sobre a PÁGINA); está errado para este outro propósito. Consumidores medidos de action-ghost-hover hoje: 2 arquivos (banco-componentes, banco-tokens), nos dois sobre a página — nenhum sobre superfície elevada. Nada muda para eles.

Gramática por família de base:

base do controle :hover :active
superfície neutra action-neutral-hover action-neutral-active
marca chapada (surface-brand-strong, o .avatar) surface-brand-deep + text-on-brand-deep (par medido 6,32 claro / 9,37 escuro) o mesmo fundo + anel interno de text-on-brand-deep — muda o pixel sem trocar o fundo, o que preserva o contraste já medido
rampa da marca (o .rail-item) chrome-overlay-hover chrome-overlay-active — já existiam, véu branco medido em composição

Alternativa descartada 1 — véu (box-shadow: inset), que é o mecanismo do Material Design 3. Descartada por motivo medido, não estético: os instrumentos desta casa leem background-color para compor contraste (o fundoResolvido da guarda HV1 aborta em imagem/gradiente, e o contraste-composicao trata gradiente como caso especial). Um véu tornaria o contraste do estado NÃO MEDÍVEL. Trocar um contrato medido por um contrato bonito é o oposto do que este projeto faz. Alternativa descartada 2 — action-primary-active no pressionado do avatar. Medido: no tema escuro ele vale #11B0A0 e o branco sobre ele mede 2,66 — reprovaria a SC 1.4.11.

71.7 O que a execução tocou nesta parte, e o defeito que a guarda pegou EM MIM#

Bloco HV1 acrescentado a shell-template.html (.topbar button, .topbar .avatar), com os quatro nomes de token na lista NOMES do gen-shell.py. tela-shell.html regerado.

medida antes depois
HV1-a no tela-shell, por tema 10 8
HV1-b no tela-shell, por tema 16 12
HV1-c no tela-shell 0 0
CT1 no tela-shell (regressão) 8 · 0 8 · 0, 2 temas × 4 estados

A guarda pegou, no mesmo minuto, um defeito que eu acabei de introduzir. A primeira versão do bloco escrevia .avatar:hover (especificidade 0,2,0), que perde de .topbar button:hover (0,2,1). Resultado: o avatar recebia o fundo neutro e ficava com tinta branca sobre #F2F6F9 — a guarda mediu 1,09 contra o piso 4,5 e reprovou na hora. .topbar .avatar (0,3,1) conserta. Este é o argumento inteiro a favor de a cláusula de contraste morar na MESMA guarda que a de resposta ao ponteiro: acrescentar estado é criar fundo novo, e fundo novo pede medida — no mesmo passe, não na próxima sessão.

71.8 Pendências#

# Pendência Por que fica
HV-P4 A gramática HV1-d ainda não foi aplicada ao acervo inteiro: 619 casos de HV1-a e 1.350 de HV1-b seguem abertos fora do tela-shell É mudança transversal em ~34 artefatos e 20 moldes, e cada um exige regeneração mais medição de contraste do estado novo. A fila está pronta e impressa por classe no §71.5 — não é buraco, é lista de trabalho com número. Fazer às pressas produziria o defeito que a guarda acabou de pegar em mim, 34 vezes
HV-P5 Os 90 casos de HV1-c são violação de norma HOJE e concentram-se em 7 classes (page-btn, btn, linha-abrir, acc-trigger, btn-focus, pg-btn, fachada) É a prioridade absoluta da próxima parte, acima de HV-P4: ali o hover já existe e já mata o contraste. Sete classes, não 1.350 controles
HV-P1 <a href> sem classe de botão não entra na população link tem convenção própria (sublinhado) e é população inteira diferente
HV-P2 toque não é medido :hover é capacidade de ponteiro; cobrir toque é medir o pressionado por toque, que é outra régua
HV-P3 a guarda compara aparência, não legibilidade da mudança uma diferença de 1 unidade num canal de cor passa em HV1-a e é invisível ao olho. O piso perceptual não existe ainda

Esc