TuBrief
구독 채널
비디오
커뮤니티

Eine Build-Pipeline zur Isolierung externer Claude-Designfähigkeiten im Produktionscode

TuBrief 편집팀
2026년 9월 11일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Deutsch한국어EnglishEspañol中文العربيةहिन्दीFrançaisPortuguêsРусскийBahasa Indonesia日本語

관련 영상

Insane Claude Design Skills You Need To Actually Build Beautiful Sites12:55

Insane Claude Design Skills You Need To Actually Build Beautiful Sites

AI LABS

커뮤니티의 다른 글

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Eine Build-Pipeline zur Isolierung externer Claude-Designfähigkeiten im Produktionscode

Für Entwickler, die ohne Designer im Alleingang einen Full-Stack-Service aufbauen, sind externe Design-Skill-Dokumente äußerst verlockend. Man kopiert einen 5.000 Zeilen langen Design-System-Prompt, den man auf Twitter oder GitHub gefunden hat, fügt ihn in die Claude-Systemanweisungen ein, und hofft, dass die Benutzeroberfläche dadurch sauber aussieht.

In der Realität tritt genau das Gegenteil ein. Schon beim Absenden der ersten Nachricht erscheint eine Token-Limit-Warnung, und die API-Antwortzeit zieht sich endlos in die Länge. Der vom Modell generierte Code packt unkenntliche Inline-Styles in die Komponenten oder importiert ein Framer Motion-Modul, das nicht einmal im Projekt installiert ist. Schließlich korrigiert man die beschädigte UI bis in die Morgenstunden manuell und kehrt zum ursprünglichen shadcn/ui-Standard-Template zurück.

Das Problem liegt nicht im ästhetischen Empfinden des Modells. Die Ursache ist die Art und Weise, wie unstrukturierter Fließtext-Markdown ungefiltert in den System-Prompt gekippt wird. Man muss die sprachlichen Stilbeschreibungen in maschinenlesbare Token-Spezifikationen umwandeln und eine Pipeline aufbauen, die den vom Modell generierten Code vor dem Erreichen der Produktionsdateien per Isolationstest überprüft.

Weg mit emotionalem Markdown, her mit der W3C DTCG JSON-Spezifikation

Das direkte Einfügen des gesamten Design-System-Markdowns in den Prompt führt zu massiver Token-Verschwendung. Ästhetische Floskeln wie „ein tiefes Navyblau, das beim Benutzer Vertrauen erweckt“ sind für die Layout-Erstellung völlig nutzlos. Solche Sätze verbrauchen im Sprachmodell Rechenressourcen, die stattdessen für TypeScript-Interfaces oder Validierungslogik benötigt würden.

Man streicht die Beschreibungen in natürlicher Sprache und baut den System-Prompt stattdessen mit JSON-Objekten im Format der W3C Design Tokens Community Group (DTCG) neu auf. Übrig bleiben nur Farben, Abstände und Eckenradien.

`json
{
"color": {
"background": {
"surface": { "value":"oklch(0.980.005250)","value": "oklch(0.98 0.005 250)", "value":"oklch(0.980.005250)","type": "color" },
"canvas": { "value":"oklch(1.000)","value": "oklch(1.0 0 0)", "value":"oklch(1.000)","type": "color" }
},
"action": {
"primary": { "value":"oklch(0.550.20250)","value": "oklch(0.55 0.20 250)", "value":"oklch(0.550.20250)","type": "color" },
"primary-hover": { "value":"oklch(0.480.22250)","value": "oklch(0.48 0.22 250)", "value":"oklch(0.480.22250)","type": "color" }
}
},
"spacing": {
"compact": { "value":"0.5rem","value": "0.5rem", "value":"0.5rem","type": "dimension" },
"comfortable": { "value":"1rem","value": "1rem", "value":"1rem","type": "dimension" }
},
"radius": {
"md": { "value":"0.5rem","value": "0.5rem", "value":"0.5rem","type": "dimension" }
}
}

`

Diese standardisierte Token-Spezifikation wird mit Anthropic Prompt Caching kombiniert. Basis-Tokens und System-Rolle werden als fester Block definiert und mit cache_control: { type: "ephemeral" } versehen.

`typescript
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

export async function requestComponentGeneration(componentBrief: string) {
return await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 4096,
system: [
{
type: "text",
text: Next.js und Tailwind CSS Komponentengenerator. Verwende ausschließlich Klassen, die in der folgenden DTCG-Token-Spezifikation definiert sind, und gib keine Inline-Styles (style-Attribut) aus. [DTCG_TOKENS_JSON_DATA],
cache_control: { type: "ephemeral" },
},
],
messages: [
{
role: "user",
content: componentBrief,
},
],
});
}

`

Durch das Weglassen von Fließtextdokumenten schrumpft die Prompt-Größe von rund 10.000 Tokens auf etwa 1.200 Tokens. Da Prompt-Cache-Lesevorgänge laut Anthropic-Dokumentation nur mit 10 % der Standard-Eingabetoken-Gebühren berechnet werden, sinken die API-Kosten bei wiederholten Aufrufen um über 70 %.

Lint-Regeln gegen willkürliche Inline-Styles und Klassenhonflikte

Bei der Einbindung externer Skills schleicht Claude sich oft willkürliche Werte wie style={{ marginTop: '13px' }} ein. Auch beliebige Utility-Klassen in eckigen Klammern (w-[342px]) werden wahllos erzeugt. Solcher Code untergräbt schrittweise das globale Style-System.

Durch das Einbinden von eslint-plugin-react und eslint-plugin-tailwindcss in die Flat Config von eslint.config.mjs werden diese bereits in der Build-Phase herausgefiltert.

`javascript
import reactPlugin from "eslint-plugin-react";
import tailwindPlugin from "eslint-plugin-tailwindcss";

export default [
{
files: ["**/.{ts,tsx}"],
plugins: {
react: reactPlugin,
tailwindcss: tailwindPlugin,
},
settings: {
tailwindcss: {
callees: ["cn", "cva"],
config: "./tailwind.config.ts",
},
},
rules: {
"react/forbid-dom-props": [
"error",
{
forbid: [
{
propName: "style",
message: "Inline-Styles sind nicht zulässig. Verwenden Sie die zugewiesenen Tailwind-Utilities.",
},
],
},
],
"tailwindcss/no-arbitrary-value": "error",
"tailwindcss/no-custom-classname": [
"error",
{
whitelist: ["animate-.
"],
},
],
},
},
];

`

Die extrahierten Tokens werden auf theme.extend in tailwind.config.ts abgebildet. Das Überschreiben der Standardpalette würde Farben beschädigen, auf die interne shadcn/ui-Primitiven verweisen.

`typescript
import type { Config } from "tailwindcss";
import themeTokens from "./build/tailwind/theme.json";

const config: Config = {
content: ["./src/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
surface: themeTokens.color.background.surface,
canvas: themeTokens.color.background.canvas,
action: {
primary: themeTokens.color.action.primary,
"primary-hover": themeTokens.color.action["primary-hover"],
},
},
borderRadius: {
token: themeTokens.radius.md,
},
},
},
plugins: [require("tailwindcss-animate")],
};

export default config;

`

Interaktionen werden über Class Variance Authority (CVA) Varianten isoliert. Ein direktes Modifizieren von Radix-UI-Primitiven zerstört die Eigenschaften des Barrierefreiheitsbaums (ARIA).

`typescript
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
"inline-flex items-center justify-center rounded-token text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-action-primary text-white hover:bg-action-hover active:scale-[0.98]",
secondary: "bg-surface text-foreground hover:bg-surface/80",
ghost: "hover:bg-surface text-foreground",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-8 px-3 text-xs",
lg: "h-12 px-8 text-base",
},
motion: {
subtle: "transition-all duration-150 ease-out",
expressive: "transition-all duration-300 cubic-bezier(0.16, 1, 0.3, 1)",
},
},
defaultVariants: {
variant: "default",
size: "default",
motion: "subtle",
},
}
);

export interface ActionButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {
asChild?: boolean;
}

export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(
({ className, variant, size, motion, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, motion, className }))}
ref={ref}
{...props}
/>
);
}
);
ActionButton.displayName = "ActionButton";

`

Animationsbeschränkungen zur Wahrung der Bundle-Größe

Wird Claude aufgefordert, auffällige Animationen zu erstellen, greift es gewohnheitsmäßig zu Framer Motion. Gemäß Bundlephobia-Messungen belegt Framer Motion selbst nach Gzip-Komprimierung rund 60 KB. Sobald man für ein paar bewegte Buttons 60 KB hinzufügt, gerät die Ladezeit-Metrik LCP ins Wanken.

Im System-Prompt wird der Import von Drittanbieter-Bibliotheken untersagt und festgelegt, dass ausschließlich Eigenschaften für den Browser-Compositor-Thread verwendet werden dürfen.

`typescript
// Nicht empfohlene Form, die im Main-Thread ein Layout-Reflow auslöst
//

// Empfohlene Form, die im GPU-Compositor-Thread verarbeitet wird

`

Geometrische Eigenschaften wie top, left, width und height berechnen den Layout-Baum bei jedem Frame neu. Im Gegensatz dazu werden das transform-gpu-Utility und opacity unabhängig vom Main-Thread in einer GPU-eigenen Ebene verarbeitet, wodurch flüssige 60 FPS gewährleistet bleiben.

Auch Unschärfe-Effekte im Hintergrund (backdrop-filter: blur()) werden in Scrollbereichen weggelassen. Auf leistungsschwachen mobilen Browsern berechnet ein Blur-Filter innerhalb des Scroll-Viewports die Texturen bei jedem Frame neu, wodurch die Bildwiederholrate unter 20 FPS sinkt. Die Verwendung von einfarbigen, halbtransparenten Alphakanälen (bg-background/80 und ein 1px Rand) anstelle von Blur verhindert Probleme mit der Videospeicherauslastung.

Automatisierte Verifizierungspipeline über Storybook und Playwright

Das direkte Schieben von Claude generiertem Code in das Next.js app/-Verzeichnis führt zu zerstörten Layouts und Hydratationsfehlern. Komponenten werden stattdessen zuerst in einer isolierten Storybook-Umgebung gerendert und erst nach bestandenem Test in die Produktion zusammengeführt.

Durch das Hinzufügen von axe-playwright zu .storybook/test-runner.js wird die Barrierefreiheit nach WCAG 2.1 AA überprüft.

`javascript
const { injectAxe, checkA11y } = require("axe-playwright");

module.exports = {
async preVisit(page) {
await injectAxe(page);
},
async postVisit(page) {
await page.waitForSelector("#storybook-root", { state: "attached" });

await checkA11y(page, "#storybook-root", {
  detailedReport: true,
  axeOptions: {
    runOnly: {
      type: "tag",
      values: ["wcag2a", "wcag2aa"],
    },
  },
});

},
};

`

Schlägt ein Test fehl, werden die Fehlermeldungen direkt an Claude zurückgegeben.

`text
[AI-Feedback-Loop: Prompt zur präzisen Komponentenkorrektur]
Der generierte Code hat den axe-core Barrierefreiheitstest nicht bestanden.
Analysieren Sie das Protokoll und geben Sie Code aus, der unter Beibehaltung der bestehenden Struktur nur die Mängel behebt.

[Fehlerdetails]
Verstoß: color-contrast
Fehlerhafter Knoten:
Grund: Kontrastverhältnis beträgt 2.8:1 und unterschreitet den WCAG AA-Standard von 4.5:1.
Lösungsanweisung: Ersetzen Sie das Text-Utility durch text-foreground oder text-action-primary-hover.

`

Es wird ein Shell-Skript erstellt, das visuelle Regressionen (Visual Regression) per Playwright als Schnappschuss erfasst und Branches nur dann erstellt, wenn alle Prüfungen erfolgreich waren.

`typescript
import { test, expect } from "@playwright/test";
import storybookManifest from "../../storybook-static/index.json";

const stories = Object.values(storybookManifest.entries).filter(
(entry) => entry.type === "story"
);

for (const story of stories) {
test(Schnappschuss-Prüfung: ${story.title} - ${story.name}, async ({ page }) => {
await page.goto(/iframe.html?id=${story.id}&viewMode=story);
await page.waitForSelector("#storybook-root");
await page.waitForLoadState("networkidle");

await expect(page).toHaveScreenshot(`${story.id}.png`, {
  animations: "disabled",
  maxDiffPixelRatio: 0.01,
  threshold: 0.2,
});

});
}

`

`bash
#!/usr/bin/env bash
set -e

COMPONENT_NAME=$1

if [ -z "$COMPONENT_NAME" ]; then
echo "Fehler: Bitte geben Sie den Namen der zu prüfenden Komponente ein."
exit 1
fi

echo "1. Lint-Guardrail-Prüfung"
pnpm eslint "src/components/ui/${COMPONENT_NAME}.tsx" --max-warnings=0

echo "2. Statischer Storybook-Build"
pnpm build-storybook --quiet

echo "3. Storybook Barrierefreiheits-CLI-Test"
pnpm test-storybook

echo "4. Playwright visueller Regressions-Schnappschussvergleich"
pnpm playwright test tests/visual/component-regression.spec.ts

echo "5. Bestanden: Branch erstellen und PR senden"
git checkout -b "feature/ui-COMPONENTNAME"gitadd"src/components/ui/{COMPONENT_NAME}" git add "src/components/ui/COMPONENTN​AME"gitadd"src/components/ui/{COMPONENT_NAME}.tsx"
git commit -m "feat(ui): COMPONENTNAMEisolierteVerifizierungabgeschlossen"gitpushorigin"feature/ui−{COMPONENT_NAME} isolierte Verifizierung abgeschlossen" git push origin "feature/ui-COMPONENTN​AMEisolierteVerifizierungabgeschlossen"gitpushorigin"feature/ui−{COMPONENT_NAME}"
gh pr create --title "feat(ui): ${COMPONENT_NAME} hinzufügen" --body "Eine Komponente, die die Storybook A11y- und Playwright-Regressionstests bestanden hat."

`

Man muss nicht mehr bei jeder verzerrten Ansicht die Browser-Entwicklertools öffnen und an Abständen herumschrauben. Durch das Übergeben der Prüfregeln an ein Terminal-Skript lässt sich die UI auch allein sicher erstellen.