HTML/CSS
投稿日:

「前のコーダーのCSS、怖くて触れないんですけど」─ 既存CSSを壊さずに修正する防御的コーディング

おはようございます!株式会社ファストコーディングの働くおかんです。

先日、子どもが積み木で大作を作っていたんです。そこに下の子が「ここにもう一個足したい」と言って1つ追加した瞬間、全体がガシャーンと崩壊。上の子は大泣き、下の子は呆然。「触らなきゃよかった」という顔をしていました。

CSSの保守でも、まさにこれと同じことが起きます。前任のコーダーが書いたCSSに修正を入れたら、まったく関係ないページのレイアウトが崩れた。「触らなきゃよかった」と思ったことがある方、多いのではないでしょうか。

!importantが連鎖していたり、なぜかmargin-top: -37pxのようなマジックナンバーが入っていたり、セレクタの詳細度が限界まで上がっていたり。既存CSSに手を入れるのが怖いのは、技術力の問題ではなく、構造の問題です。

今回は、既存CSSを壊さずに安全に修正するための「防御的コーディング」のテクニックを4つ紹介します。

まず影響範囲を調べる:DevToolsのCoverage機能

修正を入れる前に、そのCSSがどこで使われているかを調べることが最優先です。闇雲に書き換えると積み木と同じ結末になります。

Chrome DevToolsにはCoverage(カバレッジ)という機能があります。CSSファイルの中で「実際に使われている行」と「使われていない行」を可視化してくれます。

使い方は以下の通りです。

  1. DevToolsを開く(F12)
  2. Ctrl+Shift+P(Mac: Cmd+Shift+P)でコマンドパレットを開く
  3. 「Coverage」と入力して「Show Coverage」を選択
  4. リロードボタンをクリックしてページを読み込む
  5. CSSファイルをクリックすると、行ごとに赤(未使用)/青(使用中)で色分けされる
Coverage結果の見方:
├─ 青い行 → このページで使われているCSS
├─ 赤い行 → このページでは使われていないCSS
└─ 注意:他のページで使われている可能性がある

ある案件で、3000行あるCSSファイルのうち実際にそのページで使われていたのは400行程度だったことがあります。修正対象のセレクタが赤(未使用)だったら、そのページでは影響がないということ。逆に青で表示されていたら、変更の影響をしっかり確認する必要があります。

ただし、赤だから削除していいわけではありません。他のページで使われている可能性があるので、主要なページをいくつか回ってから判断してください。

テクニック1:@layerで優先度を整理する

既存CSSの怖いところは、詳細度(specificity)が予測できないことです。.header .nav ul li aのような長いセレクタがあると、それを上書きするにはさらに長いセレクタか!importantが必要になります。この!importantがまた別の!importantを呼び、連鎖が止まらなくなる。

@layerを使うと、CSSの優先度をレイヤー単位で管理できます。

/* 既存CSSをlegacyレイヤーに閉じ込める */
@layer legacy {
  .header .nav ul li a {
    color: #333;
    font-size: 14px;
    text-decoration: none;
  }
  
  .header .nav ul li a:hover {
    color: #000;
  }
}

/* 新しいスタイルはレイヤーの外(unlayered)に書く */
/* レイヤー外の通常スタイルは、レイヤー内の通常スタイルより常に優先される */
.nav-link {
  color: #1a1a2e;
  font-size: 0.875rem;
  text-decoration: none;
  transition: color 0.2s ease;
}

.nav-link:hover {
  color: #3b82f6;
  /* セレクタの詳細度に関係なく、レイヤー外が優先 */
}

ポイントは、レイヤー内の通常スタイルは、レイヤー外の通常スタイルより常に優先度が低いことです。つまり、既存CSSを@layer legacyに入れてしまえば、レイヤー外に書いた新しいスタイルが自動的に優先されます。.header .nav ul li aのような詳細度の高いセレクタでも、レイヤーに入っていれば.nav-linkのような短いセレクタで上書きできるんです。

ただし注意点があります。!importantが付いたスタイルには、この仕組みは効きません!important宣言はレイヤーの優先順位が逆転する仕様になっていて、レイヤー内の!importantはレイヤー外の!importantより強くなります。既存CSSに!importantが多い場合は、@layerだけでは解決できないので、BEMクラスの追加(テクニック2)や@scope(テクニック3)と組み合わせる必要があります。

夜、子どもが寝た後にこの方法を試したときは感動しました。詳細度バトルで苦しんでいたセレクタが、レイヤーに入れるだけで短いクラス名で上書きできるようになったんです。

ただし注意点があります。@layerはChrome 99+、Firefox 97+、Safari 15.4+で対応しています。IE対応が必要な案件では使えませんが、2026年現在、IEを考慮する案件はほぼないと思います。

テクニック2:BEMの後付け導入で影響範囲を限定する

既存CSSがグローバルなセレクタ(.title.btn.text)で書かれていると、同名のクラスが複数ページで使われていて影響が読めません。

BEM(Block Element Modifier)を後付けで導入すると、新しいスタイルの影響範囲を確実に限定できます。

/* 既存CSS:グローバルな.btnがどこで使われているか不明 */
.btn {
  padding: 8px 16px;
  background: #007bff;
  color: #fff;
  border: none;
}

/* 修正したいが、.btnを直接変更すると他ページに影響する */

/* 防御的アプローチ:BEM命名で新しいクラスを追加 */
.contact-form__submit {
  padding: 12px 24px;
  background: #2563eb;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 1rem;
  transition: background-color 0.15s ease;
}

.contact-form__submit:hover {
  background: #1d4ed8;
}

.contact-form__submit--disabled {
  background: #9ca3af;
  cursor: not-allowed;
}
<!-- 既存のHTMLはそのまま残す -->
<button class="btn contact-form__submit">送信する</button>

<!-- 既存の.btnスタイルはそのまま適用されるが、
     .contact-form__submitのスタイルが後から適用される -->

既存の.btnクラスはHTMLから外さず、新しいBEMクラスを追加するのがポイントです。既存のクラスを外すと他のJavaScriptやテストが壊れるリスクがあるので、追加だけにとどめます。新しいクラスが既存スタイルを上書きするため、見た目は意図通りに変わります。

時間に余裕があるときに、ページ単位で既存クラスを新しいBEMクラスに置き換えていけば、段階的にCSSをきれいにできます。

テクニック3:@scopeで修正範囲を物理的に限定する

@scopeを使うと、スタイルの適用範囲を特定のDOM要素内に限定できます。既存CSSに影響を与えずに、特定のセクションだけスタイルを修正したいときに便利です。

/* @scopeで.contact-sectionの中だけに適用 */
@scope (.contact-section) {
  h2 {
    font-size: 1.5rem;
    color: #111827;
    margin-bottom: 1rem;
  }

  p {
    font-size: 1rem;
    line-height: 1.8;
    color: #4b5563;
  }

  .form-group {
    margin-bottom: 1.5rem;
  }

  input[type="text"],
  input[type="email"] {
    width: 100%;
    padding: 0.75rem 1rem;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 1rem;
  }

  input:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
  }
}

@scopeの中でh2pのような要素セレクタを使っても、.contact-sectionの外には一切影響しません。既存CSSのh2スタイルを壊す心配がないんです。

さらに、toキーワードで下限を指定することもできます。

/* .contact-sectionの中、ただし.legacy-widgetの中は除外 */
@scope (.contact-section) to (.legacy-widget) {
  p {
    line-height: 1.8;
    color: #4b5563;
  }
}

これは「.contact-sectionの中にある.legacy-widget自体およびその子孫には適用しない」という意味です。既存のウィジェットやサードパーティのコンポーネントを壊さずに、周囲だけスタイルを変更できます。

@scopeはChrome 118+、Edge 118+、Safari 17.4+で対応しています。Firefoxは146+からの対応で、比較的新しい機能です。2026年現在、主要ブラウザではほぼ使える状態になっています。

テクニック4:CSS Custom Propertiesで値を差し替え可能にする

既存CSSのマジックナンバー(margin-top: -37pxwidth: 847pxなど)を直接書き換えるのは危険です。その値には何らかの理由があった可能性があります。

CSS Custom Properties(CSS変数)を使って、値を差し替え可能な状態にするのが安全です。

/* 既存CSS:マジックナンバーが散在 */
.hero-section {
  padding-top: 87px;    /* なぜ87px? */
  margin-bottom: -23px; /* なぜ-23px? */
}

.sidebar {
  width: 283px;         /* なぜ283px? */
  margin-top: 87px;     /* hero-sectionと同じ87px */
}

/* 防御的アプローチ:CSS変数に置き換え */
:root {
  --header-height: 87px;       /* ヘッダーの高さ分の余白 */
  --section-overlap: -23px;    /* セクション間のネガティブマージン */
  --sidebar-width: 283px;      /* サイドバー幅(コンテンツ253px + padding 30px) */
}

.hero-section {
  padding-top: var(--header-height);
  margin-bottom: var(--section-overlap);
}

.sidebar {
  width: var(--sidebar-width);
  margin-top: var(--header-height);
}

変数名を付ける作業自体が、マジックナンバーの意味を解読する作業になります。87pxが「ヘッダーの高さ分のオフセット」だとわかれば、ヘッダーの高さが変わったときに--header-heightの値を1箇所変更するだけで済みます。

もう1つのメリットは、変更の影響範囲がDevToolsで一目でわかることです。DevToolsでCSS変数を検索すれば、その変数を使っているすべての箇所が確認できます。

防御的コーディングの手順まとめ

実際の案件では、以下の順番で進めています。

  1. Coverage機能で使用状況を調査する。修正対象のセレクタがどのページで使われているか把握する
  2. 既存CSSには手を入れず、新しいクラスを追加する。BEM命名で影響範囲を限定する
  3. @layerで優先度を整理する。既存CSSをlegacyレイヤーに入れて、新しいスタイルをレイヤー外に配置する
  4. マジックナンバーをCSS変数に置き換える。変数名で意図を記録する
  5. @scopeで局所的な修正を行う。他のページへの影響を物理的に遮断する

ポイントは「既存CSSを消さない」ことです。消すと何が壊れるかわからない。追加と上書きで対処し、時間があるときに段階的に整理していくのが、実務では現実的です。

まとめ

今回は、既存CSSを壊さずに修正する防御的コーディングのテクニックを紹介しました。

  • DevToolsのCoverage機能で、修正対象のCSSがどこで使われているか事前に調査する
  • @layerで既存CSSをレイヤーに閉じ込め、レイヤー外の新しいスタイルで詳細度に関係なく上書きする
  • BEMの後付け導入で、新しいクラスの影響範囲を命名規則で限定する
  • @scopeでスタイルの適用範囲をDOM要素内に物理的に限定する
  • CSS Custom Propertiesでマジックナンバーに名前を付け、変更を1箇所で管理する

「前任者のCSSが怖い」のは、影響範囲が見えないからです。影響範囲を可視化し、新しいスタイルの適用範囲を限定する。これだけで「触ったら壊れる」恐怖はかなり軽減されます。ある案件でこのアプローチを導入してから、CSS修正での手戻りがほぼゼロになりました。

株式会社ファストコーディングでは、既存サイトのCSS改善やリファクタリングもお手伝いしています。「レガシーCSSの保守が大変」「CSSの品質を上げたい」という方は、お問い合わせフォームからお気軽にご連絡ください。