Dataviz DI — impresso e integração da stack

Preview v0.3c. Sub-bloco DI da Fase 5 do SEED Design System v2. Este preview não traz gráfico novo nem tipo novo: o DI não cria gráfico, cria comportamento — como a dataviz que já existe se comporta no papel, e como ela se liga à stack do produto. Spec completa no §4c do seed-dataviz.md v0.10.

Mudou na v0.3: o teste do DI4 fechou — o resultado medido está na seção 2, e a regra saiu de inferência para fato. Mudou na v0.2, tudo por achado do gate no papel: a demonstração do DI4 passou de 2 para 4 células, porque a v0.1 declarava print-color-adjust: exact num seletor universal e protegia os dois braços do próprio teste · as quatro células agora têm área idêntica (o SVG preservava a proporção do viewBox e deixava faixas brancas laterais) · break-inside: avoid passou a cobrir .nota e .card, porque uma nota partiu entre as páginas 1 e 2. Nenhum dos três o jsdom pegaria: ele confirma que a regra existe, não que ela cobre os elementos certos.
Como avaliar este preview: boa parte do que ele especifica só aparece na pré-visualização de impressão (Ctrl/Cmd+P). Em tela, o que se vê é a spec explicada; no papel, ela executada. O botão acima abre o diálogo.

1 · DI1 — dois destinos, e só um é escopo deste sub-bloco

DestinoO que sai por eleEscopo
Laser colorida de escritório Laudo, relatório de O&M, proposta impressa, qualquer PDF que o ERP mande para a impressora DI
Offset da gráfica Colateral físico da marca Fase 7

O fornecedor real da SEED trabalha em CTP (computer-to-plate) e exige PDF/X-1a com tudo convertido em CMYK; arquivos em RGB podem ter a cor alterada ou não impressa. Ele também declara variação de ±10% na cor dentro do mesmo lote — tolerância incompatível com informação codificada por diferença fina de cor, e irrelevante para o laudo, que nunca passa pelo CTP dele. Daí os dois destinos serem tratados como problemas diferentes, em vez de um pipeline único. DI7: o Design System não converte para CMYK — os hex canônicos permanecem RGB e a conversão é do fluxo da gráfica, na F7.

2 · DI4 — nenhuma informação de gráfico pode viver em background-color

Fato, não inferência: print-color-adjust: exact é uma dica ao navegador. A preferência do usuário no diálogo de impressão ("Gráficos de plano de fundo") tem precedência, e nenhuma folha de estilo a sobrepõe. Um laudo impresso por quem nunca abriu "Mais configurações" sai com todo background ausente.

A CSS background · exact
CSS
B SVG fill · exact
C CSS background · economy
CSS
D SVG fill · economy
RESULTADO MEDIDO — gate de impressão do Rafael, 2026-08-11, Chrome, "Salvar como PDF", A4. A matriz foi impressa duas vezes, com a opção "Gráficos de segundo plano" marcada e desmarcada.

Matriz do gate de impressão — o que sobreviveu em cada célula

CélulaO que éOpção MARCADAOpção DESMARCADA
ACSS background · exactsobreviveusobreviveu
BSVG fill · exactsobreviveusobreviveu
CCSS background · economysobreviveuSUMIU
DSVG fill · economysobreviveusobreviveu
O DI4 está provado como escrito. C sumiu e D sobreviveu sob condição idêntica — mesma cor, mesma área, mesma célula da matriz, única diferença sendo como a superfície é pintada. Isso confirma o que a regra afirma: fill de SVG é conteúdo desenhado e sobrevive por natureza; background-color é plano de fundo e depende de proteção. A inferência declarada na v0.1 deixa de ser inferência.

Segundo achado, não previsto: a célula A sobreviveu, ou seja, o Chrome honrou print-color-adjust: exact mesmo com a preferência do usuário desmarcada. Existem portanto duas camadas de proteção, e elas não têm o mesmo valor: a dica de CSS (que funcionou aqui) e a natureza estrutural do SVG (que não depende de navegador). A regra dura do DS continua sendo a segunda — a primeira é documentadamente uma dica, e a documentação é explícita em que a preferência do usuário pode vencê-la.

Fronteira da medição, declarada: um navegador (Chrome), um destino ("Salvar como PDF"). Não foi medido em Firefox, nem em driver de impressora física, e o Firefox tem histórico de divergir neste ponto. Vale como fato para o Chrome e como forte indício para o resto. Se algum dia um laudo sair sem as superfícies, o primeiro lugar a olhar é este parágrafo.

3 · DI5 — o <figure> nunca se parte entre páginas

O gráfico abaixo é o mesmo do preview do DG, aqui só para exercer as regras de impressão. Na pré-visualização, ele e suas cinco partes viajam juntos para a página seguinte em vez de se dividirem.

Consumo por posto tarifário — UFV Valadares, jun/2026

Três postos, em MWh. Barra agrupada, eixo a partir do zero (DG4).

Maio Junho 0 40 80 120
  • Fora de ponta
  • Ponta
  • Reservado
Ver os dados em tabela
Consumo por posto tarifário, em MWh
MêsFora de pontaPontaReservado
Maio866132
Junho1026738

No papel o <details> do fallback é forçado a aberto e o "Ver os dados em tabela" some: um acordeão fechado imprime como uma linha inútil, e o fallback textual é a alternativa acessível exigida pelo DG1, não um detalhe opcional.

4 · DI6 — régua de traço: o piso é do papel, não da tela

A escala já está resolvida por vector-effect: non-scaling-stroke, obrigatório em todo SVG de gráfico — sem ele o mesmo stroke-width=1 renderizaria entre meio pixel e doze, porque os viewBox do sistema escalam de 0,66× a 12,4×. O que não está resolvido é o piso físico. Imprima e diga qual das linhas some ou engorda.

2px — dado 1,5px — meta 1px — estrutura 0,5px — apoio

O candidato a falhar é o apoio de 0,5px. Referência de ordem de grandeza levantada na pesquisa: uma impressora laser não deposita linha abaixo de cerca de três a quatro vezes o seu ponto nominal. Isso é referência de literatura, não medição da SEED — o número nosso sai desta régua impressa.

5 · DI3 — o que este preview NÃO decide

O veredito do gatilho da textura no papel está ABERTO, e a ausência é deliberada. A pergunta "o modo de impressão entra no gatilho do DI2?" não se responde por análise: um piso decide apenas no contexto em que foi calibrado. As bandas do medidor no sub-bloco DM mediam 9, 8 e 17 pontos de espalhamento de claridade — todas acima do piso de 6 — e ainda assim eram indistinguíveis no gate. Papel é contexto novo e vai exigir piso próprio. O protocolo de sete itens está fechado no §4c.3 e será entregue como folha de prova em arquivo separado (lote DI-b). Enquanto ele não fecha, vale o gatilho do DI2 e nada mais: o impresso não liga textura por ser impresso.

6 · Guia de integração da stack — DI11 a DI17

Consumidor real e imediato: o ERP em Lovable (Vite + React + TypeScript + Tailwind + shadcn/ui + Supabase). O seed-dataviz.md permanece agnóstico de biblioteca (DF2); este guia é a ponte, nunca a lei.

DI11 — a cadeia tem quatro saltos, e o quarto é o que se erra

--seed-chart-cat-N     token SEED, dono do valor
      ↓
--chart-N              alias que o shadcn Chart espera encontrar no CSS
      ↓
chartConfig[chave].color = "var(--chart-N)"
      ↓
fill="var(--color-chave)"   ← o que o componente Recharts realmente consome

O fill não referencia --chart-N. Ele referencia var(--color-CHAVE), uma variável que o ChartContainer gera em tempo de execução a partir do chartConfig. Pular o terceiro salto produz gráfico sem cor e sem erro no console — que é o modo de falha mais caro de todos.

Bloco 1 — globals.css (Tailwind v4)

/* SEED DS v2 · aliases de dataviz para o shadcn Chart.
   DI12: Recharts v3 usa var(--chart-1) DIRETO. NUNCA hsl(var(--chart-1)),
   que era a forma do v2 e pressupunha tokens gravados como triplas de canal —
   os tokens SEED são hex, e o embrulho produziria valor inválido, o que em CSS
   significa cor ausente e não erro.
   DI16: seis aliases. O shadcn distribui cinco por padrão; o teto SEED é seis.
   Acima disso o gráfico se reestrutura (agregação em "Outros", facetas, ou o
   par destaque × contexto do DG17) e nunca ganha uma sétima cor. */
:root {
  --chart-1: var(--seed-chart-cat-1);
  --chart-2: var(--seed-chart-cat-2);
  --chart-3: var(--seed-chart-cat-3);
  --chart-4: var(--seed-chart-cat-4);
  --chart-5: var(--seed-chart-cat-5);
  --chart-6: var(--seed-chart-cat-6);
}
/* DI13: o tema vem do [data-theme], não do objeto theme do chartConfig.
   Descartado usar theme:{light,dark} no TS — duplicaria a cor dentro do
   produto, criando uma segunda fonte de verdade e furando o DF7 e o GI1.
   Como os tokens SEED já trocam de valor no [data-theme="dark"], os aliases
   acompanham sozinhos e não precisam ser redeclarados. */

Bloco 2 — chartConfig e o componente

const chartConfig = {
  foraPonta: { label: "Fora de ponta", color: "var(--chart-1)" },
  ponta:     { label: "Ponta",         color: "var(--chart-2)" },
  reservado: { label: "Reservado",     color: "var(--chart-3)" },
} satisfies ChartConfig

// DI15: ChartContainer SEMPRE com altura, min-h-* ou aspect-*. Sem uma delas o
// ResponsiveContainer não mede no primeiro render e o gráfico não aparece.
<ChartContainer config={chartConfig} className="min-h-[240px] w-full">
  <BarChart accessibilityLayer data={dados}>
    {/* DI14: accessibilityLayer é OBRIGATÓRIO e INSUFICIENTE. Ele entrega
        acesso por teclado e suporte a leitor de tela. NÃO entrega alternativa
        textual (WCAG 1.1.1) nem relação codificada entre legenda e dado
        (1.3.1) — as duas reprovações da auditoria pública do shadcn Charts.
        Por isso o invólucro <figure> do DG1 permanece por cima. */}
    <Bar dataKey="foraPonta" fill="var(--color-foraPonta)" radius={3} />
    <Bar dataKey="ponta"     fill="var(--color-ponta)"     radius={3} />
    <Bar dataKey="reservado" fill="var(--color-reservado)" radius={3} />
  </BarChart>
</ChartContainer>

Bloco 3 — o invólucro do DG1, sem o qual nada acima é um gráfico SEED

// src/components/seed/SeedChart.tsx
// As CINCO partes do DG1: título em figcaption · descrição curta ·
// fallback textual SEMPRE presente · legenda · área com role="img"
// amarrada por aria-labelledby. Sem as cinco, não é gráfico SEED.
export function SeedChart({ titulo, descricao, children, dados, colunas }) {
  const idT = useId(), idD = useId()
  return (
    <figure className="seed-chart" aria-labelledby={`${idT} ${idD}`}>
      <figcaption id={idT}>{titulo}</figcaption>
      <p id={idD} className="seed-chart__desc">{descricao}</p>
      <div className="seed-chart__plot" role="img" aria-labelledby={`${idT} ${idD}`}>
        {children}
      </div>
      <details className="seed-chart__fallback">
        <summary>Ver os dados em tabela</summary>
        <TabelaDados dados={dados} colunas={colunas} />
      </details>
    </figure>
  )
}

7 · Fronteiras declaradas

AssuntoDe quem é
Painel e composição de dashboardFase 6
Tempo real, websocket, consulta de dadosProduto (Lovable/Supabase)
Mapas MG-ES-BASub-bloco DP
Formato, gramatura, acabamento, capa, papelaria, conversão CMYK da marcaFase 7
Veredito do gatilho da textura no papelDI-b, aberto

DI8 — PDF/A não é adotado. Nada no arcabouço CONFEA/CREA exige formato de arquivo para laudo; o PDF/A é recomendação do CONARQ dirigida aos órgãos do Sistema Nacional de Arquivos, e a SEED não integra o SINAR. E há conflito técnico: o PDF/A-1 admite apenas assinatura digital simples, o que colidiria com o PAdES da ICP-Brasil — que é justamente o que dá validade jurídica ao laudo assinado. O laudo sai em PDF comum, assinável em PAdES. Gatilho de revisão: se um contrato ou órgão exigir PDF/A por escrito, avaliar PDF/A-2 ou A-3, nunca A-1.