Ir ao conteúdo
SEED engenhariaDesign System

Dataviz

4b. Sub-bloco DM — monitoramento

estávelseed-dataviz.md v0.22 · §04bseção 7 de 1204b-sub-bloco-dm-monitoramento-dm1-dm11-estavel-em.md · MD5 bc50e2c2

Título completo no canon: Sub-bloco DM — monitoramento (DM1–DM11, estável em 2026-08-11)

O que é o DM. O sub-bloco dos objetos de MONITORAMENTO — os que respondem "como está AGORA?" em vez de "como se comparam?": o medidor (KPI contra meta e faixas), a sparkline (tendência em espaço mínimo, célula de tabela §40 do ERP) e o gráfico do alerta de geração (fecha o slot ET5 do seed-email.md). Consolidado aprovado pelo Rafael em 2026-08-10 após as 3 rodadas de pesquisa do método (R1 canon · R2 mercado/stack · R3 normas — Stephen Few Bullet Graph Design Specification, Tufte Beautiful Evidence sobre sparklines, ARIA meter pattern, WAI-ARIA 1.2); preview iterado v0.1 → v0.7 com gate visual completo (matriz cruzada) e fechamento "acredito que agora esteja tudo ok".

Artefatos: seed-dataviz-monitoramento-preview.html v0.7 (30.166 B, md5 f146c84a3d140a892d1de87ee37d334c) — reanexado e conferido por MD5 em 2026-08-11, fechando a pendência P6; a v0.8 corrige as erratas E1–E4 e E6 e aguarda gate · validacao/gen-dm.py (gerador dos fragmentos SVG do preview) · validacao/render-dm.mjs (puppeteer, com a guarda de getBBox) · validacao/suite-dataviz-dm.mjs v2 — placar oficial contra o artefato do gate: 85 PASS · 0 FAIL · 6 FAIL DOCUMENTADO (as erratas E1–E6 do §5).

Promovido a estável em 2026-08-11, por "aprovo" explícito do Rafael sobre o lote de formalização. O que o "aprovo" cobre: a spec (DM1–DM11), os tokens v1.5 que ela consome e a suite. O que ele não cobre, e por isso permanece aberto no §5: as seis erratas medidas no artefato depois do gate. Nenhuma delas altera decisão — a spec é a fonte, o preview é a evidência dela, e onde os dois divergem quem manda é a spec.

DM1 — o medidor canônico é o BULLET GRAPH; gauge circular é exceção declarada. O medidor SEED é horizontal, linear, compacto — o bullet graph de Stephen Few. O gauge circular (velocímetro) só entra como exceção declarada: peça ÚNICA em tela de exibição (TV de sala de monitoramento), nunca em lista ou comparação. Racional: comprimento sobre linha de base é a codificação perceptual mais precisa (Cleveland–McGill); ângulo é das piores. E o caso real da SEED é COMPARAR muitas usinas — bullets empilham em lista com rótulos alinhados; velocímetros não. Um bullet ocupa ~1/8 da área do gauge circular equivalente. Alternativa descartada: gauge circular como padrão (é o clichê de dashboard que o mercado abandonou; Few o desenhou o bullet exatamente para substituí-lo).

DM2 — anatomia do medidor: seis partes. (a) rótulo da métrica; (b) unidade; (c) barra de valor (a medida); (d) marca de meta — traço perpendicular de 1,5px (exceção registrada da hierarquia de traço: precisa vencer visualmente a banda atrás); (e) bandas de faixa ao fundo — máximo 3 (Few: mais de 3 faixas vira arco-íris); (f) eixo numérico com os limiares rotulados. Alternativa descartada: bandas sem eixo numérico — a faixa sem número obriga o leitor a adivinhar os limiares.

DM3 — papéis invertidos: banda é intensidade de cinza; a BARRA carrega a severidade. Formalizado como supersede na §3c do seed-tokens.md v1.5 (os hex não mudam; muda quem os consome). Bandas: cinza-100/200/300 no light, invertendo para cinza-800/700/600 no dark (distâncias de claridade 9/10/19 e 9/8/17 pontos, medidas). Barra de valor: assume gauge-range-ok/warning/critical conforme a faixa em que o valor cai. Racional medido: as três faixas coloridas originais distavam 1 ponto de claridade no light e 0 no dark — diferiam quase só por matiz, ilegível para daltônico e em grayscale; Few manda codificar faixa em INTENSIDADE. Alternativas descartadas: texturizar as 3 bandas coloridas (ruído em objeto pequeno) · mudar os hex (supersede caro sem necessidade). Geometria: barra de valor com 12px de altura sobre banda de 18px — engrossada no gate (era 8px) para dominar sobre a banda mesmo com textura DM11 ativa.

DM4 — acesso do medidor: role="meter" com nome e valor em português. O elemento do medidor leva role="meter" + aria-valuenow/min/max + aria-valuetext com a leitura humana ("PR 83,1% — dentro do esperado"). Rótulo e valor visíveis ficam FORA do elemento com o role — descendentes de meter são apresentacionais para leitores de tela, texto dentro dele desaparece. No produto React, via useMeter do React Aria. Alternativas descartadas: <meter> nativo (VoiceOver o anuncia como barra de progresso — semântica errada) · role="progressbar" (progresso implica conclusão; medidor mede estado).

DM5 — sparkline: tendência, nunca valor. Sem eixo, sem grade, sem legenda, sem rótulo interno; série única; traço 2px (cat-N-stroke, hierarquia de traço); marcador só no ponto final (o valor "agora"). A sparkline responde "subiu ou desceu?" — quem responde "quanto?" é o número na célula vizinha. Racional: é a definição de Tufte (word-like graphic); qualquer adorno a transforma num gráfico de linha ruim.

DM6 — sparkline em tabela é decorativa POR CONTRATO. Na célula de tendência da tabela §40 do ERP: aria-hidden="true" na sparkline, e a célula vizinha obrigatoriamente carrega valor atual + variação em texto. Sem par numérico, a sparkline não entra. Racional: uma micro-linha por linha de tabela é inaudível por construção; fingir que ela é acessível (title, aria-label por ponto) é pior que declará-la decorativa e garantir o dado em texto — o mesmo raciocínio do fallback do DG1, aplicado ao micro-formato.

DM7 — rótulo textual de estado SEMPRE visível. Todo medidor exibe a palavra do estado ("dentro do esperado" · "atenção" · "crítico") na coluna à direita, na cor gauge-range-* correspondente + texto. Cor nunca é o único portador (1.4.1); em grayscale e no impresso, a palavra é o que sobrevive. Nasceu como defeito no gate (v0.4 do preview o omitia) e virou regra com teste.

DM8 — gráfico do alerta de geração (fecha o slot ET5 da F4). Barra agrupada esperado × medido (herda DG10 inteiro), renderizada a PNG @2x, largura fixa 560px, hospedada no R2 (/email/v1/), com alt que declara o desvio em texto ("geração 18% abaixo do esperado nos últimos 7 dias"). E-mail não roda SVG nem JS (leis do seed-email.md); o PNG estático com alt textual é o único formato que sobrevive aos clientes. A emenda de referência no seed-email.md dispara quando o PNG existir no R2.

SUPERSEDE do DM8 na v0.13 (2026-08-11) — o e-mail de alerta NÃO leva gráfico. Decisão do Rafael no gate de produção do asset, e o racional dele derruba o desenho original: um alerta não pode carregar dado que não é do destinatário. O PNG hospedado no R2 é um arquivo único, servido a todos os disparos — logo, os números nele são ilustrativos. O cliente cujo desvio é de 6% receberia um e-mail com uma imagem mostrando 18%, contradizendo o texto ao lado. Quem vê isso não conclui "a imagem é genérica"; conclui que o sistema erra — e um alerta existe justamente para ser confiável.

Regra vigente: o e-mail de alerta leva símbolo de alerta, não gráfico. O dado real vive no sistema, onde o cliente entra para ver o que está acontecendo. O e-mail avisa; o sistema mostra.

O slot ET5 sai do escopo da dataviz e volta para o seed-email.md, sem regra nova: o EC5 já determina que ícones raster são produzidos sob demanda, como par glifo×cor rasterizado do SVG normalizado quando um template o pede, em /email/v1/icones/. Um template de alerta pedindo glifo de alerta é exatamente o caso previsto. O DM8 tinha contornado essa lei ao especificar um PNG de conteúdo; a correção reconduz o slot à regra que já existia.

Alternativa descartada: gráfico gerado por usina no envio, com renderização server-side. Não é rejeitada por mérito — é fora do Design System. O DM10 já pôs comportamento dependente de dado vivo no domínio do produto (Lovable/Supabase). Se um dia se decidir mandar gráfico real ou outra informação no e-mail, isso é programação de sistema, não spec de DS.

Consequência material: o PNG seed-email-et5-grafico@2x.png (1120×520, 28.441 B) chegou a ser produzido e foi descartado sem subir ao R2. Fica registrado que existiu, para que ninguém o encontre num diretório de trabalho e o suba supondo que faltava propagar. O DI19 — que decidiu o caminho /email/v1/ para o asset do ET5 — permanece válido quanto ao caminho e muda de objeto: quando o glifo de alerta for rasterizado pelo EC5, vai para /email/v1/icones/.

DM9 — estados herdam o DG6 com uma adição. Skeleton/vazio/erro do medidor e da sparkline seguem o DG6 (moldura real, Z1/Z2, alerta §15). Adição própria: medidor sem leitura usa chart-no-data hachurado na barra de valor — "sem medição" NÃO desenha barra em zero, que mentiria (zero é um valor legítimo de geração).

DM10 — fronteiras do DM. Atualização em tempo real/websocket = comportamento de produto (Lovable/Supabase), não do DS · composição de painel/grid de medidores = F6 · comportamento no impresso = DI. O DM entrega o objeto; quem orquestra é outro bloco.

DM11 — textura nas bandas do medidor, no MESMO gatilho do DG13 (escuro + grayscale). Vocabulário próprio, por banda: esperado = sólido · atenção = pontilhado · crítico = quadriculado; a diagonal 45° permanece exclusiva do no-data (lei da §3c). A legenda do medidor repete a textura sob o mesmo gatilho. Origem — gate visual de 2026-08-10: com o DM3 aplicado, as bandas cinza mediam 9/8/17 pontos de espalhamento — ACIMA do piso de 6 do contraste-dg.py — e o Rafael ainda assim não as distinguia no escuro+grayscale. É o caso que provou que o piso não transfere de contexto (emenda registrada no DG13): faixa fina empilhada exige mais que barra alta adjacente. A textura entra como remédio no mesmo gatilho conservador do DG13; no claro, o espalhamento medido basta.

Registro de produção do DM (defeitos que viraram guarda)#

Quatro defeitos de produção foram encontrados e corrigidos ANTES do gate final — três pela suíte, um só pelo render real:

  1. Variáveis CSS órfãs — o preview consumia var(--seed-gauge-range-*) sem declará-las no bloco de tokens embutido (herança silenciosa de um preview anterior). Guarda: teste de variáveis órfãs (toda var() consumida existe declarada) — permanente na suíte.
  2. DM7 não implementado — a spec exigia o rótulo de estado e o preview v0.4 não o desenhava. Guarda: teste de presença do rótulo por medidor.
  3. Overflow silencioso de viewBox — o rótulo "83,1%" estourava o viewBox em 9px e era CORTADO no render; jsdom não detecta (não faz layout), só getBBox() em navegador real vê. Guarda: o render-dm.mjs mede getBBox() de todo texto contra o viewBox — requisito permanente da camada de render do DM (mesma família do aprendizado puppeteer-vs-CLI do §5).
  4. Acentos perdidos no HTML gerado pelo gen-dm.py (encoding). Guarda: verificação de round-trip UTF-8 no gerador.

Exceção ao DG12 usada pelo DM: registrada na emenda do próprio DG12 (§4.4) — a tinta da marca de meta acompanha a superfície porque as bandas invertem com o tema (7,07 / 3,93 medidos).

Esc