Ir ao conteúdo
SEED engenharia

Componentes

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

estávelseed-componentes.md v2.00 · §71seção 146 de 18471-hv1-a-resposta-ao-ponteiro-e-o-pressionado.md · MD5 dfdeac9c

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

Em uma frase: TODO controle responde ao ponteiro e o PRESSIONADO difere do sobrevoo: véu de 8 % e de 12 %, medido em pixel.

Vocabulário desta seção: véu

O que esta seção NÃO cobre: A guarda roda a 1440 px: :hover é capacidade de ponteiro, e a fronteira está declarada. — se é disto que você precisa, não preencha em silêncio: gere o pedido de cobertura (https://ds.seed.eng.br/pedidos/).

(decisão registrada — ver Histórico da seção)

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 SIGA 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#

(decisão registrada — ver Histórico da seção)

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 em 2026-08-19: 2 arquivos (banco-componentes, banco-tokens), nos dois sobre a página — nenhum sobre superfície elevada; nada mudava para eles. Errata de 2026-09-15 (releitura HV-P4, MANIFESTO §225): a afirmação envelheceu — a banco-componentes passou a exibir ghost/outline dentro de .card (surface-raised), e a HV1 mede hoje 5 controles no escuro com "declarou e não aparece" (rgba(0,0,0,0) → #1D272D = surface-raised); banco-tokens foi removida (D-3, §210.4). A regra por superfície: ghost/outline dentro de cartão consome action-neutral-hover / action-neutral-active (sobre raised escuro: #141D23 → #0B1419, visível e monotônico), e action-ghost-* fica para o botão fantasma sobre a página. É o mesmo raciocínio deste parágrafo, aplicado ao outro lado.

Gramática por família de base:

base do controle :hover :active
superfície neutra — controle sobre surface-raised ou surface-page (a barra superior do shell, o cartão, a página) action-neutral-hover action-neutral-active
superfície neutra suave — controle transparente posado sobre a casca (surface-subtle, o canvas de toda tela de aplicação; V-HV4.1 = a, MANIFESTO §226, tokens v1.29) action-neutral-on-subtle-hover (claro cinza-100 = surface-sunken · escuro surface-raised) action-neutral-on-subtle-active (claro cinza-200 · escuro surface-overlay) — no escuro o par sobe, como action-ghost-*; tintas medidas sobre o pressionado: text-primary 9,24 / 10,90 · text-secondary 5,22 / 7,08 · text-muted (ícone) 3,78 / 5,93 — e text-brand-strong 4,26 no claro, abaixo do piso: controle com tinta de marca sobre a casca troca a tinta para text-primary no :active ou não consome este par (HV-A, Grupo 3, 2026-09-15)
ghost/outline DENTRO de cartão (surface-raised) — regra por superfície D3(a), aplicação da sessão exposta a veto no gate do Grupo 2 (sem veto, MANIFESTO §226) action-neutral-hover (sobre raised escuro: #141D23, visível — action-ghost-hover escuro é surface-raised e sumia) action-neutral-active (#0B1419; monotônico). action-ghost-* fica para o fantasma sobre a página
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

⚠ **A linha "superfície neutra" pressupõe controle sobre surface-raised ou surface-page — medido em 2026-09-15 (MANIFESTO §225, censo de superfície): action-neutral-hover vale #F2F6F9 no claro e #141D23 no escuro, os mesmos hex de surface-subtle. Controle transparente posado sobre a casca (a superfície de toda tela de aplicação, CP1/MA4) recebe no sobrevoo a cor que já tinha atrás: hover invisível por construção nos dois temas. Hoje são 118 controles no claro e 117 no escuro nessa condição (banco-identidade 30, banco-escolha 30, banco-prioridade 12, tela-shell 11, o resto ≤ 6 por arquivo). Não existe segundo degrau neutro na escala de superfície abaixo de surface-sunken (claro) nem abaixo de surface-page (escuro) — é por isso que os seis moldes da MA-P3 usaram véu. A saída foi a gate e ele decidiu (V-HV4.1 = a, verbatim "V-HV4.1 casca: a", 2026-09-15, MANIFESTO §226): par novo por superfície, sem hex novo — action-neutral-on-subtle-hover / -active (claro cinza-100 → cinza-200, escurece; escuro surface-raised → surface-overlay, sobe), gêmeos v1.29; a alternativa (b) surface-sunken + anel interno no pressionado e a (c) véu foram renderizadas (relatório HV-G0, d1-*.png) e descartadas — a (c) porque a HV1-c mede a superfície errada e dá verde, e no escuro o clique é invisível (Δ 3/255). Regra desde então: controle transparente sobre a casca consome o par on-subtle; molde nenhum usa action-neutral-hover sobre a casca nem véu. Primeiros consumidores: o segmentado do tela-painel (item A1 do plano, Grupo 3); os moldes da MA-P3 e do SH-P11 entram no G-B.

Dado registrado, sem conserto (TK-P2, 2026-09-16): no tema escuro, surface-sunken e surface-page são o MESMO valor. Medido em 02-tokens/seed-tokens.json: surface.sunken escuro = #0B1419, exatamente o hex de surface.page escuro. Como action-neutral-active (o pressionado de controle neutro, logo acima) também vale #0B1419 no escuro por desenho (a própria descrição do token diz “é o valor de surface-sunken nos dois temas”), as TRÊS superfícies coincidem no escuro — e é por isso que não existe um quarto degrau abaixo delas para servir de “fronteira” por contraste de fundo. Efeitos MEDIDOS no CSS do canon (não no valor do token, na FORMA como cada componente usa esse fundo): (a) o botão desabilitado (.seed-btn:disabled, §1.9) declara background:var(--seed-surface-sunken);border-color:transparent — sem borda própria, só o fundo distingue o botão da página, e no escuro esse fundo VIRA a própria página: um botão desabilitado sobre a página fica sem nenhuma fronteira visível; (b) o campo em modo LEITURA (.seed-field__control[readonly], §2.5) declara background:var(--seed-field-readonly-bg) (que também resolve para surface-sunken) e border-color:transparent — mesmo efeito, mesma causa. Correção ao enunciado original da pendência (que falava em “campo disabled”): o campo :disabled em si NÃO perde a borda — o CSS dele só troca fundo e cor do texto, mantendo --seed-field-border (herdado da regra base), que é cinza-500 e continua distinto da página nos dois temas (4,50:1 medido no escuro, §2.5). Quem perde a fronteira, pela mesma causa raiz, é o BOTÃO desabilitado e o campo READONLY, não o campo desabilitado. Isto não é conserto: pode ser intencional (a escala escura de superfícies pode não precisar de mais um degrau abaixo de page) ou pode ser lacuna real da paleta — é decisão de paleta, não de implementação, e fica registrada aqui para a próxima sessão não remedir do zero.

Terceira forma aceita de pressionado, registrada: o metal da ação (§93.3) marca o pressionado por transformação (translateY(1px) scale(.985)) sobre o mesmo fundo — o pixel a vê (HV1-b passa) e a régua RC mede o contraste sob o metal; a HV1 declara [n/a] no HV1-c por desenho (fundo com gradiente). Vale só onde o módulo material_bloco.py a emite; véu (box-shadow: inset rgba(0,0,0,·)) continua descartado pelo motivo acima.

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. Errata datada (2026-09-15, tokens v1.29, V-HV4.2 = a): o action-primary-active escuro passou a #CDF3EC (turquesa-100) e o branco sobre ele mede 1,19 — a conclusão não muda, o número envelheceu.

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

(registro movido para o Histórico da seção — REV-P7)

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. Estado em 2026-09-15 (MANIFESTO §225): relida e recenseada — 552 HV1-a e 1.224 HV1-b em 39 arquivos (1.426 medições), HV1-c 0. O trabalho está classificado por molde/gerador (27 gerados, 12 sem gerador) em três grupos: G0 (duas decisões de gate — V-HV4.1 par neutro sobre a casca, V-HV4.2 primário escuro monotônico — com erratas do §71.5/§71.6), G-A (7 itens paralelos que não tocam os moldes do SH-P11: painel, 5 bancadas com gerador, 11 sem gerador, site) e G-B (7 itens depois do SH-P11 e do V-HV4.1: shell e as dez telas de aplicação, e os seis moldes com véu). A guarda HV1 entra no rito no fechamento. Estado em 2026-09-15, onda 1 do Grupo 3 (MANIFESTO §227): os vereditos chegaram — V-HV4.1 = a (par on-subtle, tokens v1.29) e V-HV4.2 = a (primário escuro monotônico); G0 e G-A executados (painel, 5 bancadas com gerador, 11 sem gerador, site; HV1 no painel 26/28 → 0/0 nos dois temas). Recenso com a mesma guarda, TETO=0, 40 arquivos × 2 temas, 1.426 medições: HV1-a 552 → 283 · HV1-b 1.224 → 518 · HV1-c 0 (a rodada achou 4 HV1-c no radio selecionado da banco-escolha, causados pela regra nova da barra de provas, e a regra foi corrigida no mesmo movimento — aria-checked é o mesmo estado que aria-pressed). O G-B entrou na onda 2 do mesmo dia (MANIFESTO §228: shell, lista, referência, tabela, site — GB-1; detalhe, gantt, quadro, chat e os seis moldes com véu — GB-2; o item ativo do rail no módulo). Recenso final da sessão principal, mesma guarda, TETO=0, 40 arquivos × 2 temas, 1.426 medições: HV1-a 552 → 94 · HV1-b 1.224 → 240 · HV1-c 0; 64 PASS · 14 FAIL · 2 n/a por tema. O que resta, por causa: as quatro bancadas geradas que nenhum item cobriu (escolha, identidade, prioridade, texto-rico — o maior bloco), os resíduos nomeados do HV-A (toggle ligado sem token de estado, véu color-mix da paleta de feedback, estados forçados da matriz) e o CTA metálico do site (anel + afundamento, exposto a veto). A guarda HV1 no rito é a PR-P8, com o teto inicial 94 · 240 · 0 e custo medido de ≈ 9 min.
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