# Migrar um app existente

Passo a passo para levar ao Sinapse um app React, Next.js ou Vue que ainda não usa shadcn nem Tailwind: preparação, tema, substituição do visual antigo e checklist.

O guia parte de um app que já funciona, com CSS próprio, e termina com o tema, os componentes, a fonte e os ícones do Sinapse. Faça um commit antes de começar: os comandos alteram o CSS principal e instalam dependências.

## 1. Veja de onde o seu app parte

React ou Vue com Vite, sem Tailwind: siga todos os passos. Next.js criado pelo create-next-app já vem com Tailwind v4 e o alias @: pule para o passo 3. App que já usa shadcn ou shadcn-vue: use o passo "Projeto existente" do guia React ou Vue. Astro e HTML não usam o registry: siga os guias Astro e HTML.

Os comandos usam pnpm. Com npm, troque pnpm add por npm install e pnpm dlx por npx.

## 2. Prepare o projeto Vite

Instale o Tailwind v4 com o plugin do Vite, registre o plugin e o alias @ no vite.config.ts e declare o mesmo alias no tsconfig.json e, se o projeto tiver, no tsconfig.app.json. Por último, importe o Tailwind na primeira linha do CSS principal do app (src/index.css no React, src/style.css no Vue).

`terminal`

```bash
pnpm add -D tailwindcss @tailwindcss/vite @types/node
```

`vite.config.ts (React)`

```ts
import { fileURLToPath } from 'node:url'
import tailwindcss from '@tailwindcss/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
  },
})

```

`vite.config.ts (Vue)`

```ts
import { fileURLToPath } from 'node:url'
import tailwindcss from '@tailwindcss/vite'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [vue(), tailwindcss()],
  resolve: {
    alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
  },
})

```

`tsconfig.json (e tsconfig.app.json, se existir)`

```json
"compilerOptions": {
  "paths": { "@/*": ["./src/*"] }
}
```

`src/index.css ou src/style.css (primeira linha)`

```css
@import "tailwindcss";
```

O CLI do shadcn lê o alias do tsconfig.json; nos templates novos do Vite, o compilador lê do tsconfig.app.json. Não use baseUrl: o TypeScript 6 rejeita. O @types/node tipa o fileURLToPath do vite.config.ts; se o projeto tem um tsconfig só, inclua também "node" em compilerOptions.types.

## 3. Aplique o tema e o Button

Em React e Next.js, o init com o base.json aplica o tema, instala o Button Sinapse, configura os ícones Tabler e registra o namespace @sinapse. Em Vue, o init do shadcn-vue prepara o projeto e o add aplica o tema. Depois, adicione os componentes que o app usa.

`terminal · React e Next.js`

```bash
pnpm dlx shadcn@latest init https://dsystem.sinapse.org.br/r/base.json
pnpm dlx shadcn@latest add @sinapse/modal @sinapse/tag
```

`terminal · Vue`

```bash
pnpm dlx shadcn-vue@latest init --base reka --style nova --icon-library tabler --font inter -b neutral
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/theme.json https://dsystem.sinapse.org.br/r/vue/button.json
```

`pnpm-workspace.yaml (Vue com pnpm 11 ou mais recente)`

```yaml
allowBuilds:
  vue-demi: true
```

Sem terminal interativo (agentes de IA, CI), acrescente --yes aos comandos add e init: sem ele, o CLI pergunta "Continue?", cancela sozinho e o pnpm ainda mostra "Done". O registry add não aceita --yes e não precisa dele. Com pnpm 11 ou mais recente, o add do shadcn-vue para com ERR_PNPM_IGNORED_BUILDS por causa do vue-demi; aprove o build antes, no pnpm-workspace.yaml, ou rode pnpm approve-builds. Se o init do shadcn-vue parar com "Cannot find matching keyid", o corepack que vem com o seu Node está desatualizado: atualize o Node 22 para a versão mais recente.

## 4. Troque o visual antigo pelos tokens

O init grava o tema no seu CSS principal, mas não apaga o CSS antigo, e regras antigas podem passar por cima do Sinapse. Remova as variáveis próprias com nomes que o tema usa (--background, --foreground, --border, --accent, --primary, --muted, --card), os blocos @media (prefers-color-scheme: dark) e as regras globais de body, #root e títulos que fixam fonte, largura ou alinhamento. Troque cada cor fixa pelo token com o mesmo papel: fundo da página por background, cartões por card, texto secundário por muted-foreground, bordas por border, destaque de marca por brand (brand-text quando for texto), erro por destructive e sucesso por success. O modo escuro passa a ser a classe .dark no <html>, no lugar da classe ou da preferência do sistema que o app usava.

`src/index.css`

```css
/* Antes: cores fixas e variáveis próprias */
.card { background: #ffffff; color: #1f1637; border: 1px solid #ece6f7; }
.card-title { color: #7c3aed; }

/* Depois: tokens do Sinapse, que já trocam no modo escuro */
.card { background: var(--card); color: var(--card-foreground); border: 1px solid var(--border); }
.card-title { color: var(--brand-text); }
```

Botões, campos, abas, modais e avisos do app antigo devem virar os componentes do Sinapse, não CSS novo. O CSS que sobra no app é de layout: grade, espaçamento e responsividade.

## 5. Open Sans local, Tabler e pt-BR

O tema declara a Open Sans, mas não carrega a fonte. Sirva a fonte pelo próprio app com o @fontsource/open-sans, importado no arquivo de entrada (src/main.tsx ou src/main.ts), e tire o link do Google Fonts do index.html. Troque os ícones antigos pelos do Tabler (@tabler/icons-react ou @tabler/icons-vue) e remova o lucide-react, que o CLI instala mesmo com Tabler configurado. Use lang="pt-BR" no <html> e troque o rótulo "Close" do Dialog e do Sheet por "Fechar".

`terminal`

```bash
pnpm add @fontsource/open-sans
pnpm remove lucide-react
```

`src/main.tsx ou src/main.ts (início)`

```tsx
import '@fontsource/open-sans/400.css'
import '@fontsource/open-sans/500.css'
import '@fontsource/open-sans/600.css'
import '@fontsource/open-sans/700.css'
```

Em React com o Toaster do sonner, passe o tema atual ao <Toaster>: ele não lê a classe .dark sozinho.

## 6. Confira antes de terminar

O app está migrado quando: não sobram cores fixas fora dos componentes do registry; nenhuma fonte vem de fora; o <html> tem lang="pt-BR"; os textos acessíveis estão em português; o modo escuro liga e desliga pela classe .dark; e o pnpm build passa. O comando abaixo deixa de fora o CSS principal, onde o init grava o tema com os valores do Sinapse: revise à mão as regras do app que ficaram nesse arquivo.

`terminal`

```bash
# cores fixas que sobraram (o tema no CSS principal e os componentes do registry ficam de fora)
grep -rnE '#[0-9a-fA-F]{3,8}\b|rgba?\(' src --include='*.css' --include='*.tsx' --include='*.vue' | grep -v -e components/ui -e src/index.css -e src/style.css
# fontes remotas
grep -rn 'fonts.googleapis' index.html src
```

## Próximos passos

- [React](/desenvolver/react): Projeto novo e componentes pelo registry do shadcn.
- [Vue 3](/desenvolver/vue): Projeto novo e componentes pelo registry do shadcn-vue.
- [Tokens](/estilos/tokens): Todos os tokens semânticos, nos temas claro e escuro.
