TuBrief
Subscribed Channels
Videos
Community

How to Safely Integrate Screen Code Generated by Claude Code into an Existing Project

TuBrief Editorial
September 12, 2026
0
Computing/Software

Written with AI assistance from the source video. The video is the authority.

English한국어Español中文हिन्दीDeutschFrançaisالعربيةPortuguêsРусскийBahasa Indonesia日本語

Related Video

Claude Code Just Got Its Biggest Design Upgrade Of The Year (And Here's How To Master It)10:58

Claude Code Just Got Its Biggest Design Upgrade Of The Year (And Here's How To Master It)

Chase AI

More from the community

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

September 13, 2026

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

September 13, 2026

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

September 13, 2026

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

September 13, 2026

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

September 12, 2026

Apple Won the AI Race

September 12, 2026

Comments (0)

Log in to leave a comment

No posts yet

© 2026 . All rights reserved.

TuBrief
Subscribed Channels
Videos
Community
Log in

How to Safely Integrate Screen Code Generated by Claude Code into an Existing Project

Open Claude Code in the terminal and type /design, and a screen draft pops up in seconds. For a solo developer, this is convenient because it requires less hands-on effort. The trouble begins the moment you open that code.

It completely ignores the shadcn/ui components already set up in the project and crafts brand-new, raw <button> tags instead. Instead of semantic colors registered in the palette, it sprinkles arbitrary hex codes like bg-[#1e293b] across every file. Every time you build a screen, you waste 40 minutes fixing messy import paths and deleting inline styles.

This isn't just your imagination. According to a 2024 study by GitClear analyzing 211 million lines of commits, the proportion of code completely discarded or rewritten within two weeks of introducing AI tools jumped from 3.1% to 5.7%, nearly doubling. The refactoring rate plummeted from 25% to under 10%. Technical debt accumulates just as fast as code grows. Drop the expectation that the model will automatically maintain the existing design system, and tie its hands at the system level instead.


Fixing Component Paths with Project Rule Files

The reason Claude Code ignores existing code is simple: it narrows down and skims only the necessary files to conserve the context window. Without any constraints, the model builds screens by combining the most primitive HTML tags.

Thanks to prompt caching, configuration files at the session root stay at around 10% of the base input cost. They don't disappear even when you reset or compress the conversation. Embedding component reuse rules here prevents the model from recklessly creating raw tags.

Write common component paths and style rules in the CLAUDE.md file at your project root.

`markdown

Design System Guidelines

  1. Component Reuse (STRICT)
  • DO NOT use raw DOM tags (, , ).
  • MUST import from @/components/ui:
    • Button: import { Button } from "@/components/ui/button"
    • Input: import { Input } from "@/components/ui/input"
    • Card: import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
    • Dialog: import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
  • If a component does not exist in @/components/ui, ask to run: "npx shadcn@latest add ".
  1. Token Boundaries
  • NEVER use arbitrary hex codes or pixel widths: NO bg-[#...], NO w-[...px].
  • Use Semantic CSS variables:
    • Surfaces: bg-background, bg-card, bg-muted
    • Text: text-foreground, text-muted-foreground, text-primary
    • Borders: border-border, border-input

`

You don't need to pass a thousand-line global CSS file into your prompt wholesale every time. You can just extract token names into JSON from your Tailwind configuration.

`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)
);

`

Hook this script into postinstall and predev inside package.json.

`json
{
"scripts": {
"postinstall": "node scripts/extract-tokens.mjs",
"predev": "node scripts/extract-tokens.mjs"
}
}

`

The list of available classes updates with every build. The manual labor spent fixing misspelled style classes vanishes.


Automatically Replacing Arbitrary Styles with Hook Scripts

No matter how many warnings you write in the prompt, the model occasionally spits out wacky values. If you throw a single screenshot at it and ask it to build a screen, it will embed fixed widths like w-[380px] tailored to the image aspect ratio. This is the primary culprit behind horizontal scrolling breaking on mobile screens.

To meet WCAG 2.1 AA standards, you must specify criteria by resolution and enforce strict linting the moment a file is generated.

Verification Area Target Criteria Required Tailwind Classes Blocking Condition
Mobile 390px (base) flex-col, w-full, grid-cols-1 Horizontal scroll caused by fixed width usage like w-[...px]
Tablet 768px (md:) md:flex-row, md:grid-cols-2, md:p-6 When mobile 1-column structure persists on wide screens
Desktop 1440px (xl:) xl:max-w-7xl, mx-auto, xl:grid-cols-4 When layout container expands infinitely on high resolution
Dark Mode .dark selector bg-background, text-foreground Leaving default classes like bg-white, text-black alone
Accessibility WCAG 2.1 AA aria-label, <main>, focus-visible:ring-2 Missing screen reader alternative text on icon buttons

When controlling an unruly model, using lifecycle hooks is much more reliable. Using Claude Code's PostToolUse hook executes scripts the moment a file is written to disk.

Register hook commands in .claude/settings.json.

`json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/ast-lint-guard.mjs",
"timeout": 30
}
]
}
]
}
}

`

Now, write the checking script. Catch common hex codes with regular expressions, convert them to project tokens, and enforce rules with the Tailwind ESLint plugin.

`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] Style rule violation:\n${failureLog});
process.exit(1);
}
});

`

Install the linter plugin in your project.

`bash
npm install -D eslint-plugin-tailwindcss

`

When the script outputs exit code 1, Claude Code reads the error log and rewrites the code using semantic classes on the next turn. This reduces the times you struggle with broken layouts during the QA phase.


Physically Separating Views and Business Logic

If you tell Claude Code to build a screen, it tends to blend fetch functions, massive mock data objects, and JSX together into a single file. If you want to attach a real API later, you have to rip out even the rendering code.

Screen code doesn't need to know how state changes. It's safer to split a single feature directory into four files and establish the data schema first.

First, define the data specifications.

`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;
}

`

Next, explicitly command Claude Code not to use any internal state when invoked in the terminal.

`bash
claude "Create src/components/features/dashboard-card/dashboard-card-view.tsx as a pure UI component implementing DashboardCardViewProps from src/components/features/dashboard-card/schema.ts. Do not use useState, useEffect, or fetch internally under any circumstances; build it responsively using only the passed props and @/components/ui elements."

`

Wrap it with a hook when attaching data. Structure mock data and actual API call functions similarly.

`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: "Monthly Active Metrics",
metrics: [
{ id: "m-1", label: "New Inflows", value: "1,240 people", changePercentage: 12.5, trend: "up" },
{ id: "m-2", label: "Churn Rate", 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("Data retrieval failed");
return res.json();
},
});
};

`

Assemble the two in the container component.

`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)}
/>
);
}

`

Refine the screen with useMock={true} before the backend is ready. Once the API is complete, just delete the flag. There is no reason to touch a single line of view code.


Isolating Working Trees and Cherry-Picking Changes

If you chat lengthily with the model in the terminal and tweak the UI, pristine global config files often end up modified or stray temporary files spawn across directories. It's much less stressful to carve out an experiment-only directory while leaving your workspace untouched.

Using Git worktree allows you to run Claude Code in a completely isolated folder.

`bash
git worktree add ../saas-ui-sandbox -b experiment/ai-dashboard-ui
cd ../saas-ui-sandbox
claude

`

If the experiment breaks, you can wipe the whole folder without a second thought.

`bash
cd ../saas-platform
git worktree remove ../saas-ui-sandbox --force
git branch -D experiment/ai-dashboard-ui

`

If you get the desired look, don't merge the entire branch; pick only code snippets of the view files using interactive mode.

`bash
git checkout main
git checkout -p experiment/ai-dashboard-ui -- src/components/features/dashboard-card/dashboard-card-view.tsx

`

Review the code blocks popping up in the terminal, press y for parts you like, and discard weird modifications with n.

If a session exceeds 5 turns, the context gets fuzzy and the model starts talking nonsense. You must clean up sessions periodically:

  1. Type Inspection: Once a single component is built, run npx tsc --noEmit immediately. Proceed to the next prompt only when type errors equal zero.
  2. Context Compression: When conversations get long, don't hesitate to type /compact to minimize token waste.
  3. Clearing Sessions: After screen work finishes, leave a commit and clear memory completely with /clear. When things get tangled, use /rewind to return to a previous checkpoint.

A model's high generation capability is entirely separate from whether that code can enter production. Narrowing input channels with CLAUDE.md, validating output code with lifecycle hooks, and separating workspaces with worktrees will prevent you from staying up all night cleaning up code spit out by AI.