antigravity-design-system

star 1

Use esta skill sempre que uma tarefa envolver qualquer aspecto visual do projeto Gravity — cores, tipografia, espaçamentos, sombras, bordas, componentes visuais ou tokens de design. Define o tema Solid Slate (Material 3 Adaptation), todas as variáveis CSS obrigatórias, escala tipográfica com Plus Jakarta Sans, sistema de botões pill, campos de formulário, badges, toasts, KPI cards, kanban, tabs, popover de filtro, select customizado, wizard timeline, modais complexos e modal de permissões. Todo agente consulta esta skill antes de escrever qualquer CSS ou componente visual.

dmmltda By dmmltda schedule Updated 5/16/2026

name: antigravity-design-system description: "Use esta skill sempre que uma tarefa envolver qualquer aspecto visual do projeto Gravity — cores, tipografia, espaçamentos, sombras, bordas, componentes visuais ou tokens de design. Define o tema Solid Slate (Material 3 Adaptation), todas as variáveis CSS obrigatórias, escala tipográfica com Plus Jakarta Sans, sistema de botões pill, campos de formulário, badges, toasts, KPI cards, kanban, tabs, popover de filtro, select customizado, wizard timeline, modais complexos e modal de permissões. Todo agente consulta esta skill antes de escrever qualquer CSS ou componente visual."

Gravity — Design System

Tema: Solid Slate (Material 3 Adaptation)

Dark mode é o padrão. Light theme é ativado via body.light-theme.


1 — Variáveis Globais (Root CSS)

:root {
  /* === Backgrounds === */
  --bg-body-dark:    #0f172a;
  --bg-base:         #1e293b;
  --bg-surface:      #334155;
  --bg-elevated:     #475569;

  /* === Acento principal (Indigo 500) === */
  --accent:          #6366f1;
  --accent-hover:    #4f46e5;

  /* === Texto === */
  --text-primary:    #f1f5f9;
  --text-secondary:  #94a3b8;
  --text-muted:      #64748b;

  /* === Status === */
  --success:         #22c55e;
  --warning:         #f59e0b;
  --danger:          #ef4444;

  /* === Geometria === */
  --radius-sm:       4px;
  --radius-md:       8px;
  --radius-lg:       12px;
  --radius-pill:     9999px;

  /* === Focus ring === */
  --focus-ring:      0 0 0 2px rgba(99, 102, 241, 0.4);

  /* === Sombras === */
  --shadow-sm:       0 1px 3px rgba(0,0,0,0.4);
  --shadow-md:       0 4px 12px rgba(0,0,0,0.5);
}

body.light-theme {
  --bg-body-dark:    #f8fafc;
  --bg-base:         #ffffff;
  --bg-surface:      #f1f5f9;
  --bg-elevated:     #e2e8f0;
  --text-primary:    #0f172a;
  --text-secondary:  #475569;
  --text-muted:      #94a3b8;
}

2 — Escala Tipográfica

Fontes: Plus Jakarta Sans (UI) e DM Mono (código). Carregar via Google Fonts.

/* Tipografia — Plus Jakarta Sans obrigatório */
body { font-family: 'Plus Jakarta Sans', sans-serif; }
code, pre { font-family: 'DM Mono', monospace; }

.text-display  { font-size: 2.25rem;  font-weight: 700; line-height: 1.2; }
.text-h1       { font-size: 1.875rem; font-weight: 700; line-height: 1.3; }
.text-h2       { font-size: 1.5rem;   font-weight: 600; line-height: 1.3; }
.text-h3       { font-size: 1.25rem;  font-weight: 600; line-height: 1.4; }
.text-body-lg  { font-size: 1rem;     font-weight: 400; line-height: 1.6; }
.text-body     { font-size: 0.875rem; font-weight: 400; line-height: 1.6; }
.text-sm       { font-size: 0.8125rem;font-weight: 400; line-height: 1.5; }
.text-micro    { font-size: 0.75rem;  font-weight: 600; line-height: 1.4; letter-spacing: 0.06em; text-transform: uppercase; }

Regra: .text-micro é sempre UPPERCASE. Nunca use para corpo de texto corrido.


3 — Sistema de Botões (Pill System)

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1.25rem;
  border-radius: var(--radius-pill);  /* OBRIGATÓRIO — sempre pill */
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s ease;
  border: none;
}

.btn-primary {
  background: var(--accent);
  color: #0f172a;
}
.btn-primary:hover { background: var(--accent-hover); }

.btn-secondary {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--bg-elevated);
}
.btn-secondary:hover { background: var(--bg-elevated); }

.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
}
.btn-ghost:hover {
  background: var(--bg-surface);
  color: var(--text-primary);
}

.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

4 — Campos de Formulário

.input-group {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.input-group label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.input-group input,
.input-group textarea,
.input-group select {
  background: var(--bg-base);
  border: 1px solid var(--bg-elevated);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.75rem;
  color: var(--text-primary);
  font-size: 0.875rem;
}

.input-group input:focus,
.input-group textarea:focus {
  outline: none;
  box-shadow: var(--focus-ring);
  border-color: var(--accent);
}

Placeholder — Spec Obrigatória

Todo campo vazio exibe um placeholder (texto de referência). A spec abaixo é inviolável — vale para <input>, <textarea>, <select>, SelectGlobal (.sg-placeholder), CampoDecimalGlobal e qualquer componente de formulário.

/* Placeholder canônico — Gravity Design System */
input::placeholder,
textarea::placeholder,
.sg-placeholder {
  font-family: 'Plus Jakarta Sans', sans-serif;  /* fonte UI — NUNCA monospace */
  font-size:   0.875rem;                          /* 14px — .text-body */
  font-weight: 400;                               /* normal */
  font-style:  normal;                            /* NUNCA italic */
  color:       var(--text-muted, #64748b);        /* dark: #64748b · light: #94a3b8 */
  opacity:     1;                                 /* browsers aplicam 0.54 por default — forçar 1 */
  text-align:  left;                              /* mesmo em campos numéricos right-aligned */
}
Propriedade Valor Justificativa
font-family 'Plus Jakarta Sans', sans-serif Fonte UI padrão — DM Mono é só para código/valores numéricos preenchidos
font-size 0.875rem (14px) Classe .text-body — alinha com valor selecionado (.sg-valor-selecionado)
font-weight 400 Normal — valores preenchidos usam 500, placeholder fica mais leve
color var(--text-muted) Dark #64748b / Light #94a3b8 — token de texto terciário
opacity 1 Explícito — Firefox/Chrome reduzem para 0.54 se não forçado
text-align left Placeholder é texto descritivo, sempre à esquerda — valor numérico muda para right ao digitar

Regras de conteúdo do placeholder

  1. Nunca usar valor válido do domínio como placeholder — ex: proibido placeholder="USD" em campo de moeda (confunde com seleção real)
  2. Texto descritivo curto — ex: "Selecionar moeda", "Quantidade", "Valor unitário"
  3. Campos computados/read-only — usar "Calculado" (indica que o sistema preenche)
  4. Formato de exemplo — permitido quando inequívoco: "0000.00.00" para NCM, "SKU" para part number
  5. i18n obrigatório — todo placeholder deve usar t() (ver skill traducao)

Override em CampoDecimalGlobal

O CampoDecimalGlobal aplica fontFamily: monospace e textAlign: right via inline style. Como ::placeholder herda do input, é obrigatório fazer override via CSS scoped:

.meu-form input::placeholder {
  font-family: 'Plus Jakarta Sans', sans-serif !important;
  text-align: left !important;
  color: var(--text-muted, #64748b) !important;
  opacity: 1 !important;
}

O !important é justificado: inline styles têm especificidade 1000, e ::placeholder herda deles. Sem !important, a fonte e alinhamento do placeholder ficam errados.


Hints e Dicas Abaixo de Campos (3 tiers oficiais)

Texto auxiliar abaixo de campos segue 3 tiers com propósitos distintos. Cada tier tem spec visual própria — nunca misturar.

Tier Nome Propósito Ícone? Tamanho Cor
1 — Hint padrão .cg-hint Orientação estática (ex: "Formato: 0000.00.00") ❌ Sem ícone 0.8rem var(--text-muted, #94a3b8)
2 — Dica contextual .cg-hint-contextual Sugestão/informação dinâmica (ex: "Sugestão automática — você pode editar") Info 14px fill 0.8rem var(--text-muted, #94a3b8)
3 — Status badge Custom Estado de validação (ex: "NCM válido", "Verificando...") ✅ Semântico 12px 0.75rem Semântica (verde/amarelo/vermelho)

Tier 1 — Hint Padrão (via CampoGeralGlobal)

Texto puro, sem ícone. Usa a prop hint do CampoGeralGlobal:

<CampoGeralGlobal label="NCM" hint="Formato: 0000.00.00">
  <input ... />
</CampoGeralGlobal>

CSS canônico (em campo-geral.css):

.cg-hint {
  display: block;
  font-size: 0.8rem;
  color: var(--ws-muted, var(--text-muted, #94a3b8));
}

Regra: hint desaparece quando há erro — o erro tem prioridade visual.

Tier 2 — Dica Contextual (com ícone Info)

Para quando o campo tem comportamento que o usuário precisa entender — sugestão automática, valor calculado editável, preenchimento inteligente. O ícone Info sinaliza "isso é uma informação sobre o campo, não um erro".

import { Info } from '@phosphor-icons/react'

<CampoGeralGlobal label="Número do Pedido" obrigatorio>
  <input ... />
</CampoGeralGlobal>
<span className="cg-hint-contextual">
  <Info size={14} weight="fill" style={{ flexShrink: 0, opacity: 0.6 }} />
  Sugestão automática — você pode editar livremente.
</span>

Spec visual:

.cg-hint-contextual {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.8rem;
  color: var(--text-muted, #94a3b8);
}
Propriedade Valor Justificativa
Ícone <Info size={14} weight="fill"> Phosphor Info, fill para destaque sutil
Opacidade do ícone 0.6 Suficiente para ser notado sem competir com o label
gap 0.375rem (6px) Espaçamento confortável entre ícone e texto
font-size 0.8rem Alinhado com .cg-hint (Tier 1)
color var(--text-muted, #94a3b8) Mesmo tom do hint padrão

Quando usar Tier 2 vs Tier 1: se o texto descreve formato ou restrição → Tier 1 (hint puro). Se descreve comportamento do campo (sugestão, cálculo, preenchimento automático) → Tier 2 (com ícone).

Tier 3 — Status Badge

Para indicar estado de validação dinâmico — campo sendo verificado, válido, inválido. Cada estado tem cor e ícone semânticos.

// Exemplo: validação de NCM
<span className="ncm-status ncm-status--valido">
  <CheckCircle size={12} weight="fill" />
  NCM válido
</span>
Estado Cor Ícone
Válido var(--success) (#22c55e) CheckCircle 12px fill
Atenção var(--warning) (#f59e0b) Warning 12px fill
Erro var(--danger) (#ef4444) XCircle 12px fill
Carregando var(--text-muted) (#94a3b8) ArrowsClockwise 12px + animação spin

Anti-pattern proibido: usar Tier 3 (badge colorido) para informação estática. Badge é só para estado que muda.


Padrão de Campo Obrigatório (3 sinais oficiais)

Quando um campo é obrigatório e está vazio, o sistema mostra 3 sinais redundantes com propósitos distintos (alinhado com Material Design / Apple HIG):

Sinal Tipo Onde Propósito
Asterisco * vermelho estático ao lado do label "este campo é obrigatório, sempre será"
Borda vermelha no input dinâmico volta do <input> / <select> "está vazio AGORA — precisa preencher"
Banner BannerRequisitosGlobal consolidado acima do botão de avançar/salvar lista TUDO que falta com mensagens amigáveis

Anti-pattern proibido: legenda "* Campos obrigatórios" no rodapé do form. Asterisco já tem semântica universal; legenda é ruído visual redundante.

Componente canônico

import { CampoGeralGlobal } from '@nucleo/campo-geral-global'

<CampoGeralGlobal
  label="Número do Pedido"
  obrigatorio
  vazio={!form.numero_pedido.trim()}   // <- regra: vermelho dispara quando obrigatorio && vazio
  erro={erros.numero_pedido}            // <- opcional: mensagem de validação pós-submit
>
  <input value={form.numero_pedido} onChange={...} />
</CampoGeralGlobal>

Path: nucleo-global/Campos/campo-geral-global/

CSS interno (regra de cores e prioridade)

A classe .cg-wrapper--erro é aplicada automaticamente quando (obrigatorio && vazio) || erro. O CSS abaixo (em campo-geral.css) pinta de vermelho:

.cg-wrapper--erro .sg-campo,
.cg-wrapper--erro input,
.cg-wrapper--erro select,
.cg-wrapper--erro textarea {
  border-color: #f87171 !important;
}

Sobre o !important: justificado e obrigatório. Muitos consumidores aplicam border inline via style={{...}} (especificidade 1000). Sem !important, a borda vermelha nunca aparece pra inputs com border inline. Estado de erro é exceção que ganha prioridade — espelha aria-invalid no plano visual.

Componentes custom (que não usam CampoGeralGlobal diretamente)

Quando precisar de layout especial (ex: label com botão "+ Nova" inline), siga o mesmo padrão manualmente:

  1. Adicione className="cg-wrapper cg-wrapper--erro" no wrapper quando vazio || erro — reaproveita a CSS canônica
  2. Renderize o asterisco vermelho com <span style={{ color: '#f87171', marginLeft: '0.125rem' }}>*</span>
  3. Use BannerRequisitosGlobal no nível do formulário pra listar pendências consolidadas

Nunca use outline: 1px solid red ou hack visual próprio — fica invisível ou inconsistente.


5 — Badges de Status

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-pill);
  font-size: 0.75rem;
  font-weight: 600;
}

.badge-success { background: rgba(34,197,94,0.15); color: var(--success); }
.badge-warning { background: rgba(245,158,11,0.15); color: var(--warning); }
.badge-danger  { background: rgba(239,68,68,0.15);  color: var(--danger);  }

6 — Toasts (Notificações do Sistema)

Regra: nunca criar elementos de toast manualmente. Usar sempre addNotification via Shell.

const { addNotification } = useShellStore()
addNotification({ type: 'success', message: 'Operação concluída com sucesso' })
addNotification({ type: 'error',   message: 'Erro ao processar requisição' })
addNotification({ type: 'warning', message: 'Atenção: campo obrigatório' })

7 — KPI Cards (Dashboard)

.kpi-card {
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.kpi-label {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.kpi-value {
  font-size: 1.875rem;
  font-weight: 700;
  color: var(--text-primary);
}

8 — Ícones

Biblioteca obrigatória: @phosphor-icons/react

import { House, Users, Buildings } from '@phosphor-icons/react'

Peso padrão

Contexto Peso (weight)
UI geral (nav, header, botões) "duotone"
Destaque / call-to-action "fill"
Ícone sutil / placeholder "regular"

Regra: sempre usar weight="duotone" como padrão, salvo necessidade explícita.

Tamanhos canônicos

Contexto size
Sidebar nav item 18
Header título (inline com texto) 20
Botão com texto 16
Botão ícone-only / toggle 18
Badge 14

Cor

  • Ícones de acento (destaque, header): color="#6366f1" ou #818cf8 (= --accent)
  • Ícones neutros (nav, botões secundários): herdam color do elemento pai via CSS

Ícones Reservados por Contexto (Regras de Uso)

Elemento Ícone obrigatório Proibido
Botão Hub no header Graph ArrowLeft, CaretLeft, ArrowBack, qualquer seta

Regra: o botão "Hub" em qualquer header/topbar usa <Graph> — nunca seta. O ícone Graph representa "rede/cluster/hub"; setas representam "voltar", o que cria confusão UX. Esta regra foi estabelecida após regressões repetidas em múltiplos layouts (Core, Workspace, Admin). A fonte de verdade é servicos-global/configurador/src/components/HubButton.tsx.

Espaçamento (gap)

O ícone nunca tem margin próprio — o espaçamento é controlado pelo flex container pai:

Contexto gap
Botão (.btn) 0.5rem
Nav item (.ws-nav-item) 0.75rem
Header título (.ws-header__title-row) 0.5rem
Badge (.badge) 0.25rem
/* Exemplo: título de header com ícone */
.ws-header__title-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
// Exemplo de uso em header
<div className="ws-header__title-row">
  <House weight="duotone" size={20} color="#6366f1" />
  <p className="ws-header__title">Área do Cliente</p>
</div>

9 — Navigation Tabs

/* Pill Tabs — para seções de página */
.tabs-pill { display: flex; gap: 0.25rem; padding: 0.25rem; background: var(--bg-surface); border-radius: var(--radius-pill); }
.tab-pill { padding: 0.375rem 1rem; border-radius: var(--radius-pill); font-size: 0.875rem; font-weight: 500; color: var(--text-secondary); cursor: pointer; transition: all 0.15s; }
.tab-pill.active { background: var(--bg-base); color: var(--text-primary); box-shadow: var(--shadow-sm); }

/* Underline Tabs — para conteúdo aninhado em modais */
.tabs-underline { display: flex; gap: 0; border-bottom: 1px solid var(--bg-elevated); }
.tab-underline { padding: 0.5rem 1rem; font-size: 0.875rem; font-weight: 500; color: var(--text-secondary); cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.15s; }
.tab-underline.active { color: var(--accent); border-bottom-color: var(--accent); }

10 — Sistema de Cores por Zona e Produto

Referência completa: documentos-tecnicos/UX/cores.html

Dois Tiers de Cor

Tier Quem Cor Lógica
1 — Plataforma HUB, Core, Admin #818cf8 (Gravity Indigo) Unidade institucional
1 — Plataforma Configurador #f472b6 (Pink 400) Workspace admin — diferente dos produtos
2 — Produto 8 produtos COMEX Cor individual Identidade única por produto

Cores por Produto (Fonte: @nucleo/logo-produtos)

import { getProdutoMeta } from '@nucleo/logo-produtos'
// getProdutoMeta(productId).color → cor de acento

// SimulaCusto    → '#34d399'  Emerald 400
// Pedido         → '#f59e0b'  Amber 400
// BID Câmbio     → '#06b6d4'  Cyan 500
// BID Frete      → '#60a5fa'  Blue 400
// LPCO           → '#f43f5e'  Rose 500
// NF Importação  → '#c084fc'  Purple 400
// Processo       → '#facc15'  Yellow 400
// Financeiro COMEX → '#f472b6' Pink 400

Os 3 Pontos de Contato (ÚNICA aplicação da cor de produto)

Ponto Onde CSS
① Chip do produto Topbar — lado esquerdo background: linear-gradient(135deg, {color}18, {color}08)
② Dot da sidebar Item ativo — indicador background: {color} — circle 6×6px
③ Fundo item ativo Menu lateral — selecionado background: {color}12; color: {color}

Regras Invioláveis

  • Cor de acento via prop — nunca hardcoded em componente. Vem de getProdutoMeta(productId).color
  • Máximo 3 pontos de contato — nunca em fundos de página, botões, titles, badges de status
  • KPI cards — único card que aceita cor: border-top: 2px solid {color}
  • Botões primários — sempre var(--accent) (#6366f1 indigo), nunca cor do produto
  • Fonte única de verdadenucleo-global/Logo/produtos/src/produtos.tsx (PRODUTO_META)

11 — Select Box Customizada

Regra: nunca usar <select> nativo do HTML. Usar sempre o componente advanced-select.

import { CaixaSelectGlobal } from '@nucleo/caixa-campo-select-global'

12 — Wizard Timeline (Stepper)

.stepper { display: flex; align-items: flex-start; gap: 0; }
.step { display: flex; flex-direction: column; align-items: center; flex: 1; }

.step-circle {
  width: 2rem;
  height: 2rem;
  min-width: 2rem;   /* OBRIGATÓRIO — sem isso o círculo deforma */
  flex-shrink: 0;    /* OBRIGATÓRIO — sem isso o círculo encolhe */
  border-radius: 50%;
  background: var(--bg-elevated);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.step-circle.active { background: var(--accent); color: #0f172a; }
.step-circle.done { background: var(--success); color: white; }

.step-connector { flex: 1; height: 2px; background: var(--bg-elevated); margin-top: 1rem; }
.step-connector.done { background: var(--success); }

14 — Layout Profundo de Modal

/* Header do Modal — sempre --bg-surface */
.modal-header {
  background: var(--bg-surface);
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid var(--bg-elevated);
}

/* Body do Modal — sempre --bg-base */
.modal-body {
  background: var(--bg-base);
  padding: 1.5rem;
  flex: 1;
  overflow-y: auto;
}

/* Footer do Modal */
.modal-footer {
  background: var(--bg-surface);
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--bg-elevated);
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
}

15 — Modal de Permissões (Grid de Checkboxes)

.permissions-grid {
  display: grid;
  grid-template-columns: 1fr repeat(4, auto);
  gap: 0.5rem 1.5rem;
  align-items: center;
}

.permission-module-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-primary);
}

.permission-header {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  text-align: center;
}

Regras de Uso Obrigatórias

  1. Sem cores hardcoded — sempre usar variáveis CSS
  2. Sem <select> nativo — usar advanced-select / CaixaSelectGlobal
  3. Botões sempre pillborder-radius: var(--radius-pill) obrigatório
  4. Modais complexos: header e footer em --bg-surface, body em --bg-base
  5. Ícones: exclusivamente Phosphor Icons
  6. Tipografia: exclusivamente Plus Jakarta Sans (UI) e DM Mono (código)
  7. .text-micro: sempre uppercase; nunca para corpo de texto corrido
  8. Steppers: min-width e flex-shrink: 0 obrigatórios nos círculos
  9. Toasts: usar addNotification via Shell; nunca criar elementos manualmente
  10. Tema: dark é o padrão; light theme via body.light-theme
  11. Placeholders: 'Plus Jakarta Sans', 0.875rem, var(--text-muted), opacity: 1, text-align: left — mesmo em campos numéricos. Nunca monospace, nunca valor válido do domínio como texto de placeholder
Install via CLI
npx skills add https://github.com/dmmltda/gravity-antigravity --skill antigravity-design-system
Repository Details
star Stars 1
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator