Desenvolver
Com shadcn (React)
Como usar Sinapse + shadcn em React / Next.js. Tokens semantic compatíveis — componentes shadcn ficam Sinapse-coral automático.
Por que funciona
shadcn/ui usa
os mesmos semantic tokens que a Sinapse @itps/styles:
--primary, --background,
--card, etc. Carregando nossa CSS entry, os componentes shadcn
viram coral Sinapse automaticamente — sem fork, sem patch.
1 · Setup
Instalar tudo
Terminal
# 1. Tailwind v4 (se ainda não tem)
pnpm add tailwindcss
# 2. shadcn init (cria components.json, alias, etc)
pnpm dlx shadcn@latest init
# 3. Sinapse tokens — workspace OU copy
# workspace: pnpm add @itps/styles --workspace
# copy: cp <repo-sinapse>/packages/styles/*.css ./styles/ 2 · CSS entry
Importar Sinapse no globals.css
shadcn cria por default um app/globals.css com
os semantic vars. Substitua por @itps/styles:
app/globals.css
@import "tailwindcss";
@import "@itps/styles"; /* nossa CSS — sobrescreve defaults shadcn */ 3 · Adicionar componentes
CLI shadcn
pnpm dlx shadcn@latest add button
pnpm dlx shadcn@latest add card
pnpm dlx shadcn@latest add dialog # Modal
pnpm dlx shadcn@latest add tabs
pnpm dlx shadcn@latest add select
# ... etc — veja ui.shadcn.com/docs/components
Componentes React ficam em components/ui/. Use direto:
import { Button } from '@/components/ui/button'
export default function Demo() {
return (
<>
<Button>Salvar</Button> {/* coral Sinapse */}
<Button variant="outline">Cancelar</Button>
</>
)
} 4 · Componentes Sinapse drop-in
Substitua os gerados pra ficar 1:1 com o DS
O Button.tsx que a CLI shadcn gera tem styling próprio (radius, altura, hover) — coral mas não idêntico ao Sinapse.
Substitua o conteúdo de cada arquivo em components/ui/ pelos snippets abaixo. Wrappers finos que usam .itps-* — um source of truth (components.css).
Button.tsx
components/ui/button.tsx
import type { ButtonHTMLAttributes } from 'react';
type Variant = 'primary' | 'secondary' | 'ghost' | 'outline';
type Size = 'sm' | 'md' | 'lg';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: Variant;
size?: Size;
}
export function Button({
variant = 'primary',
size = 'md',
className,
...rest
}: ButtonProps) {
const cls = [
'itps-btn',
`itps-btn--${variant}`,
size !== 'md' && `itps-btn--${size}`,
className,
].filter(Boolean).join(' ');
return <button className={cls} {...rest} />;
} Card.tsx
components/ui/card.tsx
import type { HTMLAttributes } from 'react';
interface CardProps extends HTMLAttributes<HTMLDivElement> {
elevated?: boolean;
}
export function Card({ elevated, className, ...rest }: CardProps) {
const cls = ['itps-card', elevated && 'itps-card--elevated', className]
.filter(Boolean).join(' ');
return <div className={cls} {...rest} />;
} Chip.tsx
components/ui/chip.tsx
import type { HTMLAttributes } from 'react';
type Variant = 'default' | 'primary' | 'success' | 'warning' | 'danger';
interface ChipProps extends HTMLAttributes<HTMLSpanElement> {
variant?: Variant;
}
export function Chip({ variant = 'default', className, ...rest }: ChipProps) {
const cls = [
'itps-chip',
variant !== 'default' && `itps-chip--${variant}`,
className,
].filter(Boolean).join(' ');
return <span className={cls} {...rest} />;
} Callout.tsx
components/ui/callout.tsx
import type { HTMLAttributes } from 'react';
type Variant = 'default' | 'info' | 'success' | 'warning' | 'danger';
interface CalloutProps extends HTMLAttributes<HTMLDivElement> {
variant?: Variant;
}
export function Callout({ variant = 'default', className, ...rest }: CalloutProps) {
const cls = [
'itps-callout',
variant !== 'default' && `itps-callout--${variant}`,
className,
].filter(Boolean).join(' ');
return <div className={cls} {...rest} />;
} Input.tsx
components/ui/input.tsx
import { forwardRef, type InputHTMLAttributes } from 'react';
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
error?: boolean;
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ error, className, ...rest }, ref) => {
const cls = ['itps-input', error && 'itps-input--error', className]
.filter(Boolean).join(' ');
return <input ref={ref} className={cls} {...rest} />;
}
);
Input.displayName = 'Input'; Field.tsx
components/ui/field.tsx
import type { ReactNode } from 'react';
interface FieldProps {
label?: string;
hint?: string;
error?: string;
htmlFor?: string;
children: ReactNode;
}
export function Field({ label, hint, error, htmlFor, children }: FieldProps) {
return (
<div className="itps-field">
{label && (
<label htmlFor={htmlFor} className="itps-field__label">{label}</label>
)}
{children}
{error
? <p className="itps-field__error">{error}</p>
: hint && <p className="itps-field__hint">{hint}</p>}
</div>
);
} 5 · Componentes Sinapse-only
RiskClassification, DataOverlay, FeedbackIcon
Estes 3 componentes não existem em shadcn (são domain-specific de saúde pública).
Pegue o HTML+classes da página de cada um em /componentes e converta pra TSX manualmente
(apenas class → className).
Próximos passos