Como integrar com segurança o código de interface gerado pelo Claude Code em um projeto existente
TuBrief 편집팀
2026년 9월 12일
0
Computing/Software원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
Abra o Claude Code no terminal, digite /design e um rascunho de tela aparece em segundos. Para um desenvolvedor solo, isso é prático porque exige menos esforço. O problema começa no momento em que você abre esse código.
Ele nem olha para os componentes do shadcn/ui que já estão configurados no projeto e cria novas tags <button> do zero. Em vez de usar as cores semânticas registradas na paleta, ele espalha códigos hexadecimais arbitrários como bg-[#1e293b] por cada arquivo. A cada tela criada, você perde 40 minutos corrigindo caminhos de importação bagunçados e apagando estilos em linha.
Não é impressão sua. Um estudo da GitClear de 2024, que analisou 211 milhões de linhas de commits, mostrou que, após a introdução de ferramentas de IA, a proporção de código completamente descartado ou reescrito em até duas semanas saltou de 3,1% para 5,7% — quase o dobro. A taxa de refatoração despencou de 25% para menos de 10%. A dívida técnica se acumula na mesma velocidade em que o código cresce. É preciso abandonar a expectativa de que o modelo respeitará o sistema de design existente por conta própria e amarrá-lo a nível de sistema.
A razão pela qual o Claude Code ignora o código existente é simples: ele varre apenas os arquivos necessários de forma restrita para economizar a janela de contexto. Se nenhuma restrição for dada, o modelo compõe as telas usando as tags HTML mais primitivas.
Graças ao cache de prompts, o arquivo de configuração na raiz da sessão é mantido a um custo de entrada de cerca de 10% do padrão. Ele não desaparece mesmo se você reiniciar ou compactar a conversa. Inserir regras de reutilização de componentes aqui impede que o modelo crie tags cruas aleatoriamente.
Adicione os caminhos dos componentes comuns e as regras de estilo no arquivo CLAUDE.md na raiz do projeto.
`markdown
`
Não há necessidade de passar arquivos CSS globais inteiros de milhares de linhas para o prompt o tempo todo. Basta selecionar apenas os nomes dos tokens na configuração do Tailwind e extraí-los para um JSON.
`javascript
// scripts/extract-tokens.mjs
import fs from 'fs';
import resolveConfig from 'tailwindcss/resolveConfig.js';
import tailwindConfig from '../tailwind.config.js';
const fullConfig = resolveConfig(tailwindConfig);
const semanticTokens = {
colors: Object.keys(fullConfig.theme.colors || {}).filter(
(name) => !['inherit', 'current', 'transparent'].includes(name)
),
spacing: Object.keys(fullConfig.theme.spacing || {}),
borderRadius: Object.keys(fullConfig.theme.borderRadius || {}),
};
if (!fs.existsSync('.claude')) {
fs.mkdirSync('.claude');
}
fs.writeFileSync(
'.claude/design-tokens.json',
JSON.stringify(semanticTokens, null, 2)
);
`
Conecte este script aos gatilhos postinstall e predev no package.json.
`json
{
"scripts": {
"postinstall": "node scripts/extract-tokens.mjs",
"predev": "node scripts/extract-tokens.mjs"
}
}
`
A lista de classes disponíveis é atualizada a cada build. O tempo gasto manualmente corrigindo classes de estilo com erros de digitação desaparece.
Por mais que você coloque avisos no prompt, o modelo às vezes gera valores incorretos. Se você jogar uma captura de tela e pedir para ele criar uma interface, ele definirá larguras fixas como w-[380px] com base na proporção da imagem — o principal culpado pelo scroll horizontal que quebra em telas móveis.
Para atender aos critérios do WCAG 2.1 AA, é necessário especificar padrões por resolução e realizar uma inspeção obrigatória com um linter no exato momento em que o arquivo é gerado.
| Área de Verificação | Critério Alvo | Classes Tailwind Obrigatórias | Condição de Bloqueio |
|---|---|---|---|
| Dispositivos Móveis | 390px (base) | flex-col, w-full, grid-cols-1 |
Rolagem horizontal causada pelo uso de largura fixa w-[...px] |
| Tablets | 768px (md:) |
md:flex-row, md:grid-cols-2, md:p-6 |
Quando a estrutura móvel de coluna única é mantida em telas largas |
| Desktops | 1440px (xl:) |
xl:max-w-7xl, mx-auto, xl:grid-cols-4 |
Quando o container de layout se expande infinitamente em altas resoluções |
| Modo Escuro | Seletor .dark |
bg-background, text-foreground |
Classes padrão como bg-white ou text-black deixadas isoladamente |
| Acessibilidade | WCAG 2.1 AA | aria-label, <main>, focus-visible:ring-2 |
Texto alternativo para leitores de tela ausente em botões de ícone |
Ao controlar um modelo desobediente, usar ganchos de ciclo de vida é a escolha mais segura. O gancho PostToolUse do Claude Code permite que um script seja executado assim que um arquivo é gravado no disco.
Registre o comando do gancho em .claude/settings.json.
`json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/ast-lint-guard.mjs",
"timeout": 30
}
]
}
]
}
}
`
Agora, escreva o script de verificação. Ele captura códigos hexadecimais comuns usando expressões regulares, converte-os em tokens do projeto e aplica regras à força usando o plugin ESLint para Tailwind.
`javascript
// .claude/hooks/ast-lint-guard.mjs
import fs from 'fs';
import readline from 'readline';
import { execSync } from 'child_process';
const rl = readline.createInterface({ input: process.stdin });
let inputBuffer = '';
rl.on('line', (line) => { inputBuffer += line; });
rl.on('close', () => {
try {
const payload = JSON.parse(inputBuffer);
const filePath = payload.tool_input?.file_path || payload.tool_input?.path;
if (!filePath || !/\.(tsx|jsx)$/.test(filePath) || !fs.existsSync(filePath)) {
process.exit(0);
}
let code = fs.readFileSync(filePath, 'utf-8');
let changed = false;
const replacementMap = {
'#ffffff': 'bg-background',
'#000000': 'text-foreground',
'#020817': 'bg-background',
'#0f172a': 'bg-card',
'#1e293b': 'bg-muted',
'#64748b': 'text-muted-foreground',
'#2563eb': 'bg-primary',
};
for (const [hex, token] of Object.entries(replacementMap)) {
const regex = new RegExp(`(bg|text|border)-\[${hex}\]`, 'gi');
if (regex.test(code)) {
code = code.replace(regex, token);
changed = true;
}
}
if (changed) {
fs.writeFileSync(filePath, code, 'utf-8');
}
execSync(`npx eslint "${filePath}" --rule "tailwindcss/no-arbitrary-value: error"`, {
stdio: 'pipe',
});
process.exit(0);
} catch (error) {
const failureLog = error.stdout?.toString() || error.stderr?.toString() || error.message;
console.error([Lint Pipeline Block] Violação de regras de estilo:\n${failureLog});
process.exit(1);
}
});
`
Instale o plugin do linter no projeto.
`bash
npm install -D eslint-plugin-tailwindcss
`
Se o script retornar o código de saída 1, o Claude Code lê o log de erros e reescreve o código usando classes semânticas no turno seguinte. Isso reduz a necessidade de perder tempo lidando com telas quebradas durante a fase de QA.
Quando você pede ao Claude Code para montar uma interface, ele costuma misturar funções fetch, objetos gigantescos de dados falsos e JSX em um único arquivo. Para integrar uma API real depois, você acaba tendo que remover todo o código de renderização.
O código de interface não precisa saber como os estados são modificados. É mais seguro dividir um diretório de funcionalidade em quatro arquivos e firmar o esquema de dados primeiro.
Primeiro, defina o formato dos dados.
`typescript
// src/components/features/dashboard-card/schema.ts
import { z } from "zod";
export const MetricItemSchema = z.object({
id: z.string(),
label: z.string(),
value: z.string(),
changePercentage: z.number(),
trend: z.enum(["up", "down", "neutral"]),
});
export const DashboardCardSchema = z.object({
title: z.string().min(1),
metrics: z.array(MetricItemSchema),
});
export type DashboardCardData = z.infer;
export interface DashboardCardViewProps {
data: DashboardCardData;
isLoading?: boolean;
onActionClick?: (metricId: string) => void;
}
`
Em seguida, ao chamar o Claude Code no terminal, restrinja-o explicitamente para não usar nenhum estado interno.
`bash
claude "Crie src/components/features/dashboard-card/dashboard-card-view.tsx, que seja um componente de UI puro implementando DashboardCardViewProps de src/components/features/dashboard-card/schema.ts. Nunca use useState, useEffect ou fetch internamente; construa-o de forma responsiva usando apenas as Props recebidas e elementos de @/components/ui."
`
Envolva os dados com um hook ao integrá-los. Mantenha os dados mockados e a função de chamada de API real com a mesma estrutura.
`typescript
// src/components/features/dashboard-card/use-dashboard-card.ts
import { useQuery } from "@tanstack/react-query";
import { DashboardCardData } from "./schema";
const MOCK_DATA: DashboardCardData = {
title: "Métricas Ativas Mensais",
metrics: [
{ id: "m-1", label: "Novos usuários", value: "1.240", changePercentage: 12.5, trend: "up" },
{ id: "m-2", label: "Taxa de rejeição", value: "2.1%", changePercentage: -0.4, trend: "down" },
],
};
export const useDashboardCard = (cardId: string, useMock = false) => {
return useQuery({
queryKey: ["dashboard-card", cardId],
queryFn: async () => {
if (useMock) {
return MOCK_DATA;
}
const res = await fetch(/api/dashboard/${cardId});
if (!res.ok) throw new Error("Falha ao buscar dados");
return res.json();
},
});
};
`
Monte ambos no componente container.
`typescript
// src/components/features/dashboard-card/index.tsx
"use client";
import React from "react";
import { DashboardCardView } from "./dashboard-card-view";
import { useDashboardCard } from "./use-dashboard-card";
export function DashboardCardContainer({ cardId, useMock = false }: { cardId: string; useMock?: boolean }) {
const { data, isLoading } = useDashboardCard(cardId, useMock);
if (!data) return null;
return (
<DashboardCardView
data={data}
isLoading={isLoading}
onActionClick={(id) => console.log(id)}
/>
);
}
`
Antes que o backend esteja pronto, ajuste a interface usando useMock={true}. Quando a API estiver concluída, basta remover a flag. Não haverá motivo para mexer em uma única linha do código da view.
Ao conversar longamente com o modelo no terminal e ajustar a interface, é comum que arquivos de configuração globais acabem sendo modificados ou que arquivos temporários desnecessários surjam por todo o diretório. É muito mais tranquilo criar um diretório dedicado a experimentos para trabalhar, mantendo o seu espaço de trabalho intocado.
O uso do Git worktree permite executar o Claude Code em uma pasta completamente isolada.
`bash
git worktree add ../saas-ui-sandbox -b experiment/ai-dashboard-ui
cd ../saas-ui-sandbox
claude
`
Se o experimento der errado, basta deletar a pasta inteira sem preocupações.
`bash
cd ../saas-platform
git worktree remove ../saas-ui-sandbox --force
git branch -D experiment/ai-dashboard-ui
`
Se o resultado desejado for alcançado, evite fazer merge da branch inteira; em vez disso, traga apenas os trechos de código do arquivo de visualização usando o modo interativo.
`bash
git checkout main
git checkout -p experiment/ai-dashboard-ui -- src/components/features/dashboard-card/dashboard-card-view.tsx
`
Ao visualizar os blocos de código que aparecem no terminal, pressione y apenas para as partes que gostar e descarte modificações estranhas com n.
Se a sessão ultrapassar 5 turnos, o contexto começa a se perder e o modelo passa a falar besteira. Limpe a sessão sempre que isso acontecer:
npx tsc --noEmit. Prossiga para o próximo prompt apenas quando houver 0 erros de tipo./compact sem hesitar para reduzir o desperdício de tokens./clear para esvaziar completamente a memória. Quando as coisas se embaraçarem, retorne ao ponto de verificação anterior com /rewind.A capacidade de geração de um modelo é uma coisa, mas garantir que esse código possa entrar em produção é algo totalmente diferente. Estreitar o canal de entrada com CLAUDE.md, validar o código de saída com ganchos de ciclo de vida e separar o espaço de trabalho com worktrees poupará você de passar noites em claro consertando o código gerado pela IA.