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

Pipeline de build pour isoler les compétences de design externes de Claude dans le code de production

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

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

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

관련 영상

Compétences de design folles de Claude pour créer de superbes sites12:55

Compétences de design folles de Claude pour créer de superbes 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
구독 채널
비디오
커뮤니티
로그인

Pipeline de build pour isoler les compétences de design externes de Claude dans le code de production

Pour un développeur qui crée seul un service full-stack sans designer, les documents de compétences de design externes sont très attrayants. Copier un prompt de système de design de 5 000 lignes trouvé sur Twitter ou GitHub et l'insérer dans les instructions système de Claude donne l'impression que l'interface sera soignée.

En réalité, c'est exactement l'inverse qui se produit. Dès l'envoi du premier message, un avertissement de limite de jetons (tokens) s'affiche et le temps d'attente de la réponse de l'API s'allonge indéfiniment. Le code généré par le modèle insère des styles en ligne (inline styles) non identifiés dans les composants ou importe un module Framer Motion qui n'a jamais été installé dans le projet. Finalement, après avoir corrigé manuellement une interface cassée jusqu'au petit matin, on revient au modèle de base shadcn/ui initial.

Le problème ne vient pas du sens de l'esthétique du modèle, mais de la méthode elle-même qui consiste à déverser du texte brut non structuré en Markdown directement dans le système de invite (prompt). Il est nécessaire de transformer les descriptions de style en langage naturel en spécifications de jetons lisibles par machine et de mettre en place un pipeline de test d'isolation avant que le code généré par le modèle n'atteigne les fichiers de production.

Remplacer le Markdown émotionnel par la spécification JSON W3C DTCG

Coller l'intégralité du Markdown d'un système de design dans le prompt entraîne un gaspillage sévère de jetons. Les modificateurs esthétiques tels que "un bleu marine profond qui inspire confiance aux utilisateurs" ne servent à rien pour la génération de mise en page. Ces phrases gaspillent des ressources de calcul au sein du modèle de langage, qui devraient plutôt être consacrées aux interfaces TypeScript ou à la logique de validation.

Éliminez les descriptions en langage naturel et restructurez le prompt système en objets JSON conformes à la spécification W3C Design Tokens Community Group (DTCG). Ne conservez que les couleurs, les espacements et les rayons de courbure.

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

`

Associez la spécification de jetons standardisée à la mise en cache des prompts d'Anthropic (Prompt Caching). Fixez les jetons de base et le rôle système dans un bloc fixe et appliquez cache_control: { type: "ephemeral" }.

`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: Générateur de composants Next.js et Tailwind CSS. Utilisez strictement les classes définies dans la spécification de jetons DTCG ci-dessous et n'émettez aucun style en ligne (attribut style). [DTCG_TOKENS_JSON_DATA],
cache_control: { type: "ephemeral" },
},
],
messages: [
{
role: "user",
content: componentBrief,
},
],
});
}

`

En éliminant les documents en texte brut, la taille du prompt passe d'environ 10 000 jetons à environ 1 200 jetons. Selon la documentation d'Anthropic, la lecture du cache de prompts ne facture que 10 % du tarif des jetons d'entrée de base, ce qui réduit les coûts d'API de plus de 70 % lors d'appels répétés.

Règles de lissage pour empêcher les styles en ligne arbitraires et les conflits de classes

Lors de l'injection de compétences externes, Claude insère souvent discrètement des valeurs arbitraires telles que style={{ marginTop: '13px' }}. Il génère également de manière anarchique des classes arbitraires entre crochets (w-[342px]). Ce type de code fragilise progressivement le système de style global.

Filtrez ces éléments au stade de la construction en configurant eslint-plugin-react et eslint-plugin-tailwindcss dans le Flat Config de eslint.config.mjs.

`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: "Les styles en ligne ne sont pas autorisés. Utilisez les utilitaires Tailwind spécifiés.",
},
],
},
],
"tailwindcss/no-arbitrary-value": "error",
"tailwindcss/no-custom-classname": [
"error",
{
whitelist: ["animate-.
"],
},
],
},
},
];

`

Mappez les jetons extraits dans theme.extend de tailwind.config.ts. Écraser la palette de base altérerait les couleurs référencées par les primitives internes de shadcn/ui.

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

`

Isolez les interactions à l'aide des variantes Class Variance Authority (CVA). Modifier directement les primitives Radix UI efface les attributs de l'arbre d'accessibilité (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";

`

Limitation des animations pour préserver la taille du bundle

Lorsqu'on lui demande des animations sophistiquées, Claude a pour habitude d'importer Framer Motion. Selon les mesures de Bundlephobia, Framer Motion pèse environ 60 Ko même après compression Gzip. Ajouter 60 Ko juste pour faire bouger quelques boutons suffit à compromettre le LCP, indicateur clé du chargement initial.

Interdisez l'importation de bibliothèques tierces dans le prompt système et imposez l'utilisation exclusive de propriétés dédiées au thread du compositeur du navigateur (Compositor Thread).

`typescript
// Forme non recommandée provoquant un reflow de la mise en page sur le thread principal
//

// Forme recommandée traitée par le thread du compositeur GPU

`

Les propriétés géométriques telles que top, left, width et height recalculent l'arbre de mise en page à chaque image (frame). En revanche, l'utilitaire transform-gpu et opacity sont traités dans une couche indépendante du GPU sans passer par le thread principal, ce qui permet de maintenir un fluide 60 FPS.

Évitez également les effets de flou d'arrière-plan (backdrop-filter: blur()) dans les zones de défilement. Sur les navigateurs mobiles modestes, un filtre de flou dans la zone d'affichage recalculant la texture à chaque image fait chuter la fréquence de rafraîchissement sous la barre des 20 FPS. L'utilisation d'un canal alpha semi-transparent uni (bg-background/80 avec une bordure de 1 px) permet d'éviter les problèmes d'occupation de la mémoire vidéo.

Pipeline de validation automatique via Storybook et Playwright

Envoyer directement le code généré par Claude dans le répertoire app/ de Next.js entraîne des ruptures de mise en page et des erreurs d'hydratation. Le rendu est d'abord effectué dans un environnement Storybook indépendant, et seuls les composants ayant passé les tests sont fusionnés en production.

Ajoutez axe-playwright à .storybook/test-runner.js pour vérifier l'accessibilité selon les normes WCAG 2.1 AA.

`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"],
    },
  },
});

},
};

`

Si un test échoue, renvoyez directement le journal d'erreurs (log) à Claude.

`text
[Boucle de rétroaction IA : Prompt de correction précise du composant]
Le code généré a échoué au test d'accessibilité axe-core.
Analysez les journaux et sortez le code corrigé en conservant la structure d'origine et en éliminant uniquement les défauts.

[Détails de l'échec]
Élément en infraction : color-contrast
Nœud défaillant :
Raison : Le contraste de luminance est de 2.8:1, ce qui est inférieur au seuil WCAG AA de 4.5:1.
Instruction de résolution : Remplacez l'utilitaire de texte par text-foreground ou text-action-primary-hover.

`

Configurez un script shell qui prend des instantanés de régression visuelle avec Playwright et ne crée de branche que lorsque toutes les vérifications sont validées.

`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(Validation d'instantané : ${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 "Erreur : Veuillez saisir le nom du composant à valider."
exit 1
fi

echo "1. Inspection des barrières de lissage (lint)"
pnpm eslint "src/components/ui/${COMPONENT_NAME}.tsx" --max-warnings=0

echo "2. Build statique de Storybook"
pnpm build-storybook --quiet

echo "3. Test CLI d'accessibilité Storybook"
pnpm test-storybook

echo "4. Comparaison d'instantanés de régression visuelle Playwright"
pnpm playwright test tests/visual/component-regression.spec.ts

echo "5. Succès : Création de la branche et envoi de la PR"
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): COMPONENTNAMEveˊrificationd′isolationtermineˊe"gitpushorigin"feature/ui−{COMPONENT_NAME} vérification d'isolation terminée" git push origin "feature/ui-COMPONENTN​AMEveˊrificationd′isolationtermineˊe"gitpushorigin"feature/ui−{COMPONENT_NAME}"
gh pr create --title "feat(ui): Ajout de ${COMPONENT_NAME}" --body "Composant ayant passé les tests Storybook A11y et de régression Playwright."

`

Il n'est plus nécessaire d'ouvrir les outils de développement du navigateur et de manipuler les marges pixel par pixel chaque fois que l'interface se déforme. En transmettant les règles de validation à un script de terminal, vous pouvez concevoir des interfaces en toute sécurité par vous-même.