Yuto Blog
speaker-deck-iconyoutube-iconrss-iconx-icongithub-icon

Chrome 130でのCSSネスティング改善: CSSNestedDeclarationsの詳細

publishedAt:
2024/10/13
updatedAt:
2024/10/14
目次

Intro

web.devが先日、CSS nesting improves with CSSNestedDeclarationsというブログを公開しました。

このブログでは、CSSネスティングの仕様にCSSNestedDeclarationsが追加されたことについて書かれています。果たして業務でこのブログで書かれてるようなような動きに出くわすことはなかった気もしますが、CSSOMの動きなど少し面白かったので書くことにしました。

話さないこと

この記事では以下の内容については書きません。

CSSネスティングの以前までの仕様

まずCSSネスティングの以前までの仕様についてです。 以下のようなCSSを書いた場合、皆さんはどのようなUIになると思いますか??

.foo {
	width: fit-content;

	@media screen and (min-width: 900px) {
		background-color: red;
	}

	background-color: green;
}

関連: github.com/yossydev/css-nesting-improves-with-cssnesteddeclarations

この場合、適用されるbackground-colorはred🛑になります。

(左: Chrome129 / 右: Chrome130)

しかし、CSSの仕様としては後ろに書かれたコードが適用されるはずです。これがChrome 129までのCSSネスティングの仕様でした。

なぜこのような仕様だったのか

なぜこのような仕様になっていたのでしょうか?

Intent to Ship: The Nested Declarations Ruleというやり取りの中には、以下のように記載されています。

This was at the time done intentionally for a mix of CSSOM and historical reasons, and all implementations of CSS Nesting currently agree on this behavior. However, it turns out this shifting behavior isn’t what authors expect (WebKit blog post), and the CSSWG now consider the decision a mistake. In October 2023, the CSSWG resolved to not do the shifting behavior anymore

要約すると以下のようになります。

実装した時点ではその方針で進んでいたけど、それを使用する開発者にとっては思っていた挙動と違ったので直すことにした。ということらしいです。

どのようにShitingされるのか

では次に、実際にどのようにShitingされるのをみてみましょう。 先ほどのCSSに対して生成されたCSSOMを例にします。

↳ CSSStyleRule
  .type = STYLE_RULE
  .selectorText = ".foo"
  .resolvedSelectorText = ".foo"
  .specificity = (0,1,0)
  .style (CSSStyleDeclaration, 2) =
    - width: fit-content
    - background-color: green
  .cssRules (CSSRuleList, 1) =
    ↳ CSSMediaRule
    .type = MEDIA_RULE
    .cssRules (CSSRuleList, 1) =
      ↳ CSSStyleRule
        .type = STYLE_RULE
        .selectorText = "&"
        .resolvedSelectorText = ":is(.foo)"
        .specificity = (0,1,0)
        .style (CSSStyleDeclaration, 1) =
          - background-color: red

background-color: greenCSSStyleRule.styleに含まれているものの、@media screenの後に定義されたかどうかはわからないです。そのため、パースされたCSSをみると、以下のようになります。(出力された結果から見やすくなるように少し整形しています)

.foo {
  width: fit-content; 
  background-color: green;

  @media screen and (min-width: 900px) {
    background-color: red;
  }
}

この結果、background-color: green@media screenよりも上に来てしまいました。これではCSSの優先順位的に、min-width: 900pxが正の時はredが適用されます。

(そもそも常にbackground-color: greenにしたいならbackground-color: redいらないのでは?みたいなご意見はごもっともです)

CSSNestedDeclarationsの追加

CSS Working GroupはShiftingを解消するために、新しくnested declarations ruleを追加しました。

同じCSSの内容に対して、Chrome130からは以下のようなデータ構造になります。

↳ CSSStyleRule
  .type = STYLE_RULE
  .selectorText = ".foo"
  .resolvedSelectorText = ".foo"
  .specificity = (0,1,0)
  .style (CSSStyleDeclaration, 1) =
    - width: fit-content
  .cssRules (CSSRuleList, 2) =
    ↳ CSSMediaRule
      .type = MEDIA_RULE
      .cssRules (CSSRuleList, 1) =
        ↳ CSSNestedDeclarations
          .style (CSSStyleDeclaration, 1) =
            - background-color: red
    ↳ CSSNestedDeclarations
      .style (CSSStyleDeclaration, 1) =
        - background-color: green

今度はCSSNestedDeclarationsというものが追加されているのがわかるかと思います。 これにより、パーサーは以前の宣言の位置を維持させることができるようになりました。

@nest

今回CSSNestedDeclarationsが追加されましたが、最初の提案では@nestというものが提案されていたようでした。(CSS Nesting: @nest

そしてこの提案に関して、Webkit側が拒否しています。

WebKit strongly opposes introducing an @nest rule for this purpose. We don't think expanding the syntax space of CSS for the convenience of CSSOM representation is an acceptable cost to authors, and prefer a solution that represents interleaved style declarations in the CSSOM in a way that does not have an externality on CSS syntax. ref: https://github.com/WebKit/standards-positions/issues/337#issuecomment-2078329470

CSSOMのために名前空間を拡張するのは好ましくなく、CSSの構文に影響を与えないように、CSSOMのインターフェースを拡張することが理想とのことです。 あまりここは本題と逸れるので、このくらいにしておきます。

まとめ

今回はChrome130で追加されるCSSNestedDeclarationsについて、導入背景と一緒に見ていきました。 CSSのパースについて、正直今まで考えて開発をしてこなかったので、今回の内容で興味を持つきっかけになりました。特に今回はあまり触れられていないのですが、nested declarations ruleの具体的な実装内容みたいなところも、時間があれば読んでみたいと思います。

追記・編集

いくつか今回の内容でフィードバックいただいたので、内容の修正を加えています。

ご指摘ありがとうございました🙇‍♂️

参考・関連

0