Anatomia do Observatório
Como 18 indicadores oficiais de economia, finanças públicas e corrida eleitoral viram um semáforo de três cores — de onde vêm os dados, com que regras eles se atualizam e qual sistema visual sustenta tudo isso.
Visão geral
O Observatório INOVARE é um painel público que traduz indicadores oficiais em linguagem clara. Cada indicador carrega três camadas ao mesmo tempo: o conceito técnico exato (como o BCB ou o IBGE definem a métrica), uma interpretação cidadã — o bloco "Na prática:" — e, quando existe referência normativa, uma faixa ideal calibrada que alimenta o semáforo.
Os 18 indicadores estão distribuídos em 4 blocos temáticos, mais um quarteto de destaque ("O Brasil em 4 números": IPCA, Desemprego, PIB e SELIC) e um módulo especial de acompanhamento das pesquisas presidenciais de 2026.
Atividade e Trabalho
Crescimento econômico e mercado de trabalho
4 indicadoresPreços e Juros
Inflação, taxa de juros, câmbio e cripto
6 indicadoresFamílias e Empresas
Renda, endividamento, inadimplência e falências
4 indicadoresGoverno e Risco
Dívida pública, fiscal e percepção de risco
4 indicadoresComo os indicadores funcionam
Cada indicador é um objeto com campos fixos: conceito, interpretacao, fonte, uma série histórica (serie, anual, desde 2017) e, opcionalmente, uma faixa — o intervalo considerado saudável — e uma polaridade, que diz de que lado da faixa é bom ficar fora dela.
Nem todo indicador tem faixa. Câmbio, Bitcoin, INPC, custo da construção (m²) e falências não têm referência normativa oficial — entram em displayMode: 'contexto' e aparecem em cinza-neutro, com tendência, mas sem veredito de cor.
A lógica do semáforo (calcStatus)
Quatro perguntas em cascata decidem a cor de um indicador com faixa definida:
calcStatus(): a cor depende primeiro da faixa e da polaridade; só entra tendência quando o valor está do lado ruim da faixa, para distinguir "melhorando" (amarelo) de "piorando" (vermelho).calcTrend): compara os dois últimos pontos realizados (não projetados) da série. Variações menores que 0,5% relativo contam como "estável" — evita que ruído de arredondamento vire seta para cima ou para baixo.
Exceção: o quarteto "Brasil em 4 números"
Os quatro cards hero (IPCA, Desemprego, PIB, SELIC) não usam calcStatus — usam calcTrendStatus. A cor não mede distância da faixa, mede se o movimento mais recente foi bom ou ruim para o país. Um PIB subindo é sempre verde no hero, mesmo se o nível ainda estiver fora da faixa ideal; uma queda ≥ 2% relativo é vermelho, abaixo disso é amarelo.
Arquitetura e stack tecnológica
| Camada | Tecnologia |
|---|---|
| Frontend | React 19 + TypeScript + Vite 7 + Tailwind CSS + Radix UI + Lucide Icons |
| Roteamento | React Router 7 |
| Gráficos | Recharts — tooltips translúcidos, gradientes e marcação de mandatos presidenciais (1995–presente) |
| Formulários | React Hook Form + Zod |
| Banco de dados | Supabase (Postgres / PostgREST), schema observatorio, RLS somente-leitura |
| Fallback | Snapshot local estático (src/data/indicators.ts, electionPolls.ts) |
| Coleta | Node.js (scripts/coletar.mjs) |
| Automação | GitHub Actions, execução agendada (cron) |
| Hospedagem | Hostinger / Vercel / Cloudflare Pages — SPA com .htaccess para roteamento limpo |
loader.ts recorre ao snapshot embutido no bundle e marca a origem como 'local'.indicators.ts e prevalecem mesmo quando os dados vêm do Supabase — garante consistência visual independente do que está cadastrado no banco.Regras de atualização
A coleta roda via GitHub Actions com o cron 0 10 * * 1,4 — segundas e quintas às 07h (Brasília) — mais um gatilho manual (workflow_dispatch). O coletor é config-driven: lê da própria tabela observatorio.fonte_series quais indicadores têm fonte de API ativa, em vez de ter os códigos hardcoded no script.
Hoje só BCB/SGS e IBGE/SIDRA estão automatizados de ponta a ponta. O restante (CAGED, Peic, FGV IBRE, CBIC, Boa Vista, IPEAData) é curadoria-manual ou api-inativa, e entra por curadoria manual periódica.
O modelo é append-only
Nunca há UPDATE ou DELETE em observatorio.series. Uma revisão de dado é sempre uma linha nova, com created_at mais recente. A view series_atuais faz DISTINCT ON (indicador_id, ano) ORDER BY created_at DESC — serve sempre o valor mais atual, mas nenhuma revisão histórica se perde.
indicador_id + ano; a view sempre resolve para a última por created_at, sem apagar as anteriores.| Salvaguarda | Como funciona |
|---|---|
| Idempotência | Antes de gravar, o coletor compara com series_atuais; só insere se o ano é novo ou o valor mudou mais que 0,005. |
| Modo teste | node scripts/coletar.mjs --dry-run mostra o que seria gravado, sem tocar no banco. |
| Falha isolada | Se uma fonte quebrar, o erro é logado e o script segue para as demais — nada é alterado para o indicador que falhou. |
| Chave de escrita | SUPABASE_SECRET_KEY (service role) só existe como secret do GitHub Actions ou em .env.local — nunca no bundle do frontend. |
| RLS | O papel anon só tem SELECT; escrita é bloqueada no Postgres independentemente do que o frontend tente fazer. |
Modelo de dados
Seis tabelas/views no schema observatorio, todas com leitura pública via RLS (anon + authenticated) e escrita restrita ao service_role.
indicadores é o catálogo; series guarda o histórico completo, revisável; series_atuais é a view que qualquer leitor de fora usa.Sistema de design — Antigravity Spatial
O nome do sistema visual é Antigravity Spatial & Motion Design: cards com sensação de leveza (elevação sutil no hover, translateY(-3px) scale(1.004)), sombras difusas em múltiplas camadas e glassmorphism com uma borda especular de 1px no topo — um reflexo de luz simulando vidro.
Tipografia: Inter (300–800), com font-variant-numeric: tabular-nums aplicado globalmente para que números alinhem em coluna nos cards e tabelas.
| Token | Escuro (padrão) | Claro (.light) |
|---|---|---|
| Fundo | #07070d | #f6f8fb |
| Cartão | #12121a / #16162a | branco translúcido |
| Acento | hsl(239 84% 67%) — índigo | índigo mais escuro p/ contraste |
| Raio de borda | 0.75rem (constante nos dois temas) | |
| Semáforo | 4 estados × 4 variantes cada: bg, text, dot, glow | |
.light na raiz remapeia individualmente cada utilitário Tailwind em uso (fundos, textos, bordas, gradientes, sombras) em index.css — uma segunda paleta com contraste dedicado, não uma inversão automática.Componentes-chave
| Componente | Papel |
|---|---|
| StatusDot | Ponto pulsante com glow na cor do status + seta de tendência (up/down/stable) |
| IndicatorCard | Card recolhível: cabeçalho sempre visível (valor, faixa, status); painel expandido com "Na prática", gráfico e nota metodológica |
| IndicatorChart | Linha (Recharts) com marcação dos mandatos presidenciais desde 1995 e série secundária opcional (ex.: SELIC Real ao lado da Meta) |
| HeroSummary | Quarteto "Brasil em 4 números" — usa calcTrendStatus, não calcStatus |
Toda animação (float-slow, pulse-subtle, fade-in) é desativada sob prefers-reduced-motion: reduce.