ITpS

Desenvolver

Usando os tokens

Tutorial passo a passo — do zero até o primeiro botão Sinapse rodando no seu stack (React, Vue 3, Nuxt 4 ou Astro).

Tutorial · ~10 min

O caminho mais curto pro Sinapse rodar

  1. 1. Pré-requisito: Tailwind v4
  2. 2. Instalar @itps/styles
  3. 3. Importar no CSS entry
  4. 4. Validar — primeiro botão
  5. 5. Snippets prontos (Button/Card/Banner/Form)
  6. 6. Adaptar pro seu framework
  7. 7. Componentes interativos

1 · Pré-requisito

Tailwind v4 configurado

O Sinapse depende de Tailwind v4 — os @theme inline mappings e @utility helpers são features novas da v4. Se ainda não tem, siga o guia oficial do Tailwind pro seu framework antes de continuar.

Verificação rápida

# package.json deve listar:
"tailwindcss": "^4.0.0"

2 · Copiar os arquivos .css

Copie e cole os 3 arquivos abaixo no seu projeto

Sem npm, sem git clone, sem submodule. Crie a pasta src/assets/styles/sinapse/ (ou equivalente) no seu projeto e copie o conteúdo dos 3 blocos abaixo em arquivos com os mesmos nomes.

Estrutura final no seu projeto

src/
└── assets/
    └── styles/
        └── sinapse/
            ├── tokens.css       ← cole o conteúdo do bloco abaixo
            ├── components.css   ← cole o conteúdo do bloco abaixo
            └── index.css        ← cole o conteúdo do bloco abaixo

Copie os 3. Não tem decisão a tomar — funciona pra qualquer caminho (snippets do /componentes, shadcn-vue, shadcn, Preline ou Tailwind direto).

Arquivo 1 de 3 tokens.css 444 linhas

Paleta OKLCH (coral, azul, neutros, feedback), tokens semantic (--primary, --background, etc), dark mode, e Tailwind utilities (bg-primary, text-foreground).

/* ==========================================================================
   0. DARK VARIANT — Tailwind v4
   --------------------------------------------------------------------------
   Registra `.dark` como seletor pro variant `dark:` do Tailwind v4
   (default seria prefers-color-scheme). Necessário pra permitir toggle
   manual via classe .dark no <html>.
   ========================================================================== */
@custom-variant dark (&:where(.dark, .dark *));

/* ==========================================================================
   1. PRIMITIVE TOKENS
   --------------------------------------------------------------------------
   Paleta crua. Espelha 1:1 a estrutura do Figma do DS ITpS.
   NÃO usar diretamente em componentes — sempre via semantic tokens.
   ========================================================================== */

:root {
  /* --- Base scale (neutros) --- */
  --itps-base-0:    oklch(1.0000 0.0000 0);      /* #FFFFFF */
  --itps-base-50:   oklch(0.9851 0.0000 0);      /* #FAFAFA */
  --itps-base-100:  oklch(0.9551 0.0000 0);      /* #F0F0F0 */
  --itps-base-200:  oklch(0.8452 0.0000 0);      /* #CCCCCC */
  --itps-base-300:  oklch(0.7668 0.0000 0);      /* #B3B3B3 */
  --itps-base-400:  oklch(0.6830 0.0000 0);      /* #999999 */
  --itps-base-500:  oklch(0.5999 0.0000 0);      /* #808080 */
  --itps-base-600:  oklch(0.5658 0.0000 0);      /* #767676 - accessible-min */
  --itps-base-700:  oklch(0.4202 0.0000 0);      /* #4D4D4D */
  --itps-base-800:  oklch(0.3211 0.0000 0);      /* #333333 */
  --itps-base-900:  oklch(0.2178 0.0000 0);      /* #1A1A1A */
  --itps-base-950:  oklch(0.0000 0.0000 0);      /* #000000 */

  /* --- Primary (coral) --- */
  --itps-primary-50:   oklch(0.9808 0.0064 17.27);    /* #FDF7F7 */
  --itps-primary-100:  oklch(0.9613 0.0131 23.19);    /* #FBEFEE */
  --itps-primary-200:  oklch(0.9198 0.0345 20.01);    /* #FBDCDB */
  --itps-primary-300:  oklch(0.8699 0.0573 20.99);    /* #F8C6C4 */
  --itps-primary-400:  oklch(0.8519 0.0661 22.01);    /* #F7BEBB */
  --itps-primary-500:  oklch(0.8211 0.0813 21.79);    /* #F5B0AD */
  --itps-primary-600:  oklch(0.7849 0.1010 22.60);    /* #F39F9B */
  --itps-primary-700:  oklch(0.7322 0.1319 24.17);    /* #F0857F */
  --itps-primary-800:  oklch(0.6987 0.1531 24.63);    /* #EE736D */
  --itps-primary-900:  oklch(0.6619 0.1762 25.88);    /* #EB5E57 - default */
  --itps-primary-950:  oklch(0.5602 0.2076 25.22);    /* #D3242D */

  /* --- Secondary (azul institucional) --- */
  --itps-secondary-25:   oklch(0.9906 0.0017 247.84);  /* #FBFCFD */
  --itps-secondary-50:   oklch(0.9842 0.0034 247.86);  /* #F8FAFC */
  --itps-secondary-100:  oklch(0.9428 0.0121 247.95);  /* #E6EDF4 */
  --itps-secondary-200:  oklch(0.9108 0.0191 248.04);  /* #D8E3EE */
  --itps-secondary-300:  oklch(0.8481 0.0389 248.29);  /* #BAD0E6 */
  --itps-secondary-400:  oklch(0.7469 0.0778 250.66);  /* #88B1DD */
  --itps-secondary-500:  oklch(0.6882 0.0970 250.73);  /* #6C9FD5 */
  --itps-secondary-600:  oklch(0.6218 0.1189 251.92);  /* #4D8ACC */
  --itps-secondary-700:  oklch(0.5811 0.1320 252.46);  /* #397DC6 */
  --itps-secondary-800:  oklch(0.5306 0.1200 252.60);  /* #326EAF */
  --itps-secondary-900:  oklch(0.4911 0.1378 253.53);  /* #1961AC - default */
  --itps-secondary-950:  oklch(0.4298 0.1197 253.56);  /* #14508F */

  /* --- Terciary (neutro esverdeado) ---
     Escala parcial: 50, 100, 300, 500. Steps faltantes serão adicionados sob demanda. */
  --itps-terciary-50:   oklch(0.9776 0.0021 197.12);   /* #F6F8F8 */
  --itps-terciary-100:  oklch(0.9219 0.0086 197.01);   /* #DFE7E7 */
  --itps-terciary-300:  oklch(0.8940 0.0132 185.06);   /* #D3DFDD */
  --itps-terciary-500:  oklch(0.8425 0.0122 170.20);   /* #C4CECA */

  /* --- Feedback / alert (amarelo) ---
     Namespace da designer: feedback/alert/<step>. Steps especiais formalizados:
     - 950 (min-graphics): mínimo p/ ícone/linha sobre branco passar WCAG
     - 1000 (min-text):    mínimo p/ TEXTO sobre branco passar WCAG */
  --itps-alert-100:   oklch(0.9857 0.0126 86.83);      /* #FEFAF1 */
  --itps-alert-200:   oklch(0.9587 0.0382 90.65);      /* #FBF1D5 */
  --itps-alert-600:   oklch(0.9109 0.0841 90.31);      /* #F7E0A1 */
  --itps-alert-800:   oklch(0.8766 0.1128 89.25);      /* #F4D37C */
  --itps-alert-900:   oklch(0.8423 0.1392 88.83);      /* #F0C653 */
  --itps-alert-950:   oklch(0.6677 0.1288 86.51);      /* #B78E1D — min-graphics */
  --itps-alert-1000:  oklch(0.5680 0.1168 82.85);      /* #986F00 — min-text */

  /* --- Feedback: success (verde) --- */
  --itps-success-100:  oklch(0.9820 0.0084 168.76);    /* #F4FBF8 */
  --itps-success-200:  oklch(0.9291 0.0368 168.35);    /* #D1F0E3 */
  --itps-success-600:  oklch(0.7980 0.1004 166.59);    /* #79D2AF */
  --itps-success-800:  oklch(0.6503 0.1523 157.35);    /* #09AA69 */
  --itps-success-900:  oklch(0.5280 0.1253 157.19);    /* #00804D */

  /* Destructive: coral (--itps-primary-*) é single source of truth para danger/feedback. */
}

/* ==========================================================================
   2. SEMANTIC TOKENS — shadcn-vue
   --------------------------------------------------------------------------
   Tokens que os componentes shadcn esperam.
   Cada um aponta pra um primitive da camada 1.
   Trocando o valor aqui, o look-and-feel do app inteiro muda.
   ========================================================================== */

:root {
  /* === ARQUITETURA ===
     Camada 1: Primitives (--itps-*) — paleta crua OKLCH.
     Camada 2: Semantic shadcn (--background, --foreground, --primary, --destructive, etc) — CANONICAL.
     Componentes consomem APENAS camada 2 via utility (bg-background, text-foreground, etc).

     DARK MODE: redefinir camada 2 dentro de .dark { ... }. */

  /* --- Surfaces & containers (canonical shadcn) --- */
  --background:           var(--itps-base-0);          /* #FFFFFF — canvas */
  --foreground:           var(--itps-base-950);        /* #000000 — text principal */

  --card:                 var(--itps-base-50);         /* #FAFAFA — canvas elevado */
  --card-foreground:      var(--itps-base-950);

  --popover:              var(--itps-base-0);
  --popover-foreground:   var(--itps-base-950);

  --muted:                var(--itps-base-100);        /* #F0F0F0 — container interativo */
  --muted-foreground:     var(--itps-base-700);        /* #5C5C5C — 5.87:1 sobre muted (AA) */

  /* --- Brand semantic — primitives diretos --- */

  /* Primary (coral - identidade ITpS)
     fg=base-950 sobre coral = 6.27:1 (AA). White seria 3.35 (FAIL AA-text). */
  --primary:              var(--itps-primary-900);     /* #EB5E57 */
  --primary-foreground:   var(--itps-base-950);        /* #000000 */

  /* Secondary (azul institucional — tinted surface) */
  --secondary:            var(--itps-secondary-100);
  --secondary-foreground: var(--itps-secondary-950);

  /* Accent (terciary tinted surface) */
  --accent:               var(--itps-terciary-100);
  --accent-foreground:    var(--itps-base-950);

  /* Destructive (= feedback/danger = coral) */
  --destructive:          var(--itps-primary-900);
  --destructive-foreground: var(--itps-base-950);      /* preto sobre coral = 6.27:1 AA */

  /* --- Borders & inputs --- */
  --border:               var(--itps-base-100);
  --input:                var(--itps-base-100);
  --ring:                 var(--itps-primary-900);

  /* --- Feedback semantic (extensão shadcn — não-padrão mas comum) --- */

  /* Danger alias (= destructive) */
  --danger:               var(--itps-primary-900);
  --danger-foreground:    var(--itps-base-950);        /* preto sobre coral = 6.27:1 AA */

  /* Success (estado de sucesso) */
  --success:              var(--itps-success-900);
  --success-foreground:   var(--itps-base-0);

  /* Warning (atenção, não-crítico)
     fg=base-950 (preto) sobre alert-900 (#F0C653) = 12.91:1 AAA.
     alert-1000 é mustard escuro pra texto sobre branco — falha sobre alert-900. */
  --warning:              var(--itps-alert-900);
  --warning-foreground:   var(--itps-base-950);

  /* Info (mensagens informativas) */
  --info:                 var(--itps-secondary-900);
  --info-foreground:      var(--itps-base-0);

  /* --- Radius ---
     Escala Tailwind v4 default + 3 tokens semânticos por uso.
     - card (12px) = containers, cards, panels
     - form (999px) = buttons em formato pill
     - circle (999px) = avatares, badges circulares, dots */
  --radius:               0.5rem;                      /* shadcn alias — 8px */
  --rounded-card:         0.75rem;                     /* 12px */
  --rounded-form:         9999px;                      /* pill */
  --rounded-circle:       9999px;                      /* circle / pill */


  /* --- Charts (5 slots pra séries categóricas — vírus, regiões, períodos) ---
     Paleta inicial mapeada pra primitives. Refinar quando dashboards forem implementados. */
  --chart-1:              var(--itps-secondary-900);   /* azul */
  --chart-2:              var(--itps-success-800);     /* verde */
  --chart-3:              var(--itps-alert-900);       /* amarelo */
  --chart-4:              var(--itps-primary-900);     /* coral */
  --chart-5:              var(--itps-secondary-500);   /* azul claro */


  /* --- Effects (shadows + blur) ---
     Sombras tintadas azul institucional (#14508F com 10% alpha).
     Hierarquia: medium=cards, large=overlays, extra-large=pop-ups.
     2-layer composition (sharp close + soft distant). */
  --shadow-medium:
    0 1px 2px 0 rgb(20 80 143 / 0.10),
    0 4px 6px -1px rgb(20 80 143 / 0.10);
  --shadow-large:
    0 4px 6px -4px rgb(20 80 143 / 0.10),
    0 10px 15px -3px rgb(20 80 143 / 0.10);
  --shadow-extra-large:
    0 8px 10px -6px rgb(20 80 143 / 0.10),
    0 20px 25px -5px rgb(20 80 143 / 0.10);

  --blur-small: 4px;

  /* --- 2k. Typography (font-family) ---
     Open Sans single-family. Aliases font-heading/font-body deixam porta aberta
     pra dual-font futuro sem refactor de componentes.

     DECISÃO: Tailwind defaults canonical para text-*, tracking-*, etc.
     Escala documentada em /estilos/tipografia mas NÃO sobrescreve Tailwind
     globalmente — preserva a11y (rem) e evita
     redimensionamento automático mobile que quebraria componentes não previstos. */

  --font-sans:    'Open Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-heading: var(--font-sans);
  --font-body:    var(--font-sans);
  --font-mono:    ui-monospace, SFMono-Regular, 'JetBrains Mono', 'Roboto Mono', Menlo, Consolas, monospace;
}

/* ==========================================================================
   2z. DARK MODE
   --------------------------------------------------------------------------
   Estratégia: redefinir Layer 2 (state-aware) dentro de .dark.
   Layer 3 (shadcn aliases) cascateia automático — referenciam Layer 2.
   Primitives Layer 1 NÃO mudam.

   Princípios:
   - bg-default-1 = base-900 (#1A1A1A) — não puro black, mais elegante
   - bg-default-2 = base-800 — leve elevação
   - Surfaces ficam mais claros que canvas (oposto do light)
   - Texto principal = base-50 (off-white, evita brilho excessivo)
   - Borders mais sutis (base-800) pra não criar grade agressiva
   - Brand primary mantém saturação (OKLCH 0.66 lightness funciona em dark)
   - Feedbacks bg-default migram pra steps escuros tinted (saturação baixa)
   ========================================================================== */

.dark {
  color-scheme: dark;

  /* --- shadcn semantic (canonical) --- */
  --background:           var(--itps-base-900);          /* #1A1A1A canvas */
  --foreground:           var(--itps-base-50);           /* #FAFAFA texto principal */
  --card:                 var(--itps-base-800);          /* #333333 elevado */
  --card-foreground:      var(--itps-base-50);
  --popover:              var(--itps-base-800);
  --popover-foreground:   var(--itps-base-50);
  --muted:                var(--itps-base-800);          /* container interativo */
  --muted-foreground:     var(--itps-base-300);          /* WCAG AA em dark */

  --primary-foreground:   var(--itps-base-950);          /* preto sobre coral = 6.27:1 AA */
  --secondary:            oklch(0.28 0.05 252);          /* dark blue tinted */
  --secondary-foreground: var(--itps-secondary-300);     /* light blue text */
  --accent:               oklch(0.30 0.01 197);          /* dark teal tinted */
  --accent-foreground:    var(--itps-base-50);
  --destructive-foreground: var(--itps-base-950);        /* preto sobre coral = 6.27:1 AA */

  --border:               var(--itps-base-800);
  --input:                var(--itps-base-800);

  /* Feedback semantic em dark — usar primitives mais claros para contraste */
  --success:              var(--itps-success-600);       /* #79D2AF claro em dark */
  --success-foreground:   var(--itps-base-950);          /* black sobre verde claro */
  --warning:              var(--itps-alert-900);         /* #F0C653 claro */
  --warning-foreground:   var(--itps-base-950);          /* preto sobre amarelo = 12.91:1 AAA */
  --info:                 var(--itps-secondary-400);     /* #88B1DD claro em dark */
  --info-foreground:      var(--itps-base-950);
  --danger-foreground:    var(--itps-base-950);          /* preto sobre coral = 6.27:1 AA */

  /* --- SHADOWS — em dark, sombras tintadas viram inúteis. Override pra darken via overlay-black --- */
  --shadow-medium:
    0 1px 2px 0 rgb(0 0 0 / 0.20),
    0 4px 6px -1px rgb(0 0 0 / 0.20);
  --shadow-large:
    0 4px 6px -4px rgb(0 0 0 / 0.20),
    0 10px 15px -3px rgb(0 0 0 / 0.20);
  --shadow-extra-large:
    0 8px 10px -6px rgb(0 0 0 / 0.20),
    0 20px 25px -5px rgb(0 0 0 / 0.20);
}

/* ==========================================================================
   3. THEME MAPPING — Tailwind v4 @theme inline
   --------------------------------------------------------------------------
   Expõe os semantic tokens como utilitários: bg-primary, text-foreground, etc.
   `inline` é importante: sem ele, perde o suporte a opacity modifiers
   (ex: bg-primary/50).
   ========================================================================== */

@theme inline {
  /* Surfaces */
  --color-background:           var(--background);
  --color-foreground:           var(--foreground);
  --color-card:                 var(--card);
  --color-card-foreground:      var(--card-foreground);
  --color-popover:              var(--popover);
  --color-popover-foreground:   var(--popover-foreground);

  /* Brand */
  --color-primary:              var(--primary);
  --color-primary-foreground:   var(--primary-foreground);
  --color-secondary:            var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);

  /* Neutral helpers */
  --color-muted:                var(--muted);
  --color-muted-foreground:     var(--muted-foreground);
  --color-accent:               var(--accent);
  --color-accent-foreground:    var(--accent-foreground);

  /* Semantic states */
  --color-destructive:          var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-danger:               var(--danger);
  --color-danger-foreground:    var(--danger-foreground);
  --color-success:              var(--success);
  --color-success-foreground:   var(--success-foreground);
  --color-warning:              var(--warning);
  --color-warning-foreground:   var(--warning-foreground);
  --color-info:                 var(--info);
  --color-info-foreground:      var(--info-foreground);

  /* Borders / inputs */
  --color-border:               var(--border);
  --color-input:                var(--input);
  --color-ring:                 var(--ring);

  /* State-aware semantic tokens são expostos via @utility blocks abaixo
     (property-scoped — bg-disabled e border-disabled podem ter valores distintos). */

  /* Charts */
  --color-chart-1:              var(--chart-1);
  --color-chart-2:              var(--chart-2);
  --color-chart-3:              var(--chart-3);
  --color-chart-4:              var(--chart-4);
  --color-chart-5:              var(--chart-5);

  /* Radius - shadcn-vue espera essas variantes calculadas a partir de --radius */
  --radius-sm:  calc(var(--radius) - 4px);
  --radius-md:  calc(var(--radius) - 2px);
  --radius-lg:  var(--radius);
  --radius-xl:  calc(var(--radius) + 4px);

  /* --- Brand scales (escalas completas como utilitários) ---
     Permite bg-primary-50, text-secondary-300, hover:bg-primary-100 etc.
     Dark mode futuro: redefinir os primitives `--itps-*` em .dark — utilitários
     se adaptam automaticamente sem tocar em nenhuma classe. */
  --color-base-0:        var(--itps-base-0);
  --color-base-50:       var(--itps-base-50);
  --color-base-100:      var(--itps-base-100);
  --color-base-200:      var(--itps-base-200);
  --color-base-300:      var(--itps-base-300);
  --color-base-400:      var(--itps-base-400);
  --color-base-500:      var(--itps-base-500);
  --color-base-600:      var(--itps-base-600);
  --color-base-700:      var(--itps-base-700);
  --color-base-800:      var(--itps-base-800);
  --color-base-900:      var(--itps-base-900);
  --color-base-950:      var(--itps-base-950);

  --color-primary-50:    var(--itps-primary-50);
  --color-primary-100:   var(--itps-primary-100);
  --color-primary-200:   var(--itps-primary-200);
  --color-primary-300:   var(--itps-primary-300);
  --color-primary-400:   var(--itps-primary-400);
  --color-primary-500:   var(--itps-primary-500);
  --color-primary-600:   var(--itps-primary-600);
  --color-primary-700:   var(--itps-primary-700);
  --color-primary-800:   var(--itps-primary-800);
  --color-primary-900:   var(--itps-primary-900);
  --color-primary-950:   var(--itps-primary-950);

  --color-secondary-25:   var(--itps-secondary-25);
  --color-secondary-50:   var(--itps-secondary-50);
  --color-secondary-100:  var(--itps-secondary-100);
  --color-secondary-200:  var(--itps-secondary-200);
  --color-secondary-300:  var(--itps-secondary-300);
  --color-secondary-400:  var(--itps-secondary-400);
  --color-secondary-500:  var(--itps-secondary-500);
  --color-secondary-600:  var(--itps-secondary-600);
  --color-secondary-700:  var(--itps-secondary-700);
  --color-secondary-800:  var(--itps-secondary-800);
  --color-secondary-900:  var(--itps-secondary-900);
  --color-secondary-950:  var(--itps-secondary-950);

  --color-terciary-50:   var(--itps-terciary-50);
  --color-terciary-100:  var(--itps-terciary-100);
  --color-terciary-300:  var(--itps-terciary-300);
  --color-terciary-500:  var(--itps-terciary-500);

  --color-alert-100:     var(--itps-alert-100);
  --color-alert-200:     var(--itps-alert-200);
  --color-alert-600:     var(--itps-alert-600);
  --color-alert-800:     var(--itps-alert-800);
  --color-alert-900:     var(--itps-alert-900);
  --color-alert-950:     var(--itps-alert-950);
  --color-alert-1000:    var(--itps-alert-1000);

  --color-success-100:   var(--itps-success-100);
  --color-success-200:   var(--itps-success-200);
  --color-success-600:   var(--itps-success-600);
  --color-success-800:   var(--itps-success-800);
  --color-success-900:   var(--itps-success-900);

  /* --- Typography (font-family) ---
     Expõe font-sans/font-heading/font-body como utilitários Tailwind.
     `font-mono` mantido p/ blocos de código.
     Sizes/tracking permanecem Tailwind defaults — não sobrescrevemos aqui. */
  --font-sans:    var(--font-sans);
  --font-heading: var(--font-heading);
  --font-body:    var(--font-body);
  --font-mono:    var(--font-mono);
}

/* ==========================================================================
   4. UTILITIES — radius, shadows, blur, typography
   --------------------------------------------------------------------------
   Utilities customizadas que não são geradas automaticamente por @theme.
   ========================================================================== */


/* --- RADIUS semantic group ---
   Use no lugar de rounded-* arbitrário. Cada role tem propósito explícito. */
@utility rounded-card        { border-radius: var(--rounded-card);   }
@utility rounded-form        { border-radius: var(--rounded-form);   }
@utility rounded-circle      { border-radius: var(--rounded-circle); }


/* --- EFFECTS (sombras + blur) ---
   Sombras tintadas azul institucional (#14508F com 10% alpha).
   Hierarquia: medium=cards, large=overlays, extra-large=pop-ups. */
@utility shadow-medium      { box-shadow: var(--shadow-medium); }
@utility shadow-large       { box-shadow: var(--shadow-large); }
@utility shadow-extra-large { box-shadow: var(--shadow-extra-large); }

/* Aliases por use-case */
@utility shadow-card    { box-shadow: var(--shadow-medium); }
@utility shadow-overlay { box-shadow: var(--shadow-large); }
@utility shadow-popup   { box-shadow: var(--shadow-extra-large); }

@utility backdrop-blur-small { backdrop-filter: blur(var(--blur-small)); }

/* --- TYPOGRAPHY: italic combos + body/heading shortcuts ---
   Designer nomeia presets compostos: italic-semibold (italic+600), italic-bold (italic+700).
   Reproduz como @utility pra evitar repetir `italic font-semibold` em componentes. */
@utility italic-semibold {
  font-style: italic;
  font-weight: 600;
}
@utility italic-bold {
  font-style: italic;
  font-weight: 700;
}
Arquivo 2 de 3 components.css 174 linhas

Classes prontas .itps-btn, .itps-card, .itps-chip, .itps-input, .itps-callout — atalhos usados pelos snippets do /componentes.

/* ==========================================================================
   @itps/styles — Component classes
   --------------------------------------------------------------------------
   Framework-agnostic. Each app (Astro / Vue / React / Nuxt) writes a thin
   markup wrapper that just applies these classes — zero JS variant engine,
   zero runtime, zero per-framework lib to publish.

   Naming: BEM-ish with `itps-` prefix to avoid collisions.
     .itps-btn                ← base
     .itps-btn--primary       ← variant
     .itps-btn--sm            ← size
     .itps-btn.is-loading     ← state (use `is-*` for boolean states)

   Implementation: Tailwind v4 `@layer components` so consumers can still
   override with utilities at the call site.

   Tokens used (CANONICAL Layer 3 — shadcn aliases):
     bg-background, bg-card, bg-muted, bg-popover, bg-accent
     text-foreground, text-muted-foreground, text-card-foreground
     bg-primary, text-primary-foreground
     bg-secondary, text-secondary-foreground
     bg-destructive, text-destructive-foreground
     border-border, border-input, ring-ring

   Feedback tinted utilities:
     bg-success-default, text-success-default, bg-alert-default, etc
   ========================================================================== */

@layer components {
  /* --- Button -------------------------------------------------------------- */
  /* Sizes alinham com form-height tokens do DS:
     sm = 40px, md = 48px (default), lg = 56px. */
  .itps-btn {
    @apply inline-flex items-center justify-center gap-2 select-none whitespace-nowrap
           h-12 px-5
           rounded-form
           text-base font-semibold leading-none
           transition-colors duration-150
           focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-ring
           disabled:cursor-not-allowed;
  }

  /* Primary — spec Figma "action / button / primary":
     default = outline coral + texto azul institucional + ícones coral,
     hover  = filled coral + texto branco + ícones brancos,
     disabled = muted cinza (sem borda visível). */
  .itps-btn--primary {
    @apply bg-transparent text-info
           border border-primary
           hover:bg-primary hover:text-white hover:border-primary
           active:bg-primary-950 active:text-white
           disabled:bg-muted disabled:text-muted-foreground disabled:border-transparent
           disabled:hover:bg-muted disabled:hover:text-muted-foreground;
  }
  .itps-btn--primary > .material-symbols-outlined { color: var(--primary); }
  .itps-btn--primary:hover:not(:disabled) > .material-symbols-outlined,
  .itps-btn--primary:active:not(:disabled) > .material-symbols-outlined { color: #fff; }
  .itps-btn--primary:disabled > .material-symbols-outlined,
  .itps-btn--primary[aria-disabled="true"] > .material-symbols-outlined { color: var(--muted-foreground); }

  /* Outline = alias do primary (mantém retrocompat com snippets antigos). */
  .itps-btn--outline {
    @apply bg-transparent text-info
           border border-primary
           hover:bg-primary hover:text-white hover:border-primary
           active:bg-primary-950 active:text-white
           disabled:bg-muted disabled:text-muted-foreground disabled:border-transparent
           disabled:hover:bg-muted disabled:hover:text-muted-foreground;
  }
  .itps-btn--outline > .material-symbols-outlined { color: var(--primary); }
  .itps-btn--outline:hover:not(:disabled) > .material-symbols-outlined,
  .itps-btn--outline:active:not(:disabled) > .material-symbols-outlined { color: #fff; }
  .itps-btn--outline:disabled > .material-symbols-outlined,
  .itps-btn--outline[aria-disabled="true"] > .material-symbols-outlined { color: var(--muted-foreground); }

  .itps-btn--secondary {
    @apply bg-secondary text-secondary-foreground
           hover:bg-secondary-200
           active:bg-secondary-300
           disabled:bg-muted disabled:text-muted-foreground;
  }

  .itps-btn--ghost {
    @apply bg-transparent text-foreground
           hover:bg-accent
           active:bg-muted
           disabled:text-muted-foreground disabled:hover:bg-transparent;
  }

  .itps-btn--sm { @apply h-10 px-4 text-sm; }
  .itps-btn--lg { @apply h-14 px-6 text-xl; }
  .itps-btn--icon { @apply w-12 px-0; }
  .itps-btn--icon.itps-btn--sm { @apply w-10; }
  .itps-btn--icon.itps-btn--lg { @apply w-14; }

  /* --- Card ---------------------------------------------------------------- */
  .itps-card {
    @apply rounded-card border border-border bg-card text-card-foreground p-6;
  }

  .itps-card--elevated {
    @apply border-transparent shadow-card;
  }

  /* --- Field (form row) ---------------------------------------------------- */
  .itps-field {
    @apply flex flex-col gap-1.5;
  }

  .itps-field__label {
    @apply text-sm font-medium text-foreground;
  }

  .itps-field__hint {
    @apply text-xs text-muted-foreground;
  }

  .itps-field__error {
    @apply text-xs text-destructive;
  }

  /* --- Input --------------------------------------------------------------- */
  .itps-input {
    @apply w-full h-10 px-3
           rounded-form
           border border-input bg-background
           text-sm text-foreground
           placeholder:text-muted-foreground
           transition-colors
           focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
           disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-60;
  }

  .itps-input--error {
    @apply border-destructive focus-visible:ring-destructive;
  }

  /* --- Chip / Badge -------------------------------------------------------- */
  .itps-chip {
    @apply inline-flex items-center gap-1
           h-6 px-2
           rounded-full
           text-xs font-medium
           bg-secondary text-secondary-foreground;
  }

  .itps-chip--primary { @apply bg-primary-100 text-primary-900; }
  /* Feedback chips — shadcn pattern com opacity modifier para tinted bg */
  .itps-chip--success { @apply bg-success/15 text-success; }
  .itps-chip--warning { @apply bg-warning/20 text-warning-foreground; }
  .itps-chip--danger  { @apply bg-destructive/15 text-destructive; }

  /* --- Link ---------------------------------------------------------------- */
  .itps-link {
    @apply text-foreground underline-offset-4
           hover:underline hover:text-primary
           focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm;
  }

  /* --- Callout ------------------------------------------------------------- */
  .itps-callout {
    @apply flex gap-3
           rounded-card border border-border
           p-4
           bg-card text-card-foreground;
  }

  /* Feedback callouts — shadcn pattern com opacity modifier */
  .itps-callout--info    { @apply border-info/30 bg-info/10 text-foreground; }
  .itps-callout--success { @apply border-success/30 bg-success/10 text-foreground; }
  .itps-callout--warning { @apply border-warning/30 bg-warning/15 text-foreground; }
  .itps-callout--danger  { @apply border-destructive/30 bg-destructive/10 text-foreground; }
}
Arquivo 3 de 3 index.css 18 linhas

Entry curtinho que importa os 2 acima. É o arquivo que você vai referenciar no passo 3.

/* ==========================================================================
   @itps/styles — Entry point
   --------------------------------------------------------------------------
   Consumer must import "tailwindcss" (and optionally "tw-animate-css") BEFORE
   this file, e.g.:

     @import "tailwindcss";
     @import "tw-animate-css";
     @import "@itps/styles";

   Or import parts only:
     @import "@itps/styles/tokens.css";
     @import "@itps/styles/components.css";
   ========================================================================== */

@import "./tokens.css";
@import "./components.css";

3 · Importar

Criar / editar o CSS entry do seu projeto

Crie (ou edite) o arquivo CSS principal do seu framework e adicione 2 @import — Tailwind primeiro, Sinapse depois. O caminho do Sinapse aponta pros arquivos que você copiou no passo 2.

Astro

Crie: src/styles/global.css

@import "tailwindcss";
@import "../assets/styles/sinapse/index.css";

No src/layouts/BaseLayout.astro adicione: import '../styles/global.css'

Vue 3 (Vite)

Crie: src/assets/main.css

@import "tailwindcss";
@import "./styles/sinapse/index.css";

No src/main.ts: import './assets/main.css'

Nuxt 4

Crie: app/assets/css/main.css

@import "tailwindcss";
@import "../styles/sinapse/index.css";

No nuxt.config.ts: css: ['~/assets/css/main.css']

React / Next

Crie ou edite: app/globals.css (Next) ou src/index.css (Vite)

@import "tailwindcss";
@import "./styles/sinapse/index.css";

Next App Router já importa globals.css no app/layout.tsx. Vite: import './index.css' no main.tsx.

Variação dos caminhos

O @import do Sinapse usa caminho relativo à pasta do .css que está importando. Se você copiou pra src/assets/styles/sinapse/, ajuste ./, ../ conforme onde o seu CSS entry mora. Submodule (Modo B): @import "../../../vendor/sinapse/packages/styles/index.css"; — depende de profundidade.

4 · Validar

Primeiro botão — coral Sinapse

Cole esse HTML em qualquer página/componente. Se aparecer botão coral arredondado em pill, install funcionou.

<button type="button" class="inline-flex items-center gap-2 rounded-form
                               bg-primary px-5 py-3 text-sm font-medium
                               text-primary-foreground transition-colors
                               hover:bg-primary-800">
  Sinapse rodando
  <span class="material-symbols-outlined text-[18px]">check_circle</span>
</button>

✗ Sem cor coral? Verifique: (1) ordem do @import — Tailwind antes do Sinapse, (2) caminho do arquivo CSS no entry do framework, (3) Tailwind v4 instalado (não v3).

5 · Snippets prontos

Padrões core — Button, Card, Banner, Form

HTML + classes Tailwind. Cole no seu template e adapte só a sintaxe de class binding do seu framework (passo 6).

Card

Indicador epidemiológico

Casos confirmados nos últimos 7 dias.

<div class="rounded-card border border-border bg-card p-6 shadow-medium">
  <h4 class="text-base font-semibold text-foreground">Título</h4>
  <p class="mt-1 text-sm text-muted-foreground">Body</p>
</div>

Banner (success)

check_circle

Backup configurado

Backups diários às 02h.

<div class="flex items-start gap-3 rounded-card border border-success/30
            bg-success/10 p-4 text-foreground">
  <span class="material-symbols-outlined shrink-0 text-success"
        style="font-size: 20px">check_circle</span>
  <div class="flex flex-col gap-0.5">
    <p class="text-sm font-medium text-foreground">Backup configurado</p>
    <p class="text-[13px] text-muted-foreground">Backups diários às 02h.</p>
  </div>
</div>

Form field (label + input + helper)

Usamos somente para envio de releases internos.

<div class="flex flex-col gap-1.5">
  <label for="email" class="text-sm font-medium text-foreground">Email</label>
  <input type="email" id="email" placeholder="..."
    class="rounded-form border border-input bg-background px-4 py-3
           text-sm text-foreground placeholder:text-muted-foreground
           focus:border-ring focus:outline-none focus:ring-2 focus:ring-ring/30" />
  <p class="text-[13px] text-muted-foreground">Helper text</p>
</div>

6 · Adaptar pro framework

Mesmo HTML, sintaxes diferentes

Pegue qualquer snippet acima e adapte só duas coisas: como você escreve class e como você compõe o template.

Astro

---
const { label } = Astro.props;
---
<button class="bg-primary ...">
  {label}
</button>

Vue 3 / Nuxt 4 SFC

<script setup lang="ts">
defineProps<{ label: string }>()
</script>

<template>
  <button class="bg-primary ...">
    {{ label }}
  </button>
</template>

React / Next.js

export function Button({ label }: { label: string }) {
  return (
    <button className="bg-primary ...">
      {label}
    </button>
  );
}

HTML puro

<button class="bg-primary ...">
  Click
</button>

7 · Interatividade

Modal, dropdown, tabs — engine headless do seu stack

Componentes interativos precisam de JavaScript. O Sinapse não distribui plugin JS ainda — use o engine headless padrão do seu framework e aplique as classes Sinapse aos primitives. Cada caminho tem guia dedicado:

Próximos passos

Onde ir