最近便利なCSSおれおれAdvent Calendar 2023 – 24 日目

SCSS とかでやってたやつが素の CSS でもできるようになりました。ネ申

便利~

a {
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }
}

@media

すげえ便利~~

:root {
  color: #333;
   
  /* ダークモード */
  @media (prefers-color-scheme: dark) {
    color: #eee;
  }
}

要素型には & が必要

仕様上、こういう使い方はできません。

/* だめ */
article {
  h1 {
    color: red;
  }
}

& を付ける必要があります。

/* よし */
article {
  & h1 {
    color: red;
  }
}

仕様に駄目と書いてあります

逆にクラスセレクターとかには不要なだけで、付けたければ付けても大丈夫です。

理由はパフォーマンス

仕様では “Why are there restrictions on nested rule selectors?” (なぜ入れ子ルールのセレクターには制約があるのか)というコラムみたいなのを用意して説明しています。

例えば padding-left:hover と書かれていたらどうでしょう。まあ混乱しますね。

人でなくてもブラウザーのエンジンが CSS の文法解析を行う際も大変です。そこまで読んだら判断をいったん保留して先読み lookahead を行う必要があるためです。

読んでから、次が ; ならプロパティ(値 hover は無効)、あるいは { なら入れ子になったスタイル定義(要素型 padding-left はおそらくカスタム要素)と判断を確定することになります。

また CSS のプロパティとセレクターのいずれも空白文字で区切って複数置くことができるので、padding-left:hover div auto みたいのも想定できます。わあ大変だあ。

というわけで仕様上許されていないので、要素型には & を忘れずに付けましょう。

でも動く……?

仕様上駄目でもブラウザーが実装しちゃうことはありますが、試したら Chrome と Firefox では動いてしまいました。

Safari は動いてないけどこっちが正解。

Chrome も今年の 9 月に試した頃は動いてなかったんだけど、対応しちゃったんだなあ。当時はChrome のドキュメントにもわざわざ「無効なネストの例」と節を作って書いてあったのに。

動くけど文法違反なのでやめましょう。(もしかしたら将来正式な仕様になるかもだけど……。)

追記(2023/12/24):Chrome 120 からできるようになったそうです。

幸い、あるエンジニアが、パーサーが通常の消費パスでネストされたセレクタをプロパティとして解析できなかった場合に、プロパティではなくセレクタを想定したモードで再起動できることを発見しました。解析が再開され、セレクタのネストとしてネストを確認します。これは、構文を解放するのに十分であると判断されるに十分な速度と信頼性を備えています。

さらっと書いてんなあ。(追記終わり)

vs SCSS

という感じで大変便利でございますね。自分の場合はこれでもう SCSS が必要な場面はあんまりなさそうかな。

これまでの主な用途としては &:hover みたいなので、変化形をまとめて書いておきたいなあたりがモチベーションでした。

あとは $foo みたいな変数というか方々で使える定数も嬉しかったんだけど、それも CSS 変数ことカスタムプロパティ var(--foo) で念願叶ってます。

いま SCSS は何が利点なんだろ

煽りじゃなくて無知故のピュア疑問です。

変数は @use で管理したい気持ちはまあまああります。それと @mixin() は便利かも? あとは &--sub みたいなのとか?

でもそれくらいしか自分は Sass/SCSS のこと知らないので、その、すみません。CSS 方面専門でやってる方は便利な使い方をもっとご存じだろうと思います。

その他

入れ子の親の詳細度は :is() と同じ

一番高い詳細度になります。

/* (0,1,0), (1,0,0) */
.foo, #bar {
  /* (1,1,0) */
  .boo {
    color: red;
  }
}

/* (0,2,0) */
.foo .boo {
  color: blue;
}

.foo .boo な要素の文字色は blue ではなく red になります。

入れ子は後回しになる

入れ子になった宣言は後回しにされるので、こう書いたのと同じになります。

article {
  color: green;
  & { color: blue; }
  color: red;
}

/* ↑同じ↓ */

article {
  color: green;
  color: red;
  & { color: blue; }
}

& なし要素型セレクターは実際どれだけ遅いのか

調べてません。ごめんね……。

セレクターの速度は Edge の DevTools で測定できます。でも普通のウェブページだとどうせ一瞬なのでなあ。程よく複雑な例があると良いんだけど。

入れ子はあんまりしない方が良い

ところで入れ子はあんまりしない方が良いと自分は思ってます。

特に .some-area label みたいな要素型でやると想定しない(後から追加された)UI をわざわざ例外としてスタイルを打ち消すスタイルを書く必要があったりするので避けたいです。要素型よりクラスを付けてやる方が対象が明確になって良いと思うんですよね。

そしてクラスの場合は命名が自由なので、入れ子 .some-area .label (0,2,0) にして詳細度を上げるよりもクラス名自体を入れ子構造 .someArea-label (0,1,0) にすることで、詳細度の上昇を抑えつつ可読性を上げる(単体で見ただけで利用想定がわかる)ことができるかなと思います。入力はエディターの補完があるし。

いや別に詳細度に親を殺された経験はありません。むやみな要素型セレクターにはプロジェクトを殺されかけているが。

おしまい

いやはや良い時代になったものです。

というわけで 2023 年もおつかれさまでした。新年もよろしくお願いいたします。

参考

履歴