CSSの優先順位とは、同じ要素の同じプロパティに複数の指定が当たったとき、どれを採用するかを決めるルール(カスケード)のことです。 判定は「!important かどうか → インライン style かどうか → @layer の順番 → 詳細度 → @scope の近さ → 書いた順番」の順に進み、上の段階で決着がつけば、下の段階は見られません。詳細度(セレクタの強さ)は、この流れの4番目に登場する1つの要素です。

この記事では、セレクタ別の詳細度の一覧と、カスケード全体の判定順のフローチャートを載せ、「!important を付けたのに上書きされない」ケースを1つずつ再現しました。結果はすべて Playwright(Chromium 141.0.7390.37)で getComputedStyle() の値を読み取ったものです。詳細度の数値は、DevTools が内部で使っている仕組み(Chrome DevTools Protocol)から取得した値を載せています。

CSS優先順位の早見表

判定の順番(上から順に比べ、差がついた時点で決まる)

順 比べるもの 勝つほう ひとことメモ
1 重要度(!important) !important が付いた宣言 アニメーション中の値は通常の宣言より強く、!important より弱い
2 インライン style かどうか style="..." 属性の宣言 !important 同士でも、インラインが勝つ
3 @layer(カスケードレイヤー) 通常:レイヤー外 > 後のレイヤー > 先のレイヤー !important では順番が逆になる
4 詳細度(A-B-C) ID → クラス → タイプの順に数が多いほう 同じレイヤー内でだけ比べられる
5 @scope の近さ スコープの根元に近いほう 詳細度が同じときだけ使われる
6 書いた順番 後に書いたほう class 属性の中の並び順は関係しない

セレクタ別の詳細度(A-B-C 表記)

詳細度は A(ID)- B(クラス・属性・疑似クラス)- C(タイプ・疑似要素) の3つの数で表します。左の桁から比べ、大きいほうが勝ちます。

セレクタ 例 詳細度 数え方
全称セレクタ * 0-0-0 数えない
タイプ(要素名) p 0-0-1 C に1
疑似要素 p::before 0-0-2 疑似要素も C に1
クラス .a 0-1-0 B に1
属性 [data-x]、[type="text"] 0-1-0 B に1
疑似クラス :hover、:first-child 0-1-0 B に1
ID #t 1-0-0 A に1
結合子 div p、.wrap > p 0-0-2、0-1-1 空白・>・+・~ は数えない
:is() :is(.a, p)、:is(#x, p) 0-1-0、1-0-0 引数の中でいちばん高いもの
:not() :not(.zzz)、:not(#x) 0-1-0、1-0-0 引数の中でいちばん高いもの
:has() div:has(.a) p 0-1-2 引数の中でいちばん高いもの
:where() :where(#t, .a) 0-0-0 常に0
:nth-child() p:nth-child(1) 0-1-1 疑似クラスとして B に1
:nth-child(n of S) p:nth-child(1 of .a) 0-2-1 疑似クラスの1 + S の詳細度
インライン style style="color: red" (別枠) 詳細度より前の段階で勝つ

この表の詳細度は、Chromium 141 が各セレクタに対して実際に計算した値です(CSS.getMatchedStylesForNode で取得)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS優先順位と詳細度の早見表|!importantが効かない原因と直し方」

カスケード全体の判定順(フローチャート)

ブラウザは、同じプロパティに当たった複数の宣言を、次の順でふるいにかけます。MDNの「Introduction to the CSS cascade」に書かれている順番を、制作でよく出会う範囲(自分で書くCSS)に絞って図にしました。

flowchart TD
  S["同じプロパティに複数の宣言が当たった"] --> R{"セレクタと@mediaは<br>その要素に一致している?"}
  R -- 一致しない --> X["候補から外れる"]
  R -- 一致する --> I{"!important が<br>付いた宣言がある?"}
  I -- ある --> I2["!important 同士で比べる"]
  I -- ない --> N["通常の宣言同士で比べる"]
  I2 --> IL{"インライン style?"}
  IL -- はい --> W1["インラインの !important が勝つ"]
  IL -- いいえ --> IY{"@layer の中にある?"}
  IY -- ある --> W2["先に宣言したレイヤーが勝つ<br>レイヤー内 > レイヤー外"]
  IY -- すべてレイヤー外 --> SP
  N --> NL{"インライン style?"}
  NL -- はい --> W3["インラインが勝つ"]
  NL -- いいえ --> NY{"@layer が違う?"}
  NY -- 違う --> W4["レイヤー外 > 後のレイヤー > 先のレイヤー"]
  NY -- 同じ --> SP{"詳細度 A-B-C が違う?"}
  SP -- 違う --> W5["詳細度が高いほうが勝つ"]
  SP -- 同じ --> SC{"@scope の根元までの<br>近さが違う?"}
  SC -- 違う --> W6["近いほうが勝つ"]
  SC -- 同じ --> W7["後に書いたほうが勝つ"]

全体の強さを1本の列に並べると、次のようになります(上ほど強い)。

強さ 宣言の種類 補足
1 トランジション中の値 変化している間だけ
2 ブラウザ標準CSS・ユーザー設定CSSの !important 制作側からは上書きできない領域
3 インライン style の !important
4 @layer 内の !important(先に宣言したレイヤーほど強い) 通常の宣言と順番が逆
5 レイヤー外の !important
6 アニメーション(@keyframes)の値 再生中だけ
7 インライン style(通常)
8 レイヤー外の通常の宣言 ふだん書いているCSSはここ
9 @layer 内の通常の宣言(後に宣言したレイヤーほど強い)
10 ユーザー設定CSS、ブラウザ標準CSS(通常) a 要素の青色など
11 親から継承した値 直接の指定がないときだけ使われる

3〜5と7〜9のそれぞれの中で、さらに「詳細度 → @scope の近さ → 書いた順番」で比べます。 大事なのは、詳細度がどれだけ高くても、上の段階(重要度・インライン・レイヤー)で差がついていれば逆転しない、という点です。

実測:段階ごとの勝ち負け

次のHTMLに対して、2つのルールの色を red と blue にして、どちらが採用されるかを確かめました。

<div id="wrap" class="wrap">
  <p id="t" class="a b" data-x="1">text</p>
</div>
対決 採用された色 決め手
.a { red } → .b { blue } blue 詳細度が同じなので後勝ち
上と同じCSSで class 属性を "b a" に入れ替え blue class 属性内の順番は無関係
<style> を2つに分け、1つ目 .a { red }、2つ目 .a { blue } blue 文書内で後に出てくるほうが勝つ
1つ目 .a.b { red }、2つ目 .a { blue } red 順番より詳細度が先
#t { red } 対 style="color: blue" blue インラインは ID より強い
.a { red !important } 対 style="color: blue" red !important はインラインより強い
#t { red !important } 対 style="color: blue !important" blue !important 同士ならインラインが勝つ
.a { red !important } 対 #t { blue } red 重要度が詳細度より先
アニメーション(green)対 #t { blue } green 再生中は通常の宣言より強い
アニメーション(green)対 #t { blue !important } blue !important はアニメーションより強い

詳細度の数え方と比べ方

桁をまたいで繰り上がらない

詳細度は左の桁から順に比べます。 クラスをいくつ重ねても、ID 1つには届きません。

先に書いたセレクタ 後に書いたセレクタ 採用されたほう 詳細度の比較
#t .a.b[data-x]:first-child 先(#t) 1-0-0 対 0-4-0
#t .a を256個つなげたもの 先(#t) 1-0-0 対 0-256-0
.a html body div p 先(.a) 0-1-0 対 0-0-4
.a div p 先(.a) 0-1-0 対 0-0-2
p.a .a 先(p.a) 0-1-1 対 0-1-0
.a.a .a 先(.a.a) 同じクラスの繰り返しも数える
[id="t"] .a 後(.a) 属性セレクタはクラスと同じ 0-1-0。同点なので後勝ち
#t [id="t"] 先(#t) 同じ要素を指していても、書き方で詳細度が変わる
:first-child .a 後(.a) 疑似クラスも 0-1-0。同点なので後勝ち
p * 先(p) * は 0-0-0
.wrap p .wrap > p 後 結合子は数えない。同点なので後勝ち
:root p html p 先(:root p) :root は疑似クラスなので 0-1-1 対 0-0-2

「ID は100点、クラスは10点、タイプは1点」という覚え方は、桁が繰り上がらない点だけ補って使うと便利です。クラス10個(0-10-0)は ID 1個(1-0-0)に届かない、と覚えておけば十分です。

:is()・:not()・:has()・:where() の詳細度

この4つは、かっこの中にセレクタを書ける疑似クラスです。自分自身は数えず、引数の詳細度だけが反映されるのが共通のルールで、:where() だけは常に0になります。

先 後 採用されたほう 理由
:is(#x, p) .a.b 先 引数の最大(#x の 1-0-0)が採用される。要素に #x が付いていなくても
:is(.a, p) p 先 0-1-0 対 0-0-1
:not(#x) .a.b 先 :not() も引数の最大が入る(1-0-0)
p:not(.zzz) p.a 後 どちらも 0-1-1。同点なので後勝ち
div:has(.a) p div p 先 :has() の引数 .a が加わり 0-1-2
:where(#t) p 後(p) :where() は 0-0-0
p :where(#t) 先(p) 後に書いても 0-0-0 は 0-0-1 に届かない

:is(#x, p) は、p 要素に当たる場合でも詳細度が 1-0-0 になります。 セレクタをまとめる目的で :is() に ID を混ぜると、思ったより強いルールができあがるので、まとめるだけなら :where() のほうが扱いやすい場面があります。

CSSネスト(入れ子)も :is() と同じ扱いです。.a, #zz { & span { color: red } } は :is(.a, #zz) span と同じ意味になり、詳細度は 1-0-1 です。実測でも、後に書いた .a.b span { color: blue }(0-2-1)より優先されて red になりました。

!importantの仕組み

!important は、宣言の値の後ろに付けて「重要度」を上げる目印です。

.note { color: red !important; }

押さえておきたい性質は4つです。

  • プロパティ1つずつに付く。 ルール全体ではなく、その1行だけが強くなります。
  • !important 同士の勝負は、ふつうの詳細度 → 順番で決まる。 付ければ必ず勝つ、という印ではありません。
  • @layer の中では順番が逆になる。 通常はレイヤー外が強いのに対し、!important ではレイヤー内(それも先に宣言したレイヤー)が強くなります。
  • 継承には効かない。 親に付けた !important は、子が受け継ぐ値を強くはしません。

!importantを付けても上書きされない9つのケース

「!important を付けたのに変わらない」を、実際に再現して確かめました。期待は red、実際の結果が blue などになっているものが「効いていない」ケースです。

# 書いたCSS(red にしたい) 実測 原因
1 .a { color: red !important } と #t { color: blue !important } blue 相手も !important で、詳細度が上
2 .a { color: red !important } → .b { color: blue !important } blue 相手も !important で、同じ詳細度・後ろにある
3 #t { color: red !important } と style="color: blue !important" blue 相手がインラインの !important
4 #t { color: red !important } と、@layer a { p { color: blue !important } } blue レイヤー内の !important はレイヤー外より強い
5 #t { color: red !importnat } blue つづりの誤りで、その宣言ごと無効になる
6 #t { color: redd !important } blue 値が無効で、その宣言ごと無効になる
7 .wrap .c { color: red !important }、@media (max-width: 500px) { ... } blue セレクタや @media が一致していない
8 親 .wrap { color: red !important } と子 p { color: blue } blue 親の値は継承で届くだけ。子への直接指定が優先
9 .a { --c: red !important; color: var(--c) } と #t { color: blue } blue !important が付いたのは変数 --c。color の宣言は通常のまま

順に直し方を見ていきます。

1・2:相手も !important のとき

!important 同士では、通常と同じく詳細度と順番で決まります。相手の !important を外せるなら外すのがいちばんすっきりします。外せない場合(外部のCSSなど)は、同じ詳細度のセレクタを後ろに書くか、次の章の @layer を使います。

/* 直す前 */
.a { color: red !important; }
#t { color: blue !important; }   /* 詳細度 1-0-0 で勝つ */

/* 直した後:相手の !important を外せば、こちらは通常の宣言で足りる場合が多い */
#t { color: blue; }
.a { color: red !important; }    /* 重要度で勝つ → red */

3:インライン style や JavaScript が相手のとき

style="color: blue !important" は、スタイルシート側の !important より強くなります。JavaScript が書き込むインライン style も同じ扱いです。

JavaScript の書き方 スタイルシート側 .a { color: blue !important } に対して
el.style.color = 'red' blue のまま(通常のインラインは !important に届かない)
el.style.setProperty('color', 'red', 'important') red になる(インラインの !important)

スライダーやモーダルのライブラリがインライン style を書き込んでいる場合、CSS 側で上書きするには !important が必要になります。ライブラリ側にクラス名やCSS変数で見た目を変える設定がないかを先に確かめると、!important を使わずに済むことがあります。

4:@layer の中の !important が相手のとき

通常の宣言ではレイヤー外が勝ちますが、!important ではレイヤー内が勝ちます。

対決 実測
@layer a { p { red !important } } 対 レイヤー外 #t { blue !important } red(レイヤー内が勝つ)
@layer a, b; で a 内 p { red !important } 対 b 内 #t { blue !important } red(先に宣言した a が勝つ)
@layer a { p { red !important } } 対 style="color: blue !important" blue(インラインは レイヤーより前の段階)

レイヤー内の !important を上書きしたいときは、それより先に宣言したレイヤーを用意して、そこに !important を書きます。

@layer override, vendor;                 /* override を先に宣言 */
@layer vendor   { p  { color: blue !important; } }
@layer override { p  { color: red  !important; } }   /* → red */

5・6・7:宣言そのものが届いていないとき

つづりや値に誤りがあると、ブラウザはその1行を読み飛ばします。エラー表示は出ないので、DevTools で確かめるのが近道です(次の章)。なお ! important のように ! の後ろに空白が入る書き方は有効で、実測でも red になりました。

8:親に付けた !important

!important は「その要素の、そのプロパティ」を強くするだけで、子へ渡る値を強くするわけではありません。 継承した値は、子に直接当たる宣言があれば、それがどんなに弱いセレクタでも負けます。

対決 実測 理由
親 #wrap { color: red !important }、子に指定なし red 継承で届く
親 #wrap { color: red !important } と * { color: blue } blue *(0-0-0)でも、子への直接指定が継承より優先
親 .wrap { color: red !important } の中の a 要素 rgb(0, 0, 238) ブラウザ標準CSSの a の色が、継承より優先

直すには、変えたい要素そのものに当たるセレクタで書きます。

/* 直す前 */  .wrap   { color: red !important; }
/* 直した後 */ .wrap a { color: red; }       /* または .wrap a { color: inherit; } */

9:カスタムプロパティに付けた !important

--c: red !important の !important は、変数 --c をめぐる勝負にだけ効きます。その変数を使う color: var(--c) の宣言は通常の重要度のままです。逆に、変数同士の勝負では効きます。.a { --c: red !important } と #t { --c: blue } があるとき、p { color: var(--c) } は red になりました。

補足:アニメーションとトランジション

  • @keyframes の中に書いた !important は無視されます。 実測でも、keyframes 内の color: green !important は採用されず、#t { color: blue } の blue になりました。
  • アニメーション再生中の値を止めたいときは、通常のルール側に !important を付けます。
  • トランジションで変化している間は、その途中の値が最優先になります。transition: color 100s を付けた要素に !important の色を当てた直後は、まだ変化前の色のままでした。

DevToolsで打ち消し線から原因を特定する手順

Chrome DevTools の Elements パネルを使うと、どの宣言が採用され、どれが上書きされたかを確かめられます。以下は DevTools の一般的な操作手順で、画面の操作そのものは今回は未実行です(表示のもとになるデータは、次の項で実際に取得しています)。

手順 操作 わかること
1 対象の要素を右クリック →「検証」 Elements パネルでその要素が選ばれる
2 Styles タブを見る 当たっているルールが、強い順に上から並ぶ
3 打ち消し線の付いた宣言を探す その宣言は、より上にある別の宣言に上書きされている
4 Styles の上部の Filter に color などのプロパティ名を入れる そのプロパティに関係する宣言だけに絞れる
5 Computed タブでプロパティ名の横の矢印を開く 採用された宣言と、上書きされた宣言の一覧が出る
6 セレクタにマウスを重ねる 詳細度が表示される
7 宣言の左に警告マークが出ていないか見る 値やプロパティ名が無効なときに出る
8 ルールが Layer の見出しの下にあるか見る @layer に入っているかどうか

打ち消し線が付いた宣言と、いちばん上で打ち消されていない宣言を見比べ、早見表の「判定の順番」を上から当てはめます。

見比べてわかったこと 原因の段階
勝っているほうに !important がある 重要度
勝っているほうが element.style の欄にある インライン style(JavaScript の書き込みを含む)
自分のルールが Layer の下にあり、相手は外にある @layer
相手のセレクタのほうが ID やクラスの数が多い 詳細度
セレクタの強さが同じで、相手のファイルや行が後ろ 書いた順番(読み込み順)
自分のルールが一覧に出てこない セレクタの不一致・@media の不一致・ファイルの未読み込み
一覧にはあるが警告マーク付き つづり・値の誤り
Inherited from ... の欄にしか出てこない 継承で届いているだけ(ケース8)

Consoleで「当たっている宣言」を一覧にするスニペット

Styles タブと同じ情報を、プロパティを1つに絞って一覧にするスニペットです。#t と color を調べたい対象に書き換えて使います。

const el = document.querySelector('#t'), prop = 'color', rows = [];
const walk = (rules, ctx) => {
  for (const r of rules) {
    if (r instanceof CSSStyleRule) {
      if (el.matches(r.selectorText) && r.style.getPropertyValue(prop)) {
        rows.push({ selector: r.selectorText, value: r.style.getPropertyValue(prop),
                    important: r.style.getPropertyPriority(prop) === 'important', within: ctx });
      }
      if (r.cssRules?.length) walk(r.cssRules, ctx);
    } else if (r.cssRules) {
      walk(r.cssRules, (ctx + ' ' + r.cssText.split('{')[0].trim()).trim());
    }
  }
};
for (const s of document.styleSheets) { try { walk(s.cssRules, ''); } catch (e) {} }
console.table(rows);
console.log('採用された値:', getComputedStyle(el)[prop]);

次のCSSを当てたページで実行した結果です(Playwright から同じコードを実行。console.table の代わりに配列の中身を表にしています)。

p { color: black }
.a { color: blue }
.wrap .a { color: red !important }
#t { color: green }
@layer x { p.a { color: orange } }
selector value important within 詳細度(Chromium の計算値)
p black false 0-0-1
.a blue false 0-1-0
.wrap .a red true 0-2-0
#t green false 1-0-0
p.a orange false @layer x 0-1-1

採用された値は rgb(255, 0, 0)(red)でした。#t のほうが詳細度は高いのに、!important の付いた .wrap .a が勝っていることが一覧から読み取れます。別ドメインから読み込んだCSSは、ブラウザの制限で cssRules を読めないため、この一覧には出てきません(try で読み飛ばしています)。

詳細度を上げずに直す書き方

上書きしたいとき、セレクタを長くしたり !important を足したりすると、次に上書きする人がさらに強い指定を書くことになります。相手を弱くする、層の順番で勝つ、必要な分だけ上回る、という方向の直し方を5つ紹介します。いずれも red を blue に上書きできることを確かめました。

方法 書き方 実測
A. :where() で相手を弱くする :where(.wrap) p { red } → .a { blue } blue
B. リセットCSSを @layer に入れる @layer reset { #wrap p { red } } → .a { blue } blue
C. 外部CSSを @layer に入れる @layer vendor { .wrap .a { red } } → .a { blue } blue
D. カスタムプロパティで値だけ渡す .wrap p { color: var(--c, red) } → .a { --c: blue } blue
E. 相手と同じ形にそろえ、クラス1つぶんだけ上回る .wrap p { red }(0-1-1)→ .wrap .a { blue }(0-2-0) blue
比較:クラスを重ねる #wrap p { red } → .a.a.a { blue } red(ID には届かない)

A. :where() で土台のスタイルを弱くしておく

共通部品やリセットのように「あとで上書きされる前提」のスタイルは、:where() で包んでおくと、利用する側はクラス1つで上書きできます。

/* 土台:詳細度 0-0-1(:where の中は数えない) */
:where(.article) a { color: blue; text-decoration: underline; }

/* 利用側:クラス1つ(0-1-0)で上書きできる */
.button { color: white; text-decoration: none; }

B・C. @layer で「層の順番」を先に決める

@layer は、CSSを層に分け、層の順番で優先度を決める仕組みです。同じ層の中でだけ詳細度が比べられ、層が違えば詳細度は見られません。

/* 1行目で順番を宣言する。後ろほど強い */
@layer reset, vendor, components, utilities;

@layer reset      { #main p { margin: 0; } }      /* ID を使っていても… */
@layer components { .lead   { margin: 1em 0; } }  /* 後の層のクラス1つが勝つ */

実測で確かめたレイヤーの性質をまとめます。

確かめたこと 実測
レイヤー内 #t { red } 対 レイヤー外 p { blue } blue(レイヤー外が強い)
レイヤー外のルールを先に書いた場合 blue(位置にかかわらずレイヤー外が強い)
@layer a, b; のとき a 内 #t { red } 対 b 内 p { blue } blue(後の層が強い)
b のブロックを a より先に書いた場合 blue(最初の @layer a, b; の順番が使われる)
名前のないレイヤー @layer { #t { red } } 対 レイヤー外 p { blue } blue
レイヤーの入れ子:a 直下 #t { red } 対 a の中の x 内 p { blue } red(その層の直下は、入れ子の層より強い)
color: blue; color: revert-layer; 1つ前の層の値(red)に戻る

外部のCSSファイルを層に入れるときは、@import url("vendor.css") layer(vendor); と書きます(この @import の書き方は今回は未実行で、同じ内容を @layer vendor { ... } のブロックで確かめています)。自分のCSSをレイヤー外か後ろの層に置けば、外部CSSのセレクタがどれだけ長くても、短いセレクタで上書きできます。

D. カスタムプロパティで値だけを差し替える

部品側が var(--c, red) のように変数で値を受け取る形にしておくと、利用側はセレクタの強さを競わずに値を変えられます。変数は継承されるので、親やその要素自身のどこかで --c を定義すれば届きます。

@scope:範囲を区切り、近いほうを優先する

@scope は、ルールが効く範囲を「この要素の中だけ」と区切る仕組みです。MDNでは Baseline 2026(2026年3月から主要ブラウザの現行版で利用可能)と案内されています。

@scope (.card) to (.card__body) {   /* .card の中、ただし .card__body より内側は除く */
  p { color: red; }
}

カスケードでは、詳細度が同じときに「スコープの根元に近いほう」が勝つという段階が加わります。次のHTMLで確かめました。

<div class="card"><div class="note"><p id="t" class="a">t</p></div></div>
対決 実測 理由
@scope (.card) { p { red } } → @scope (.note) { p { blue } } blue .note のほうが p に近い
上の2つを逆の順番で書く blue 順番より近さが先
@scope (.card) { p.a { red } } 対 @scope (.note) { p { blue } } red 近さより詳細度が先
@scope (.card) { p { red } } → スコープなしの p { blue } red 同じ詳細度なら、スコープ付きが優先
スコープなしの .a { blue } → @scope (.card) { p { red } } blue 詳細度(0-1-0 対 0-0-1)が先
@scope (.card) to (.note) { p { red } } 適用されない p は .note より内側なので範囲外
@layer a { @scope (.wrap) { p { red } } } 対 後の層 @layer b { p { blue } } blue レイヤーが近さより先

@scope の中のセレクタには、スコープの根元(.card)の詳細度は加算されません。@scope (#wrap) { p { ... } } の p は 0-0-1 のままで、実測でもスコープ外の .a に上書きされました。根元の詳細度も加えたいときは :scope p(0-1-1)と書きます。

!importantを使う場面の判断表

!important は、使いどころを決めておくと便利な道具です。

場面 使うか 理由と書き方
余白や表示切り替えのユーティリティクラス(.hidden、.mt-0 など) 使う 「付けたら必ず効く」ことが役目。.hidden { display: none !important; }
動きを減らす設定への対応 使う @media (prefers-reduced-motion: reduce) の中で、すべてのアニメーションを止める
ライブラリが書き込むインライン style の上書き 使う(ほかに手段がないとき) 設定やCSS変数で変えられないかを先に確認
編集できない外部CSSの上書き できれば @layer 外部CSSを層に入れ、自分のCSSを外に置く
自分のCSS同士の上書き 使わない セレクタの詳細度をそろえ、順番で解決する
「とりあえず効かせたい」 使わない DevTools で原因の段階を特定してから直す

上書きされないときのチェックリスト

確認 見る場所
自分のルールが Styles に出ているか 出ていなければセレクタ・@media・ファイルの読み込み
警告マークが付いていないか つづり・値
相手に !important があるか あれば重要度の段階で決着している
相手が element.style か インライン style・JavaScript
どちらかが Layer の中か @layer の順番
詳細度はどちらが高いか セレクタにマウスを重ねる
読み込み順はどちらが後か ファイル名と行番号
継承で届いているだけではないか Inherited from の欄

よくある質問

Q. !important とインライン style はどちらが強いですか? A. スタイルシートの !important のほうが、通常のインライン style より強いです。ただし、インライン style 側にも !important が付いている場合はインラインが勝ちます。実測でも、.a { color: red !important } 対 style="color: blue" は red、#t { color: red !important } 対 style="color: blue !important" は blue でした。

Q. CSSファイルの読み込み順は優先順位に関係しますか? A. 関係します。ただし見られるのは最後の段階で、重要度・インライン・レイヤー・詳細度がすべて同じときだけです。<style> を2つに分けた実測では、同じ .a 同士なら後の <style> が勝ち、1つ目が .a.b(詳細度が上)なら1つ目が勝ちました。

Q. :is() と :where() はどう使い分けますか? A. セレクタをまとめる働きは同じで、詳細度が違います。:is() は引数の中でいちばん高い詳細度になり、:where() は常に 0-0-0 です。あとで上書きされる前提の土台のスタイルには :where()、いまの強さを保ちたいところには :is() が向いています。

Q. 詳細度を確認できるツールはありますか? A. Chrome DevTools の Styles タブでセレクタにマウスを重ねると表示されます。この記事の表の値は、DevTools が使っているのと同じ仕組みから取得しました。

まとめ

  • 優先順位は「重要度 → インライン → @layer → 詳細度 → @scope の近さ → 書いた順番」の順に判定され、上の段階で決まれば下は見られない。
  • 詳細度は A-B-C の3桁で、左から比べる。 クラスを256個重ねても ID 1個には届かない。
  • :is()・:not()・:has() は引数の最大の詳細度、:where() は常に0。
  • !important 同士は、詳細度と順番で決まる。 インラインの !important と、@layer 内の !important はさらに強い。
  • 親に付けた !important は子の直接指定に届かない。 変えたい要素に当たるセレクタで書く。
  • 原因は DevTools の打ち消し線と、判定順の表を上から当てはめて特定する。
  • 直すときは :where()・@layer・カスタムプロパティで、詳細度を上げずに解決する。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS優先順位と詳細度の早見表|!importantが効かない原因と直し方」

株式会社RJCでは、CSSの優先順位と詳細度の仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?