Dataviz
4b. Sub-bloco DM — monitoramento
seed-dataviz.md v0.22 · §04bseção 7 de 1204b-sub-bloco-dm-monitoramento-dm1-dm11-estavel-em.md · MD5 bc50e2c2Tí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, ARIAmeterpattern, 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.htmlv0.7 (30.166 B, md5f146c84a3d140a892d1de87ee37d334c) — 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 degetBBox) ·validacao/suite-dataviz-dm.mjsv2 — placar oficial contra o artefato do gate: 85 PASS · 0 FAIL · 6 FAIL DOCUMENTADO (as erratas E1–E6 do §5).Promovido a
estávelem 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:
- 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 (todavar()consumida existe declarada) — permanente na suíte. - 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.
- 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: orender-dm.mjsmedegetBBox()de todo texto contra o viewBox — requisito permanente da camada de render do DM (mesma família do aprendizado puppeteer-vs-CLI do §5). - 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).