Componentes
71. HV1 — a RESPOSTA AO PONTEIRO e o PRESSIONADO DISTINTO
seed-componentes.md v2.00 · §71seção 146 de 18471-hv1-a-resposta-ao-ponteiro-e-o-pressionado.md · MD5 dfdeac9cTí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 é exatamentesurface-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 deaction-ghost-hoverem 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 — abanco-componentespassou 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-tokensfoi removida (D-3, §210.4). A regra por superfície: ghost/outline dentro de cartão consomeaction-neutral-hover/action-neutral-active(sobreraisedescuro:#141D23→#0B1419, visível e monotônico), eaction-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-raisedousurface-page— medido em 2026-09-15 (MANIFESTO §225, censo de superfície):action-neutral-hovervale#F2F6F9no claro e#141D23no escuro, os mesmos hex desurface-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-identidade30,banco-escolha30,banco-prioridade12,tela-shell11, o resto ≤ 6 por arquivo). Não existe segundo degrau neutro na escala de superfície abaixo desurface-sunken(claro) nem abaixo desurface-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; escurosurface-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 paron-subtle; molde nenhum usaaction-neutral-hoversobre a casca nem véu. Primeiros consumidores: o segmentado dotela-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-sunkenesurface-pagesão o MESMO valor. Medido em02-tokens/seed-tokens.json:surface.sunkenescuro =#0B1419, exatamente o hex desurface.pageescuro. Comoaction-neutral-active(o pressionado de controle neutro, logo acima) também vale#0B1419no 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) declarabackground: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) declarabackground:var(--seed-field-readonly-bg)(que também resolve para surface-sunken) eborder-color:transparent— mesmo efeito, mesma causa. Correção ao enunciado original da pendência (que falava em “campo disabled”): o campo:disabledem 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 depage) 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ódulomaterial_bloco.pya 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 leembackground-colorpara compor contraste (ofundoResolvidoda guarda HV1 aborta em imagem/gradiente, e ocontraste-composicaotrata 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-activeno pressionado do avatar. Medido: no tema escuro ele vale#11B0A0e 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): oaction-primary-activeescuro 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 |