CSSセレクタの優先順位を自由自在に上げる方法

CSSは簡単なようで難しいです。設定した覚えのないスタイルが適用されているっていうのはウェブサイト制作者なら誰でも経験があるはず。CSS設計をきちんとしていれば起こりづらくはなるのですが、それでも、ごくまれに「あれっ、おかしい」ってなります。

そんなときに役立つ、CSSセレクタの優先順位を自由自在に上げる方法をご紹介します。

設定した覚えのないスタイルが適用されている問題は、祖先要素に対して設定されたスタイルが、意図せず子孫要素にも設定されていたっていうケースがほとんどです。

ですが、意図的に子孫要素にもスタイルを適用させたい場合もあります。このとき、CSSセレクタの優先順位 (詳細度) を考慮しないと、スタイルを上書きしたいのに上書きできないっていう問題が発生します。

CSSセレクタの優先順位 (詳細度) のおさらい

まずは、CSSセレクタの優先順位 (詳細度) のおさらいをしてみましょう。

次のCSSコードでp要素の文字色はどうなるでしょうか?

p {
  color: red;
}
p {
  color: green;
}

正解は「green」です。通常、後に記述された方のスタイルが優先されます。

それでは、次のCSSコードでp要素 (文書構造的に同一のp要素) の文字色はどうなるでしょうか?

html body main article header p {
  color: red;
}
p {
  color: green;
}

正解は「red」です。記述された順序を無視して「どこの要素内の要素に対する設定なのか」がより詳細な方のスタイルが優先されます。

それでは、次のCSSコードでp要素 (文書構造的に同一のp要素) の文字色はどうなるでしょうか?

html body main article header p {
  color: red;
}
._article_header p {
  color: green;
}

正解は「green」です。CSSセレクタを構成する各種セレクタには詳細度が設定されており、次のようなルールで詳細度の高いスタイルが優先されます。

A (IDレベル)B (クラスレベル)C (要素レベル)
ID (#X)、IDの否定 (:not(#X))クラス (.X)、クラスの否定 (:not(.X))、疑似クラス (:X)、疑似クラスの否定 (:not(:X))、疑似要素 (::X)、属性 ([X])、属性の否定 (:not([X]))要素 (X)、要素の否定 (:not(X))
詳細度 (高い > 低い)
A の数が多い > A の数が少ない > B の数が多い > B の数が少ない > C の数が多い > C の数が少ない

先のCSSコードの場合、html body main article header p { … } は A = 0, B = 0, C = 6、._article_header p { … } は A = 0, B = 1, C = 1 となります。

A、B、C のそれぞれの数が同じ場合は、通常通り、後に記述したスタイルが優先されます。

IDレベルでCSSセレクタの優先順位を上げる方法

この記事を書くきっかけになったのは、WordPressでAMPプラグインを使用したときに出力されるスタイルに :not(#_) というセレクタを見つけて「なるほど」と思ったからです。:not(#_) はid属性値がアンダーバー (<X … id="_" …>) ではない要素を意味します。

通常、アンダーバーのみのid属性値を指定することはないはずなので、安全にIDレベルの詳細度を上げることができます。

次のCSSコードはborderプロパティをmarginプロパティのように使用するためのものです。幅 (width, height) に余白を含みつつ、paddingプロパティも併用したい場合に重宝します。

.example {
  background: #fff;
  border: 1em;
  padding: 1em;
}
.example:not(#_) {
  background-clip: padding-box;
  border-style: solid;
  border-color: transparent;
  box-sizing: border-box;
}
.example.option {
  background: #ccc;
  border: 2em;
  padding: 2em;
}

個々のプロパティの値を強制する !important というのもありますが、:not(#_) を使用することで複数のプロパティを一括して強制できるメリットがあります。また重ねて使用することで詳細度の微調整もできます。

p:not(#_):not(#_) {
  color: red;
}

クラスレベルでCSSセレクタの優先順位を上げる方法

:not(#_) を応用した :not(._) というセレクタが使えます。:not(._) はclass属性値にアンダーバーのみの値 (<X … class="_ …" …>) がない要素を意味します。

id属性値の場合と同様に、アンダーバーのみのclass属性値を指定することはないはずなので、安全にクラスレベルの詳細度を上げることができます。

他に :nth-child(n):root といった疑似クラスを使用する方法もあります。

p:not(._):not(._) {
  color: red;
}
p:nth-child(n):nth-child(n) {
  color: red;
}
:root:root p {
  color: red;
}

要素レベルでCSSセレクタの優先順位を上げる方法

:not(#_) を応用した :not(_) というセレクタが使えます。:not(_) はHTMLの仕様には存在しない_要素 (<_ …>) ではない要素を意味します。

将来的なHTMLの仕様 (HTML 5.X やHTML 6 など) でも、_要素は作られることがないと思われるため、安全に要素レベルの詳細度を上げることができます。

次のCSSコードはセレクタの要素数が3個以上あるp要素のみ文字色が設定されます。

p:not(_):not(_) {
  color: inherit;
}
div p {
  color: red;
}
div div p {
  color: green;
}

つまり「red」が設定されることはありません。

CSS設計をしっかりと行い、CSSコードの記述順に気を配ることで、CSSセレクタの優先順位を操作する必要性はほぼ無くなります。

しかし、ウェブサイトの運用中にCSSセレクタの優先順位を操作する必要が出てくる場合もあります。そんなときに、今回の内容をお役立てください。

関連記事