CSSの仕組み
「なぜか上書きできない」の原因は、5段のどこかにあります。順に見れば、だいたい3段目までで見つかります。
図の中身を文字で読む
- 「上書きできない」ときは、この順に上から確かめる。
- 1. !important が付いているか 付いていれば、以下はすべて無視される
- 2. カスケードレイヤーの順序 後から宣言したレイヤーが勝つ。詳細度より先に効く
- 3. インラインスタイルか style 属性は、レイヤー外の通常宣言より強い
- 4. 詳細度の点数 同じレイヤーの中で、大きいほうが勝つ
- 5. 記述順 ここまで同点なら、後に書いたほうが勝つ
- 詳細度=3つの数の組。左の桁が1つでも多ければ、右がいくつあっても勝つ。
- セレクタ ID クラス・属性・擬似クラス 要素・擬似要素
- #main .card p 1 1 1 ID が1つあるので最強
- .card .title.is-active 0 3 0 クラスを何個重ねても届かない
- :where(.card) p 0 0 1 :where() の中身は 0 と数える
- :where() で包むと詳細度が上がらないので、上書きしやすい土台になる。逆に :is() は、中で最も強いものが採用される。
ひと目で#
「なぜか上書きできない」の原因は、ほぼこの5段のどこかにあります。
- カスケード
- どの宣言を採用するかを決める仕組み
- 詳細度
- セレクタの強さ。ID、クラス等、要素の3つの数の組
- カスケードレイヤー
@layerで層を作る。詳細度より先に効く- カスタムプロパティ
--nameで値に名前を付ける。継承する- 論理プロパティ
- 上下左右ではなく、文章の流れで方向を書く
- ボックスモデル
- 幅がどこまでを含むか。
border-boxにすると外周まで
上書きできないときに見る順#
| 順 | 確かめること |
|---|---|
| 1 | !important が付いていないか |
| 2 | 相手があとのカスケードレイヤーにいないか |
| 3 | インラインの style になっていないか |
| 4 | 詳細度で負けていないか |
| 5 | 相手のほうがあとに書かれていないか |
使い分け#
| こういうとき | やること |
|---|---|
| 外部ライブラリを上書きしたい | @layer で層の順を決める。詳細度を上げない |
| 上書きしやすい土台を作りたい | :where() で包む。詳細度が0になる |
| 命名の衝突を避けたい | BEM。ブロック、要素、修飾子で名前空間を作る |
| クラスを組み合わせて組みたい | ユーティリティファースト |
| 縦書きや多言語に備えたい | 論理プロパティ(margin-inline など) |
詳細度で戦わない#
引き継いだサイトで色を1つ変えたいだけなのに、既存のセレクタが長くて効かない。ここでセレクタを伸ばして勝つと、次に直す人はさらに長い一手を探すことになります。
図の中身を文字で読む
- ✕ セレクタを伸ばして勝つ ○ 層の順で決める
- 勝つたびに詳細度が積み上がる .btn(0,1,0) @layer vendor, site;(あとの層ほど強い)
- vendor 層(先)
- 引き継いだCSS・ライブラリをまるごと入れる
- .nav .btn(0,2,0)
- #header .nav .btn(1,2,0) site 層(あと)
- 自分のCSS。.btn(0,1,0)のままで勝つ
- #header .nav .btn.is-on { … !important }
- あとの層は、詳細度に関係なく必ず勝つ
- 次に直す人は、これより強い一手を探すことになる !important も長いセレクタも要らない
- 層の順は @layer vendor, site; と最初に宣言した順で決まる。ファイルの読み込み順ではない。
- 詳細度で戦うのは同じ層の中だけになり、競争が外へ広がらない。
あとの層は、詳細度に関係なく必ず勝ちます。 引き継いだCSSを丸ごと先の層に入れ、自分の分をあとの層に置けば、.btn の1クラスのままで上書きできます。層の順は最初の @layer の宣言で決まり、ファイルの読み込み順とは関係ありません。
このコードで動いている見本です
間違えやすいところ#
!importantで押し切る次に上書きするとき、また!importantが要る- セレクタを長くして勝とうとする詳細度が上がり、以後どのページでも上書きしにくくなる
- ID セレクタでスタイルを当てるクラスをいくつ重ねても勝てなくなる
- カスタムプロパティを
:rootに集める局所で切り替えられなくなる。要素に持たせると継承で効く widthにborder-boxを忘れるpadding と border の分だけ膨らむ- 論理プロパティと物理プロパティを混ぜる打ち消し合って、原因が追えなくなる
- ユーティリティと BEM を方針なく混ぜるどちらで直すか毎回迷う
実装#
このコードで動いている見本です
直す例#
架空の案件を1つ例にとります。どこを見て、何をどう直すかを書きます。
引き継いだ会計事務所のサイトで、CTAボタンの色を1つ変えるだけの依頼です。.btn { background: … } を足しても効きません。開発者ツールで見ると、勝っているのは #main .contact-box ul li a.btn(1,2,3)でした。
前の担当者も同じ場面に立って、セレクタを伸ばして勝ったのだと分かります。ここでさらに長い一手を足すと、次の人はもっと長い一手を探します。
やることは、勝ち方を変えることです。引き継いだCSSを @layer vendor に丸ごと入れ、自分の分を @layer site に置きます。あとの層は詳細度に関係なく勝つので、.btn の1クラスのままで色が変わります。触るのは追加する1ファイルだけで、引き継いだCSSは1行も書き換えません。
コピーする#
方向を持つ余白は、上下左右ではなく文章の流れで書きます。縦書きや他言語に組み替えるとき、打ち消しの記述が要らなくなります。
このコードで動いている見本です
確認する#
- 追加したCSSに
!importantと ID セレクタが無い - 新しく書いたセレクタが、クラス3つ以内で収まっている
- 引き継いだCSSのファイルを書き換えていない(層の順だけで解決している)
使えるパターン#
このページで扱う語#
このページで扱う語を並べる本文では太字2 語
語の名前から引くなら用語索引へ。
- BEM
- ブロック・エレメント・モディファイアの3要素でクラス名を構成する命名規約。
- ユーティリティファースト
- 1つの役割だけを持つ小さなクラスを組み合わせてスタイルを組む方式。
この話が要る場面#
このページが答えになる、現場の困り事です。困り事の側から探すと、同じ場所に着きます。
!important、カスケードレイヤーの順、インライン style、詳細度、記述順の順に確かめる。
このコードで動いている見本です
セレクタを長くしない。@layer で層の順を決めるほうが、あとで壊れない。
このコードで動いている見本です
:where() で包む。中身の詳細度が0になるので、あとから素直に上書きできる。
このコードで動いている見本です
box-sizing: border-box を全体に当てる。
このコードで動いている見本です
上下左右ではなく論理プロパティで書く。margin-inline や inset-block-start。
このコードで動いている見本です
:root にしか置いていない。要素に持たせると継承で子に届き、局所で切り替えられる。
このコードで動いている見本です
この場面のほかの困り事と、そのまま貼れる部品はCSSで詰まるにまとめてある。逆引きには全188件。