はじめに

CSSでスタイルを記述していると、「なぜか思い通りのスタイルが適用されない」「後から書いたはずのスタイルが無視される」といった問題に直面することがあります。これらの問題の多くは、CSSの詳細度(Specificity)とカスケードの仕組みを理解することで解決できます。

本記事では、CSSのスタイル優先順位を決定する仕組みについて、以下の内容を解説します。

  • カスケードの仕組みとスタイルが適用される順序
  • 詳細度の計算方法と3つの列(ID・CLASS・TYPE)
  • !important の扱いと使用時の注意点
  • スタイルの優先順位を決める3つの要素(詳細度・順序・継承)
  • 詳細度に関する問題を解決するためのベストプラクティス

前提条件

本記事を読み進めるにあたり、以下の知識があることを前提としています。

  • HTMLの基本的なタグ構造
  • CSSの基本構文(セレクタ・プロパティ・値の関係)
  • CSSセレクタ(要素・クラス・ID・属性セレクタ)の基本的な使い方

動作確認環境

  • Google Chrome 131以降
  • Firefox 133以降
  • Safari 18以降
  • Microsoft Edge 131以降

CSSカスケードとは

カスケード(Cascade)とは、CSSにおいて「複数のスタイル定義が同じ要素に適用される場合に、どのスタイルを優先するか」を決定するアルゴリズムです。「Cascading Style Sheets」という名前が示すとおり、カスケードはCSSの中核をなす概念です。

MDN Web Docsでは、カスケードを次のように説明しています。

カスケードは、異なるソースから来るプロパティ値を組み合わせる方法を定義するアルゴリズムです。

カスケードの評価順序

カスケードアルゴリズムは、以下の順序でスタイルを評価し、最終的に適用される値を決定します。

flowchart TD
    A[1. 関連性<br/>セレクタが要素に一致するか] --> B[2. オリジンと重要度<br/>スタイルシートの種類と!important]
    B --> C[3. 詳細度<br/>セレクタの重み付け]
    C --> D[4. スコープ近接性<br/>@scopeブロック内の距離]
    D --> E[5. 出現順序<br/>後に宣言されたスタイルが優先]

各ステップの詳細は以下のとおりです。

  1. 関連性: セレクタが対象の要素に一致するかを確認します
  2. オリジンと重要度: スタイルシートの種類(ユーザーエージェント・ユーザー・作成者)と!importantの有無を評価します
  3. 詳細度: セレクタの重み付けを計算し、より高い詳細度を持つスタイルが優先されます
  4. スコープ近接性: @scopeブロック内でのルートまでの距離を評価します
  5. 出現順序: 上記がすべて同じ場合、後に宣言されたスタイルが適用されます

オリジンの種類と優先順位

CSSには3種類のスタイルシートのオリジン(出所)があり、それぞれ優先順位が異なります。

順位 オリジン 説明
1 ユーザーエージェント ブラウザが提供するデフォルトスタイル
2 ユーザー ユーザーが設定したカスタムスタイル
3 作成者 Web開発者が記述したスタイル

通常のスタイルでは、作成者スタイルが最も高い優先順位を持ちます。ただし、!importantが付与されている場合は、この優先順位が逆転します。

1
2
3
4
5
6
7
8
9
/* ユーザーエージェントスタイル(ブラウザデフォルト) */
p {
  margin: 1em 0;
}

/* 作成者スタイル(開発者が記述)- こちらが優先される */
p {
  margin: 0;
}

CSS詳細度とは

詳細度(Specificity)とは、ある要素に適用されるCSSスタイルを決定するためにブラウザが使用するアルゴリズムです。同じオリジンで複数のセレクタが同じ要素に一致する場合、詳細度が高いセレクタのスタイルが優先されます。

詳細度の3つの列

詳細度は、3つの列(ID列・CLASS列・TYPE列)で構成される値として表現されます。

graph LR
    subgraph 詳細度の計算
        A[ID列<br/>1-0-0] --- B[CLASS列<br/>0-1-0] --- C[TYPE列<br/>0-0-1]
    end

各列の内容は以下のとおりです。

重み 含まれるセレクタ
ID列 1-0-0 IDセレクタ(#example
CLASS列 0-1-0 クラスセレクタ(.class)、属性セレクタ([type="text"])、擬似クラス(:hover:focusなど)
TYPE列 0-0-1 要素型セレクタ(pdivなど)、擬似要素(::before::afterなど)

詳細度に影響しない要素

以下の要素は詳細度の計算には含まれません。

  • 全称セレクタ(*: 詳細度は 0-0-0
  • 結合子(>+~ : セレクタ間の関係を示すだけで、詳細度には影響しません
  • :where() 擬似クラス: 常に詳細度 0-0-0 として扱われます
  • 否定擬似クラス(:not())自体: ただし、引数内のセレクタは詳細度に加算されます

詳細度の計算方法

基本的な計算例

詳細度は、セレクタに含まれる各要素を列ごとにカウントして計算します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 詳細度: 0-0-1(TYPE列に1) */
p {
  color: black;
}

/* 詳細度: 0-1-0(CLASS列に1) */
.text {
  color: blue;
}

/* 詳細度: 1-0-0(ID列に1) */
#content {
  color: red;
}

複合セレクタの計算例

複数のセレクタを組み合わせた場合、各列の値を合計します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 詳細度: 0-0-2(要素セレクタ2つ) */
div p {
  color: black;
}

/* 詳細度: 0-1-1(クラス1つ + 要素1つ) */
div.container {
  color: blue;
}

/* 詳細度: 0-2-1(クラス2つ + 要素1つ) */
div.container.main {
  color: green;
}

/* 詳細度: 1-1-1(ID1つ + クラス1つ + 要素1つ) */
#header .nav a {
  color: red;
}

/* 詳細度: 1-2-1(ID1つ + 擬似クラス1つ + 属性セレクタ1つ + 要素1つ) */
#content input[type="text"]:focus {
  border-color: blue;
}

3つの列の比較方法

詳細度の比較は、左から右へ列ごとに行われます。左側の列で値が大きい方が常に優先されます。

1
2
3
4
5
6
7
8
9
/* 詳細度: 1-0-0(ID列が1) - 勝利 */
#myElement {
  color: green;
}

/* 詳細度: 0-4-0(CLASS列が4) */
.bodyClass .sectionClass .parentClass [id="myElement"] {
  color: yellow;
}

上記の例では、CLASS列に4つの成分があっても、ID列に1つの成分がある方が優先されます。これは、ID列の1は、CLASS列の値がいくつあっても上回るためです。

1
2
3
4
/* 詳細度の比較(左から右へ) */
#header          /* 1-0-0 */
.a.b.c.d.e.f.g   /* 0-7-0 */
/* → #header が勝利(ID列 > CLASS列) */

実践的な計算練習

以下のセレクタの詳細度を計算してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 問題1 */
ul li a {
  /* 答え: 0-0-3(要素3つ) */
}

/* 問題2 */
nav ul.menu li a:hover {
  /* 答え: 0-2-4(クラス1 + 擬似クラス1 + 要素4) */
}

/* 問題3 */
#sidebar .widget h3.title {
  /* 答え: 1-2-1(ID1 + クラス2 + 要素1) */
}

/* 問題4 */
body #content article.post p:first-child {
  /* 答え: 1-2-4(ID1 + クラス1 + 擬似クラス1 + 要素4) */
}

:is()、:not()、:has() の詳細度

:is():not():has() 擬似クラスは、それ自体は詳細度を持ちませんが、引数として渡されたセレクタの中で最も高い詳細度が適用されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 詳細度: 0-0-1(:is()自体は0、引数pの詳細度) */
:is(p) {
  color: black;
}

/* 詳細度: 1-0-1(:is()内の#fakeIdが最も高い) */
:is(p, #fakeId) {
  color: blue;
}

/* 詳細度: 0-1-2(:not()内の.innerが適用される) */
div:not(.inner) p {
  color: green;
}

:where() の特別な扱い

:where() 擬似クラスは、常に詳細度 0-0-0 として扱われます。これは、詳細度を上げずにセレクタをより具体的にしたい場合に有用です。

1
2
3
4
5
6
7
8
9
/* 詳細度: 0-0-1(:where()内のIDは無視される) */
:where(#defaultTheme) a {
  color: red;
}

/* 詳細度: 0-0-2(上記を簡単に上書きできる) */
footer a {
  color: blue;
}

インラインスタイルの詳細度

HTML要素のstyle属性で直接指定されたインラインスタイルは、通常のセレクタよりも高い詳細度を持ちます。概念的には 1-0-0-0 と表現できます。

1
2
<!-- インラインスタイルは最も高い詳細度を持つ -->
<p style="color: purple;">このテキストは紫色</p>
1
2
3
4
5
6
/* 詳細度: 1-0-0(IDセレクタ) */
#content {
  color: red;
}

/* 上記より詳細度が低いが、インラインスタイルには勝てない */

インラインスタイルを上書きできるのは、!important を使用した場合のみです。

!important の扱いと注意点

!important とは

!important フラグは、宣言に対して最高の優先度を与えます。詳細度やカスケードの通常のルールを上書きして、そのスタイルを強制的に適用します。

1
2
3
4
5
6
7
p {
  color: red !important;
}

#content p {
  color: blue; /* 詳細度は高いが、!importantには勝てない */
}

!important の優先順位

!important が付与されたスタイルは、通常のカスケード順序とは逆の優先順位を持ちます。

順位 オリジン + 重要度
1 作成者 - 通常
2 作成者 - !important
3 ユーザー - !important
4 ユーザーエージェント - !important

また、!important 同士が競合する場合は、通常どおり詳細度で比較されます。

1
2
3
4
5
6
7
8
9
/* 詳細度: 0-1-0 + !important */
.highlight {
  color: yellow !important;
}

/* 詳細度: 1-0-0 + !important - こちらが勝利 */
#special {
  color: red !important;
}

!important を使うべきでない理由

!important の使用は、以下の理由から避けるべきとされています。

  1. デバッグが困難になる: カスケードの自然な流れを壊すため、スタイルの追跡が難しくなります
  2. 保守性が低下する: !important を上書きするには、さらに !important が必要になり、悪循環に陥ります
  3. 再利用性が損なわれる: コンポーネントのスタイルを柔軟に変更できなくなります
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 悪い例: !importantの連鎖 */
.button {
  background: blue !important;
}

.button-danger {
  background: red !important; /* blueを上書きするために必要 */
}

.button-danger-large {
  background: darkred !important; /* さらに上書き... */
}

!important の適切な使用場面

以下のような限定的なケースでは、!important の使用が正当化される場合があります。

  1. サードパーティCSSの上書き: 変更できない外部CSSを上書きする必要がある場合
  2. ユーティリティクラス: 確実に適用されるべきヘルパークラス
  3. アクセシビリティ対応: ユーザースタイルシートでの使用
1
2
3
4
5
6
7
8
/* 適切な使用例: ユーティリティクラス */
.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
}

スタイルの優先順位を決める3つの要素

CSSのスタイル適用は、以下の3つの要素によって決定されます。

1. 詳細度(Specificity)

セレクタの重み付けに基づいて優先順位が決まります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 詳細度: 0-0-1 */
p {
  color: black;
}

/* 詳細度: 0-1-0 - こちらが優先 */
.text {
  color: blue;
}

/* 詳細度: 1-0-0 - 最も優先 */
#content {
  color: red;
}

2. 出現順序(Source Order)

詳細度が同じ場合、後に宣言されたスタイルが優先されます。

1
2
3
4
5
6
7
p {
  color: red;
}

p {
  color: blue; /* 後に宣言されているため、こちらが適用される */
}

3. 継承(Inheritance)

要素に直接適用されるスタイルは、親要素から継承されたスタイルよりも常に優先されます。

1
2
3
4
5
6
7
#parent {
  color: green; /* 継承される */
}

h1 {
  color: purple; /* 直接適用 - こちらが優先 */
}
1
2
3
<body id="parent">
  <h1>このテキストは紫色</h1>
</body>

継承の詳細度に関係なく、要素を直接対象とするセレクタが優先されます。

詳細度に関する問題を解決するベストプラクティス

詳細度を低く保つ

セレクタの詳細度は可能な限り低く保つことで、後からのスタイル変更が容易になります。

1
2
3
4
5
6
7
8
9
/* 悪い例: 詳細度が高すぎる */
body #main-content .article-wrapper .post-content p.intro {
  font-size: 1.2rem;
}

/* 良い例: 詳細度を低く保つ */
.post-intro {
  font-size: 1.2rem;
}

IDセレクタを属性セレクタに置き換える

詳細度を下げつつ要素を特定したい場合、IDセレクタを属性セレクタに置き換えることができます。

1
2
3
4
5
6
7
8
9
/* 詳細度: 1-0-1 */
#myContent h1 {
  color: green;
}

/* 詳細度: 0-1-1(より低い詳細度で同じ要素を選択) */
[id="myContent"] h1 {
  color: yellow;
}

:where() を活用する

詳細度を加えずにセレクタを具体的にしたい場合、:where() を使用します。

1
2
3
4
/* 詳細度: 0-0-1(ID部分は詳細度に影響しない) */
:where(#myContent) h1 {
  color: blue;
}

カスケードレイヤーを活用する

CSS @layer を使用すると、詳細度に依存せずにスタイルの優先順位を制御できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* 最初に宣言されたレイヤーは優先度が低い */
@layer base {
  p {
    color: black;
  }
}

/* 後に宣言されたレイヤーが優先される */
@layer theme {
  p {
    color: blue;
  }
}

/* レイヤー外のスタイルは全てのレイヤーより優先される */
p {
  color: red;
}

セレクタの重複による詳細度の調整

特定のケースで詳細度を上げる必要がある場合、セレクタを重複させることができます。

1
2
3
4
5
6
7
8
9
/* 詳細度: 0-2-0 */
.button.button {
  background: blue;
}

/* 詳細度: 0-3-0 */
.button.button.button {
  background: red;
}

ただし、この手法は可読性を損なうため、慎重に使用してください。

詳細度のデバッグ方法

ブラウザ開発者ツールの活用

ブラウザの開発者ツールを使用すると、要素に適用されているスタイルとその詳細度を確認できます。

  1. 対象の要素を右クリックして「検証」を選択
  2. 「Elements」パネルで要素を選択
  3. 「Styles」タブで適用されているスタイルを確認
  4. 取り消し線が引かれているスタイルは、より高い詳細度のスタイルによって上書きされています

詳細度計算ツール

オンラインの詳細度計算ツールを使用すると、セレクタの詳細度を素早く確認できます。

  • Specificity Calculator: セレクタを入力すると詳細度を計算
  • SpeciFISHity: 視覚的に詳細度を理解できる学習ツール

まとめ

CSSの詳細度とカスケードを理解することで、スタイルが意図通りに適用されない問題を解決できるようになります。

覚えておくべきポイント:

  1. カスケードは、オリジン → 重要度 → 詳細度 → 出現順序の順で評価される
  2. 詳細度は、ID列(1-0-0)> CLASS列(0-1-0)> TYPE列(0-0-1)の順で比較される
  3. インラインスタイルは通常のセレクタより高い詳細度を持つ
  4. !important は最終手段であり、使用は最小限に抑えるべき
  5. 継承されたスタイルより、要素を直接対象とするスタイルが常に優先される
  6. 詳細度は低く保つことで、保守性の高いCSSを書ける

これらの知識を活用して、予測可能で保守しやすいCSSを書いていきましょう。

参考リンク