Tips & Tricks (更新: 2026/6/6)

Claude CodeでCSSが崩れる原因と、崩れない設計の進め方

Claude CodeでCSSを直すと別ページが崩れる——その原因と直し方を、僕の失敗込みで解説。レイヤー設計で優先順位を固定し、よくある崩れを順に潰す進め方。

Claude CodeでCSSが崩れる原因と、崩れない設計の進め方

「このボタンの色だけ直して」とClaude Codeに頼んだら、ボタンは直りました。

その代わり、まったく関係ないはずの記事ページの見出しがずれていました。プレビューでは気づかず、デプロイして数時間後に「なんか表示崩れてない?」と連絡が来て、はじめて気づいたんです。

CSSって、こういう「直したつもりが別の場所を壊す」が一番こわい。今日はその原因と、崩さずに直すための進め方を、僕の失敗込みで書きます。

この記事の要点

  • Claude CodeのCSS崩れの大半は「能力不足」ではなく優先順位(カスケード)が読めていないことが原因。だから設計の棚を先に渡す。
  • 最初に作るのは整ったCSSではなく足場。対象ファイル・触ってよい範囲・禁止事項・検証コマンドを先に固定する。
  • @layer(カスケードレイヤー)でどのCSSが勝つかを名前で固定すると、!importantの殴り合いが止まり、別ページの崩れが激減する。
  • よくある崩れ(!important地獄、ダークモードの読めない文字、画面幅だけ見て配置場所を見ない)は型が決まっているので、順に潰せる。
  • 細かい技(変数・Grid・Flexbox・アニメ)は専用記事へ。この記事は全体の進め方に絞る。

CSSはClaude Codeに任せやすく見えて、本番では「見た目が近い」だけでは出せません。なぜなら、見た目が合っていても優先順位の理由が誰にも追えない状態だと、次の修正で必ず崩れるからです。逆に言うと、優先順位さえ固定できれば、Claude Codeに任せられる範囲は一気に広がります。

最初にひとつだけ用語を言い換えておきます。カスケードレイヤー(@layer) は「CSSの優先順位を棚に分ける仕組み」です。resetの棚、componentsの棚、overridesの棚……と分けておくと、同じ要素に複数のCSSが当たっても「どの棚が勝つか」が名前で決まります。これをClaude Codeに先に渡すかどうかで、出てくる差分の安全性がまるで変わります。

なぜClaude CodeのCSSは「別の場所」を壊すのか

理由はモデルの賢さではありません。カスケード——同じ要素に複数のCSSが当たったとき、どれを採用するかのルール——が、プロジェクト全体で曖昧だからです。

既存サイトのCSSは、たいてい層が混ざっています。reset.cssがあって、global.cssがあって、コンポーネントCSSがあって、Tailwindのユーティリティがあって、CMS由来の本文スタイルもある。この状態で「いい感じに直して」と頼むと、Claude Codeは目の前の見た目を合わせにいきます。手段として一番手っ取り早いのが、詳細度を上げることと!importantを足すこと。これで局所的には直りますが、全体の優先順位がさらに歪むので、次の修正がもっと難しくなる。これが「直すたびに別の場所が崩れる」の正体です。

だから順番を逆にします。整ったCSSを書かせる前に、設計の棚(足場)を読ませる。冒頭の事故も、足場がなかったから起きました。

まず足場を作る:いきなり書かせない

Claude Codeに最初に渡すのは「直して」ではなく「調べて、まだ直すな」です。これはハーネスエンジニアリング入門で書いた考え方と同じで、作業範囲を先に狭く固定するほど事故が減ります。

AGENTS.md、CLAUDE.md、package.json、src/styles 配下を全部読んで。
まだ編集はしないで。
現状を報告して: CSS設計、命名規則、トークンの使われ方、
ダークモード戦略、レスポンシブのブレークポイント、テストコマンド。
そのうえで、料金カードとCTAボタンだけを直す最小の安全な計画を出して。

この一手で、Claude Codeがいきなり新設計を持ち込む事故が減ります。人間が「今回はカードとボタンだけ」と境界を決め、Claude Codeがその範囲だけ書く。地味ですが、これが現実的な役割分担です。

全体の流れはこの順で進めます。

ステップ人間が固定することClaude Codeに任せること
1. 調査対象ファイルと禁止事項現状CSSの棚卸し報告
2. レイヤー@layerの並び順既存CSSを棚に振り分け
3. トークン変数名(色・余白)既存トークンで組み立て
4. コンポーネントクラス命名の方針カード/ボタンの実装
5. レスポンシブ確認する幅(320〜1440px)各幅の崩れ修正
6. 検証合格条件スクショ比較とレポート

上から順に固定していくと、Claude Codeの差分が「カードとボタン」に収まります。逆に2〜6を飛ばして「全部きれいにして」と頼むと、色の直書きと!importantが増えて、また崩れます。

カスケードレイヤーで「どれが勝つか」を固定する

崩れない設計の背骨が@layerです。昔は詳細度を上げたり読み込み順に頼ったりしていましたが、本番ではそれだと壊れやすい。棚を分けて、緊急回避以外でoverridesを増やさない運用にします。

次のapp.cssは、そのままコピーして小さく検証できる構成です。tokensbasecomponentsutilitiesoverrides の順に責務を分けています。

/* src/styles/app.css */
/* 棚の並び順を最初に宣言する。後ろの棚ほど強い */
@layer reset, tokens, base, components, utilities, overrides;

@import "./tokens.css" layer(tokens);
@import "./base.css" layer(base);
@import "./components.css" layer(components);
@import "./utilities.css" layer(utilities);

@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  body,
  h1,
  h2,
  h3,
  p {
    margin: 0;
  }
}

@layer overrides {
  /* 緊急回避はここだけ。なぜ必要かをコメントに残す */
  .legacy-markdown :where(table, pre) {
    max-width: 100%;
  }
}

Claude Codeへの依頼は、コード生成より「分類」から始めると安定します。

src/styles/app.css のレイヤーモデルに、既存のグローバルCSSを移して。
テンプレートが使っているクラス名は変えないで。
棚は reset, tokens, base, components, utilities, overrides だけを使う。
overrides に入れるルールがあれば、その理由を最後にまとめて。
編集後に npm test と視覚チェックのコマンドを実行して。

落とし穴は、@layerを入れたのに既存CSSの読み込み順を壊すことです。@importはCSSの先頭付近に置く必要があります。また、レイヤー外の通常ルールはレイヤー内のルールより強くなりやすいので、移行中に「一部だけレイヤー化」すると逆に読みにくくなります。まず新規コンポーネントだけで始め、効果を確認してから既存CSSに広げるのが安全です。優先順位の詳しい仕様はMDNの@layerで原文を確認してください。

なお、棚の中身である色や余白の変数(デザイントークン)の設計は、それだけで一記事になる話なのでCSS変数の実務投入ガイドに分けています。この記事では「トークンはcomponentsの手前の棚に置く」という配置だけ押さえれば十分です。

カードとボタンを「閉じた部品」にする

棚を決めたら、実際のUIを部品にします。カードとボタンはブログでもSaaSでも管理画面でも出てくる定番ですが、ここをグローバルな.title.buttonで作ると、後から別ページを壊します。責務が見える名前にして、componentsの棚に閉じ込めます。

/* src/styles/components.css */
@layer components {
  .ui-card {
    /* 親要素の幅で見た目を切り替えられるようにしておく */
    container: card / inline-size;
    display: grid;
    gap: var(--space-4);
    padding: var(--space-6);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-card);
  }

  .ui-card__title {
    max-width: 18ch;
    font-size: clamp(1.5rem, 1rem + 2cqi, 2.25rem);
    line-height: 1.1;
  }

  .ui-card__body {
    color: var(--color-muted);
    line-height: 1.7;
  }

  .ui-button {
    display: inline-flex;
    min-height: 2.75rem;
    align-items: center;
    justify-content: center;
    padding: 0.75rem 1rem;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: var(--color-accent);
    color: var(--color-surface);
    font: inherit;
    font-weight: 700;
    text-decoration: none;
  }

  .ui-button:hover {
    background: var(--color-accent-strong);
  }

  .ui-button:focus-visible {
    /* キーボード操作の人にフォーカス位置を必ず見せる */
    outline: 3px solid var(--color-focus);
    outline-offset: 3px;
  }
}

検証用の最小HTMLも一緒に置きます。Astro、Next.js、Viteのどれでも、/style-labのようなプレビュー画面にこの形を置くと、後でスクショ比較がしやすくなります。

<main class="style-lab">
  <article class="ui-card" data-testid="pricing-card">
    <p class="ui-card__eyebrow">Team plan</p>
    <h1 class="ui-card__title">Claude CodeでCSSを崩さず出す</h1>
    <p class="ui-card__body">
      棚を分け、変数を再利用し、ダークモードを確認し、公開前に見た目の崩れを捕まえる。
    </p>
    <div class="ui-actions">
      <a class="ui-button" href="/training/">研修から始める</a>
      <a class="ui-button" href="/thanks/">無料チェックリスト</a>
    </div>
  </article>
</main>

ポイントは、data-testidを本番の文言から切り離すことです。見た目のテストは、文言が変わっても対象を見つけられる方が安定します。ただしアクセシビリティのテストではgetByRoleを優先し、実際のユーザーが操作する名前で確認します。

部品の中身、つまり横並びの組み方はFlexbox、面の組み方はGridという使い分けは、それぞれFlexboxの実務パターンCSS Gridを実務レベルにする方法に詳しく書きました。この記事では「部品はcomponentsの棚に閉じる」という設計判断だけ押さえます。

よくある崩れと、その直し方

ここからが本題です。Claude CodeのCSS崩れは、だいたい型が決まっています。僕が何度も踏んだものを、原因と直し方で並べます。

症状本当の原因直し方
直すたびに!importantが増える優先順位が棚で決まっていない@layerで棚を分け、overrides以外!important禁止
ダークモードで補助文字が読めない色だけ変えてコントラスト未確認背景/本文/補助/リンク/フォーカスを全部確認
サイドバー内だけカードが崩れる画面幅で判断し配置場所を見ていない親幅で切り替えるコンテナクエリを使う
スクショ差分が毎回失敗するアニメや外部フォントが固定されていないprefers-reduced-motionと固定データで安定させる
別ページが連鎖で崩れるグローバルな.title等で作っている責務の見える名前で部品に閉じる

一番多いのが!importantです。短期的には直って見えますが、次の修正でさらに強い指定が要る。だからClaude Codeには最初から「!importantを追加しない。必要に見える場合は原因のセレクタを説明して」と明記します。

二番目に多いのが、画面幅だけで判断する崩れです。カードがサイドバー内に置かれると、画面は広くてもカード自身は狭い。ここはメディアクエリ(画面全体の幅)ではなく、コンテナクエリ(親要素の幅)の出番です。次のように、同じ.ui-cardが置かれた場所の幅で見た目を切り替えます。

/* src/styles/responsive.css */
@layer components {
  /* 親(.ui-card)の幅が 36rem 以上になったときだけ横並びにする */
  @container card (min-width: 36rem) {
    .ui-card {
      grid-template-columns: 1fr auto;
      align-items: center;
    }

    .ui-actions {
      justify-content: end;
    }
  }

  /* 画面そのものが狭いときはボタンを全幅に */
  @media (max-width: 40rem) {
    .ui-actions,
    .ui-button {
      width: 100%;
    }
  }

  /* 動きが苦手な人のために、アニメを実質止める */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      transition-duration: 0.001ms !important;
      animation-duration: 0.001ms !important;
    }
  }
}

Claude Codeにレスポンシブ修正を頼むときは、幅を具体的に書きます。「スマホ対応して」ではなく「320px、375px、768px、1024px、1440pxで、横スクロール・折り返し・フォーカスリング・CTAの高さを確認して」。これだけで、見た目だけの修正から検証付きの差分に変わります。アニメーションを足す場面の崩れと止め方はCSSアニメーション上級実装に分けています。

公開前にスクショで崩れを捕まえる

最後の確認を人間の目だけに頼ると、必ず漏れます。冒頭の事故がまさにそれでした。Playwrightのスクリーンショット比較を入れておくと、カードの幅、ボタンの折り返し、ダークモード、フォーカス状態を機械が見張ってくれます。

// tests/visual/style-regression.spec.ts
import { expect, test } from "@playwright/test";

const viewports = [
  { name: "mobile", width: 375, height: 812 },
  { name: "tablet", width: 768, height: 1024 },
  { name: "desktop", width: 1440, height: 900 },
];

for (const viewport of viewports) {
  test(`料金カードの見た目 ${viewport.name}`, async ({ page }) => {
    await page.setViewportSize({ width: viewport.width, height: viewport.height });
    // 揺れの原因(モーション)を止めてから撮る
    await page.emulateMedia({ colorScheme: "light", reducedMotion: "reduce" });
    await page.goto("/style-lab");

    const card = page.getByTestId("pricing-card").first();
    await expect(card).toBeVisible();
    await expect(card).toHaveScreenshot(`pricing-card-${viewport.name}-light.png`, {
      animations: "disabled",
      maxDiffPixelRatio: 0.02,
    });
  });
}

初回は基準画像を作り、以降は差分だけ見ます。

npx playwright test tests/visual/style-regression.spec.ts --update-snapshots
npx playwright test tests/visual/style-regression.spec.ts

Claude Codeへのレビュー指示も固定文にします。曖昧に頼むと曖昧なレビューが返るので、見る項目を全部書きます。

CSSの差分を批判的にレビューして。
カスケードレイヤー、トークンの使用、セレクタの詳細度、ダークモード、
コンテナクエリ、キーボードフォーカス、コントラスト、reduced motion、
Playwrightの視覚カバレッジを確認して。
具体的な問題だけ、ファイルパスと行番号つきで返して。

テスト全体の組み立て方はテスト戦略ガイド、エージェントへの渡し方のルールはCLAUDE.mdベストプラクティスに分けています。1回の依頼は「トークン追加だけ」「カード整理だけ」のように小さく切り、複数人や複数エージェントで同時編集するなら「このディレクトリだけ。未コミット変更は戻さない」と必ず明記してください。

よくある質問

Q. Claude CodeにCSS全体をまとめてきれいにさせてもいい? やめたほうがいいです。差分が大きいほど崩れの原因が特定できなくなります。トークン追加、カード整理、レスポンシブ修正、テスト追加、と小さく切って1回1テーマにします。

Q. @layerは古いブラウザで動く? モダンブラウザでは広く使えます。ただ、レイヤー外の通常ルールが混ざると優先順位が読みにくくなるので、対応範囲よりも「全部レイヤーに入れるか、入れないか」を揃えるほうが事故が減ります。

Q. TailwindとカスケードレイヤーやCSS Modulesは併用できる? できます。Tailwindのユーティリティをutilitiesの棚に置き、コンポーネントCSSをcomponentsに置くと優先順位がはっきりします。Tailwind寄りの崩れ対策はTailwind CSS実践Tipsにまとめています。

Q. ダークモードで文字が薄くて読めない、と言われる。 色だけ変えてコントラストを確認していないのが原因です。背景・本文・補助テキスト・リンク・フォーカスリングの組み合わせを全部見ます。補助テキストを薄くしすぎないのがコツです。

Q. スクショ差分テストが毎回落ちて、すぐ無視されてしまう。 小さな揺れが原因です。prefers-reduced-motionでアニメを止め、外部フォントと表示データを固定し、安定したテストページを用意してから基準画像を撮ります。

実際に試した結果

冒頭の「ボタンを直したら見出しが崩れた」事故のあと、僕がやめたのは「Claude Codeを信じるか」で悩むことでした。代わりにやったのは、優先順位を棚で固定することと、公開前に機械の門番を置くことです。

実際にいちばん効いたのは、じつは@layerそのものではなく、作業前の調査プロンプトでした。最初に既存CSS・トークン・検証コマンドを読ませた回は、Claude Codeの差分がカードとボタンに収まりました。逆に「見た目を整えて」だけだと、色の直書き、重複した余白、テストなしの差分が毎回増えました。

CSSはClaude Codeで速く書けます。でも本番品質にする決め手は、設計の棚・変数名・検証コマンドを人間が先に固定することでした。崩れない設計を先に置けば、あとは安心して任せられます。

チームでこの足場を一気に整えたい人向けに、設計テンプレとレビュー手順を教材一覧にまとめています。導入を相談したい場合は研修・導入相談もどうぞ。

#Claude Code #CSS #CSS設計 #カスケードレイヤー #フロントエンド
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。