TuBrief
Subscribed Channels
Videos
Community

AIで作ったランディングページがすべて同じに見える理由と解決策

TuBrief Editorial
August 22, 2026
0
Computing/Software

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

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

Related Video

Claudeで「AI製に見えない」完璧なサイトを構築する方法:Impeccable活用術7:58

Claudeで「AI製に見えない」完璧なサイトを構築する方法:Impeccable活用術

Better Stack

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

AIで作ったランディングページがすべて同じに見える理由と解決策

企画からデプロイまで1人でこなす個人開発者にとって、CursorやClaude Codeは救世主のような存在です。プロンプトを数行打つだけで、10分ほどでそれらしいウェブページが完成します。

問題はその次です。デプロイボタンを押して画面を見ると、既視感を覚えます。紫色のグラデーションボタン、Interフォント、真ん中に配置された3カラムのカードレイアウト。どこかで見たようなテンプレートの匂いがプンプン漂います。見た目は綺麗なのに、ユーザーの決済や登録につながりません。人々は工場で量産されたようなページを鋭く見抜き、ページを閉じてしまいます。

LLMは統計的平均に基づいて動作します。ルールを与えなければ、ウェブ上で最も一般的に使用されているTailwindのデフォルトプリセットやShadcn UIのデフォルト値へと回帰します。「清潔感のある洗練されたランディングページを作って」というプロンプトを投げた瞬間、AIは w-[320px] や top-[117px] のような正体不明のインラインピクセル値を乱発し、レイアウトを崩壊させます。

この問題をプロンプトの修正だけで解決しようとすると疲弊します。コードを生成するたびにブランドルールを強制し、違反したコードをコミット段階で物理的にブロックするパイプラインを構築する必要があります。

AIエージェントの手足を縛るルールファイルの作成

Cursorは .cursor/rules/*.mdc、Claude Codeは CLAUDE.md、オープンソースツールは AGENTS.md を読み込みます。このファイルにブランドのデザインデザイントークンを定義しておけば、AIがコード生成の段階から勝手なスタイルを使用するのを防ぐことができます。

ルールファイルが500行を超えると、セッションごとに読み込むコンテキストのコストが増大し、モデルの指示遵守率も低下します。200〜300行前後に明確にまとめる必要があります。

プロジェクトのルートに .cursor/rules/design-system.mdc を作成し、以下の内容を追加します。

`yaml

description: Design System Rules and Custom Token Enforcement
globs: ["src/app//*.tsx", "src/components//.tsx", "src/styles/**/.css"]
alwaysApply: false

Brand Design System Constraints

Universal Rules

  • MUST NOT use arbitrary Tailwind utility classes such as bg-[#123456] or h-[117px].
  • MUST use predefined semantic Design Tokens for colors, spacing, and typography.
  • MUST run pnpm lint:style to verify token compliance before completing tasks.

Design Token Reference Map

Color Tokens

  • Surface Background: var(--color-bg-primary) (Tailwind: bg-brand-primary)
  • Surface Secondary: var(--color-bg-secondary) (Tailwind: bg-brand-secondary)
  • Text Main: var(--color-text-main) (Tailwind: text-brand-main)
  • Text Muted: var(--color-text-muted) (Tailwind: text-brand-muted)
  • Accent Primary: var(--color-accent-default) (Tailwind: bg-brand-accent)

Spacing Scale (8pt Grid Standard)

  • 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)

Typography Rules

  • Main Heading (H1): Class text-brand-h1 -> Font: Inter, Weight: 700, Size: 2.5rem, Tracking: -0.02em
  • Body Text: Class text-brand-body -> Font: Inter, Weight: 400, Size: 1.0rem, Leading: 1.5

`

このルールを配置しておくと、AIがブラケットの任意の値やHexコードを直接埋め込む頻度が劇的に減少します。

StylelintとHuskyによるハードコードされたスタイルのコミット阻止

プロンプトだけでは不十分です。AIは時々ルールを無視して、こっそりインラインピクセル値を混ぜ込んできます。StylelintとGit Pre-commitフックを連携させ、ハードコードされたスタイルがリポジトリに侵入する経路を遮断します。

ツールをインストールし、Huskyを初期化します。

`bash
pnpm add -D husky lint-staged stylelint stylelint-declaration-strict-value
npx husky init

`

プロジェクトのルートに stylelint.config.mjs を作成します。任意のカラーコードやピクセル値が混入した場合にビルドエラーを発生させる設定です。

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

`

package.json に lint-staged の設定を追加します。

`json
{
"lint-staged": {
"*.{css,scss,tsx,jsx}": [
"stylelint --fix",
"eslint --max-warnings=0"
]
}
}

`

.husky/pre-commit ファイルに以下の1行を登録します。

`bash
npx lint-staged

`

これで git commit を実行するたびに、ステージングされたコードの静的解析が行われます。AIが勝手に追加した margin: 17px のようなコードがあれば、コミット自体が失敗します。スタイルの修正にかけていた時間を週に約5時間ほど節約できるようになります。

Playwrightを活用したビジュアルリグレッションテストの自動化

静的解析はテキストのルールしかチェックしません。要素が重なったり、モバイル画面でメニューが崩れたりする問題は、ブラウザを実際に起動して確認する必要があります。毎回手動でビューポートを縮小・拡大することはできないため、Playwrightを用いたスナップショット比較を自動化します。

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

`

主要なUI 5箇所を検証するテストスクリプト (tests/visual/landing-page.spec.ts) を作成します。

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

`

package.json にコマンドを登録します。

`json
{
"scripts": {
"test:visual": "playwright test",
"test:visual:update": "playwright test --update-snapshots"
}
}

`

ターミナルで pnpm test:visual を1回実行するだけで、デスクトップとモバイルのビューポートにおけるレイアウト崩れを1分で検出できます。画面サイズを一つずつ手動で調整しながら目を凝らしてチェックしていた作業を排除することが可能です。

ルールファイルでAIの入力を統制し、Gitフックで不正なスタイルの流入を防ぎ、Playwrightで画面崩れを確認する構造を整えることで、1人の個人開発プロジェクトでもデザインの一貫性を維持することができます。