Pourquoi toutes les pages de destination créées par l'IA se ressemblent et comment y remédier
TuBrief 편집팀
2026년 8월 22일
0
Computing/Software원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
Pour un développeur solo qui gère tout de la conception au déploiement, Cursor ou Claude Code ressemblent à des sauveteurs. Il suffit de taper quelques lignes de prompt pour obtenir une page web crédible en 10 minutes.
Le problème vient ensuite. Lorsque vous appuyez sur le bouton de déploiement et regardez l'écran, un sentiment de déjà-vu s'installe. Boutons avec dégradé violet, police Inter, disposition en cartes à trois colonnes bien au centre. L'odeur d'un modèle déjà-vu est omniprésente. En apparence, c'est propre, mais cela ne se traduit ni par des paiements ni par des inscriptions d'utilisateurs. Les gens repèrent à coup sûr ces pages produites à la chaîne et ferment l'onglet.
Les LLM fonctionnent sur la base de moyennes statistiques. Si on ne leur donne pas de règles, ils reviennent aux préréglages de base de Tailwind et aux valeurs par défaut de Shadcn UI les plus couramment utilisés sur le web. Dès que l'on balance le prompt "Crée-moi une page de destination propre et élégante", l'IA crache des valeurs de pixels en ligne non identifiées comme w-[320px], top-[117px] et ruine la mise en page.
Essayer de résoudre ce problème en modifiant les prompts est épuisant. À chaque génération de code, il faut imposer des règles de marque et mettre en place un pipeline qui bloque physiquement le code non conforme lors de l'étape de commit.
Cursor lit .cursor/rules/*.mdc, Claude Code lit CLAUDE.md, et les outils open source lisent AGENTS.md. En définissant vos jetons (tokens) de design de marque dans ce fichier, vous empêchez l'IA d'utiliser des styles arbitraires dès l'étape de génération du code.
Si un fichier de règles dépasse 500 lignes, le coût de contexte lu à chaque session augmente et le taux de respect des instructions par le modèle diminue. Il doit être rédigé clairement autour de 200 à 300 lignes.
Créez .cursor/rules/design-system.mdc à la racine du projet et ajoutez-y le contenu suivant :
bg-[#123456] or h-[117px].pnpm lint:style to verify token compliance before completing tasks.var(--color-bg-primary) (Tailwind: bg-brand-primary)var(--color-bg-secondary) (Tailwind: bg-brand-secondary)var(--color-text-main) (Tailwind: text-brand-main)var(--color-text-muted) (Tailwind: text-brand-muted)var(--color-accent-default) (Tailwind: bg-brand-accent)var(--space-1): 0.25rem (4px)var(--space-2): 0.5rem (8px)var(--space-4): 1.0rem (16px)var(--space-6): 1.5rem (24px)var(--space-8): 2.0rem (32px)text-brand-h1 -> Font: Inter, Weight: 700, Size: 2.5rem, Tracking: -0.02emtext-brand-body -> Font: Inter, Weight: 400, Size: 1.0rem, Leading: 1.5`
L'intégration de cette règle réduit considérablement la fréquence à laquelle l'IA injecte directement des valeurs arbitraires entre crochets ou des codes hexadécimaux.
Les prompts seuls ne suffisent pas. L'IA ignore souvent les règles et insère en cachette des valeurs de pixels en ligne. En combinant Stylelint et les hooks Git Pre-commit, vous bloquez le passage aux styles codés en dur dans le dépôt.
Installez les outils et initialisez Husky :
`bash
pnpm add -D husky lint-staged stylelint stylelint-declaration-strict-value
npx husky init
`
Créez stylelint.config.mjs à la racine du projet. C'est la configuration qui déclenche une erreur de build si des codes de couleur ou des valeurs de pixels arbitraires sont saisis.
`javascript
import type { Config } from "stylelint";
export default {
plugins: ["stylelint-declaration-strict-value"],
rules: {
"scale-unlimited/declaration-strict-value": [
["/color/", "font-size", "/margin/", "/padding/"],
{
ignoreVariables: false,
ignoreFunctions: false,
ignoreKeywords: {
"": ["transparent", "inherit", "currentColor", "auto", "0"]
},
message: "Design System Violation: Hardcoded value for '{property}' is forbidden. Use CSS Design Tokens instead."
}
]
}
} satisfies Config;
`
Ajoutez la configuration de lint-staged dans package.json :
`json
{
"lint-staged": {
"*.{css,scss,tsx,jsx}": [
"stylelint --fix",
"eslint --max-warnings=0"
]
}
}
`
Enregistrez la ligne suivante dans le fichier .husky/pre-commit :
`bash
npx lint-staged
`
Désormais, chaque fois que vous lancez un git commit, le code stagé est analysé statiquement. S'il y a du code comme margin: 17px introduit arbitrairement par l'IA, le commit lui-même échoue. Vous pouvez économiser environ 5 heures par semaine passées à corriger les styles.
L'analyse statique ne vérifie que les règles de texte. Les problèmes de superposition d'éléments ou de menus cassés sur écran mobile doivent être testés en ouvrant directement le navigateur. Comme il n'est pas possible de réduire et d'agrandir manuellement la vue à chaque fois, l'automatisation de la comparaison de captures d'écran est confiée à Playwright.
Créez le fichier playwright.config.ts :
`typescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/visual',
snapshotPixelRatioTemplate: '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}',
expect: {
toHaveScreenshot: {
maxDiffPixelRatio: 0.01,
threshold: 0.2,
animations: 'disabled',
},
},
webServer: {
command: 'pnpm dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
projects: [
{ name: 'Desktop Chrome', use: { ...devices['Desktop Chrome'] } },
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
],
});
`
Rédigez le script de test (tests/visual/landing-page.spec.ts) qui vérifie 5 zones UI clés :
`typescript
import { test, expect } from '@playwright/test';
test.describe('Visual Regression Guardrails', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000');
await page.evaluate(() => document.fonts.ready);
});
test('TC1: 데스크톱 히어로 섹션 렌더링', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
const heroSection = page.locator('section#hero');
await expect(heroSection).toBeVisible();
await expect(heroSection).toHaveScreenshot('hero-desktop.png', { maxDiffPixelRatio: 0.01 });
});
test('TC2: 모바일 내비게이션 메뉴 뷰포트 렌더링', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 });
const navBar = page.locator('header#main-nav');
await expect(navBar).toHaveScreenshot('nav-mobile.png');
});
test('TC3: 요금제 카드 그리드 정렬', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 800 });
const pricingGrid = page.locator('div#pricing-cards');
await expect(pricingGrid).toHaveScreenshot('pricing-grid.png', {
mask: [page.locator('.dynamic-price-timestamp')]
});
});
test('TC4: 메인 CTA 버튼 호버 상태', async ({ page }) => {
const ctaButton = page.locator('button#primary-cta');
await ctaButton.hover();
await expect(ctaButton).toHaveScreenshot('cta-button-hover.png');
});
test('TC5: 로그인 모달 레이아웃', async ({ page }) => {
await page.click('button#open-login-modal');
const modalDialog = page.locator('div[role="dialog"]');
await expect(modalDialog).toBeVisible();
await expect(modalDialog).toHaveScreenshot('login-modal.png');
});
});
`
Enregistrez les commandes dans package.json :
`json
{
"scripts": {
"test:visual": "playwright test",
"test:visual:update": "playwright test --update-snapshots"
}
}
`
Une simple ligne pnpm test:visual dans le terminal permet de détecter les layouts cassés sur les vues de bureau et mobiles en une minute. Vous pouvez ainsi vous débarrasser de la tâche fastidieuse consistant à ajuster manuellement la taille de l'écran en plissant les yeux.
En mettant en place une structure qui contrôle les saisies de l'IA par des fichiers de règles, bloque l'entrée de styles incorrects avec des hooks Git et vérifie les casses d'affichage avec Playwright, vous pouvez maintenir la cohérence du design même dans un projet de développeur solo.