新春特別企画

2022年のCSS

2022年になりました。矢倉眞隆(@myakura)と申します。昨日に続き、新春特別企画のブラウザとウェブ標準動向について紹介します。

取り上げるトピックの数やそのインパクトから、今回はCSSを独立した記事として取り上げることになりました。「ブラウザとウェブ標準動向」についても寄稿していますので、そちらもお読みいただければうれしいです。

2022年以降のCSSは大きく変化しそうだなと思っています。これまでも、CSS3と呼ばれていた機能による表現力の強化、FlexboxやGridなど強力なレイアウト機能の追加など、大きな変化と言えるだろうものはありました。しかし現在提案・実装されている機能は、CSSの根幹を拡充するものと、これまでと性質が異なるものです。

Compat 2021とInterop 2022で互換性の向上

CSSのつらいところとしてまず取り上げられるのが、ブラウザ実装の挙動の違いです。2021年はCSSの互換性を向上させようという、Compat 2021というプロジェクトがChrome、Microsoft、Igaliaを中心に行われました。仕様のテストスイートweb-platform-testsのうち、複数ブラウザでパスしていないCSSの機能を修正していくというもので、ChromiumではFlexboxやGrid、position: stickyCSS Transformsのバグ修正のほか、aspect-ratioプロパティの実装が行われました。

FirefoxやSafariでもバグの修正や機能の実装が行われ、年末までには全ブラウザでスコアが上昇する喜ばしい結果となりました。

プロジェクトが一定の成果をみせたことで、今年も同様の取り組みが行われます。名前はInterop 2022と改められ、MozillaやAppleもトピック選定に加わっています。機能はスクロールやビューポート、フォーム要素など引き続きCSSが中心になりそうです。現在はFirefoxでのみ実装されているSubgridや、ウェブ標準の記事でも取り上げたdialog要素の実装もトピックに挙がっているので、それらのリリースも期待したいですね。

:focus-visible擬似クラスが全ブラウザでサポート

ウェブページでTabキーを押すと、リンクや入力フォームのまわりに枠線がつきます。これをフォーカスリング(またはフォーカスインジケーター)と言うのですが、このスタイルに関して機能の向上がありました。

フォーカスリングのスタイル指定には:focusという、フォーカスされている状態にマッチする擬似クラスが定義されていましたが、ここに新たに:focus-visibleという擬似クラスが登場したのです。

:focus疑似クラスはフォーカスされた状態にマッチするのですが、これはマウスでボタンをクリックした状態も含まれます。このためボタンクリック時に意図せず枠線がついてしまうことがあり、その見栄えや挙動を嫌って、サイトやアプリ開発者がフォーカスリングを消してしまうスタイルを書くことがあったのです。

/* アクセシビリティを損なうよくないCSS */
:focus {
  outline: none;
}

これはアクセシビリティを損なうものなので、解決策が検討されました。それが:focus-visible擬似クラスになります。

:focus-visible擬似クラス「フォーカスリングを出すべき状態」にマッチする擬似クラスです。たとえば、リンクやフォーム入力欄、マウスにTabキーで移動した場合には「フォーカスリングを出すべき状態」ですが、ボタンをマウスクリックする場合はそうではありません。

/* 見えるべきときに見えるフォーカスリング */
:focus-visible {
  outline: 2px solid;
  outline-offset: 2px;
}

この実装に合わせ、フォーカスリングが消される原因となっていた、意図しないフォーカスリングが出てしまう条件の調整もブラウザ内で行われました。ChromeやFirefoxは昨年に:focus-visibleが実装・リリースされ、すでに使えるようになっています。Safariも昨年初め頃からIgaliaによって実装が進められ、年末には開発版で有効になりました。リリースも近いでしょう。今からフォーカスリングのスタイルを書く場合には、:focusを使い消すのではなく、:focus-visibleを使って見やすいフォーカスリングのスタイルとなるようにしてください。

Scroll-linked Animationsが実装中

Scroll-linked Animationsという仕様があります。アニメーションというと時間経過による値の変化を指すことが多いですが、これはスクロール位置の変化と値の変化を関連づけるものです。画面をスクロールしたらふわっと要素が出てきたり、ページ上部にどれくらい読んだかを示すインジケーターバーを出すページがありますが、ああいった表現をCSSで実現する仕組みになります。

こうした表現は華美であるものの、スクロール位置をJavaScriptで都度取得し、要素の位置やスタイルを都度書き換えなければならず、ガタガタしたり重たく感じる実装がとても多いものです。Scroll-linked AnimationsはWeb Animations仕様を拡張し、CSSに@scroll-timelineというブロックを追加することで、宣言的にスクロールのアニメーションを定義できるようになっています。

Chromeでは以前から実装が行われており、Firefoxでも実装が始まっています。構文を変更する提案がされているなど、まだ仕様の安定度合いが高くないようですが、今後に期待したい機能のひとつです。

Safariが先行して実装する興味深い機能たち

ブラウザとウェブ標準の記事で取り上げましたが、2022年のSafariにはとても期待しています。エンジンの機能追加を伴うバージョンアップが頻繁に行われだしたからです。

なかでもCSSについては、面白い機能が技術プレビュー版ですでにいくつか実装されています。

まずは色関連です。LCH色空間など、より直感に近い色の指定ができる記法や、相対色を記述できる記法、色を混ぜ合わせるcolor-mix()関数などがCSS Colorモジュールに追加され、実装が進められています。Apple製品でDisplay P3など広色域のディスプレイが出てしばらく経ちますが、それらを活かしたいのでしょうか。

色の変換やアニメーションで複雑な計算を行いたいときに使うのでしょうか、sin()cos()tan()といった三角関数、ほかにもabs()round()などの数学関数も実装されています。ここまで要るのかという気もしますが、実装したとなると何かしらのユースケースがあるのでしょう。

ビューポートの大きさを元にした長さの単位、vwvhにも機能追加があります。モバイルではアドレスバーの表示状態でビューポートの大きさが変わってしまうことがあり、画面いっぱいに要素を広げるスタイルに難がありました。

それに対応するべく、ビューポートの最小時や最大時、動的に変化するビューポートを参照する単位、svw/svhlvw/lvhdvw/dvhなどが仕様に追加され、実装されています。

また、2021年はMacBook Proがモデルチェンジを行い、ProMotionという可変フレームレート技術を採用したディスプレイがMacに初搭載されました。スクロールやアニメーションなどがよりヌルヌルと動く期待が持てます。

しかし現在アニメーションの実装でよく使われているrequestAnimationFrame()に対し、そのまま可変フレームレートを適用してしまうと、バッテリー消費が上がる、一部サイトのアニメーションに影響が出てしまうといった問題があるようです。可変フレームレートでのアニメーションをよくすべく、AppleはWeb Animations仕様の拡張を提案しています。

ハードウェアの機能を活かしたプラットフォーム機能の提案は、Appleの垂直統合モデルならではのものと感じます。表現力の向上に寄与する提案を、今後も期待したいです。

カスケードレイヤーが全ブラウザでサポート

ここからが冒頭で言及した、CSS根幹を拡充する機能の紹介です。

CSSにはカスケーディングという、数あるスタイル宣言(プロパティと値のペア)から、どの値を使うかを解決する仕組みがあります。CSSのCはカスケーディング(cascading)からきており、CSSの根幹をなすものです。

カスケーディングでは、宣言の登場順(order of appearance)や、セレクタの詳細度(specificity)などいくつかの優先順位から、最終的に使われる宣言が決定されます。 詳細はMDNの記事などを読んでいただくとして、簡単に述べると、セレクタの詳細度が高いものが優先され、詳細度が同じ場合は登場順が後のものが優先されるようになっています。

CSSを書く際は、セレクタの詳細度や登場順を気にしてスタイル宣言を書いていくわけですが、サイトやアプリが大規模になればなるほど、現在のシンプルなカスケーディングでは立ち行かなくなります。サードパーティのコードのCSSによって、意図せず宣言が奪われることもあります。詳細度を下げコントロールを行いやすくするBEMなどの命名規約も発明されましたが、本質的な解決ではありませんでした。

そこに登場したのが、CSS Cascading and Inheritance Level 5仕様で提案された、カスケードレイヤー(Cascade Layers)です。セレクタの詳細度よりも優先されるレイヤー(layers)という概念を導入し、宣言の優先順位をコントロールしやすくする仕組みです。

レイヤーの優先度を工夫すると、たとえばIDセレクタをclassセレクタで上書きできるようになります。

/* <div id="A" class="B C"> という要素があったとして */
div { height: 100px }

@layer one {
  /* このレイヤーを見る限りは red になりそうだが */
  #A { background-color: red }
  .B { background-color: blue }
}
@layer two {
  /* このレイヤーのほうが優先度が高いので、green になる */
  .C { background-color: green }
}

またレイヤーは入れ子にしたり、優先度を別のところで定義できるなど、優先度の制御に自由が効くようになっています。とりあえず後ろにスタイルを書いていくといったことを、しなくてもよくなるのです。

/* レイヤーの優先度はあらかじめ定義できる */
@layer reset, base, components, util;

/* resetレイヤーにnormalize.cssをインポート */
@import url(normalize.css) layer(reset);

@layer util {
  /* utilレイヤー */
}
@layer base {
  /* baseレイヤー。一番後なので他のレイヤーよりも優先度が高そうだが、
     最初の@layerルールで優先度が決まっているので、utilのほうが優先される */
}

ほかにもいろいろな例がMDNの記事にありますので、読んでみてください。

カスケードレイヤーはかなり強力で、カスケーディングという根幹に触る機能なので、この仕様が提案された時に筆者は「かなりの時間がかかるだろうな」と思っていました。しかし2021年の半ばから各ブラウザで実装が始まり、年末にはリリース直前という段階まで達しました。Firefoxでは2月リリースのFirefox 97、Chromeでは3月リリースのChrome 99でリリース予定です。Safariでも開発版で有効になっており、遠くない時期にリリースされるでしょう。

仕様の提案が出てきたのが2019年の半ばだったので、2年で基本的な仕様の策定と、さらには実装も行われたことになります。驚くべき早さです。

:has()擬似クラスの実装

CSSで長らく望まれていたのが、親や祖先を選択するセレクタです。セレクタ仕様では:has()擬似クラスが以前から定義されていたのですが、クエリー対象となる要素が増え、パフォーマンスが悪化する懸念から、長年実装が行われていませんでした。

これが動いたのが昨年です。まず6月にChromiumでの実装がIgaliaにより始まりました。8月にはWebKitでも実装が始まり、昨年末リリースのSafari開発版で有効になりました。パフォーマンスの懸念についても、実装の工夫により抑えられているようです。すべてのブラウザとはいかないかもしれませんが、2022年に使えるブラウザが出てくることに期待したいです。

コンテナクエリーの実装も進む

レスポンシブなコードを書いているときに、画面幅ではなく、スタイルを適用するブロックの幅でスタイルを書き分けられたらと思うことはしばしばです。特にコンポーネントベースでUI部品をつくる昨今の開発においては、コンポーネント外の情報をなるべく考えずスタイルを書きたくなるため、メディアクエリーは筋が悪いとまで感じてしまうかもしれません。

こうした事情もあり、長年望まれてきた機能がコンテナクエリー(Container Queries)です。これはページ中のある要素をコンテナとして定義し、その幅に応じてスタイルを書き分けられる仕組みです。

/* コンテナの定義 */
main, .sidebar {
  container: inline-size;
}

/* デフォルトのカードスタイルは画像とテキストが縦に並ぶ */
.card {
  display: grid;
  grid-template:
    " image " auto
    "content" auto
    / 100% ;
}

/* コンテナの幅が400px以上の場合、画像とテキストを横並びに */
@container (width > 400px) {
  .card {
    grid-template:
      "image content" auto
      / auto 1fr ;
  }
}

記述したスタイルよっては、コンテナ要素とコンテナ内の要素が相互に参照しあうおそれもあります。これを回避するため、コンテナを定義した要素にはスタイルの封じ込め(containment)が行われます。封じ込めは、定義した要素の外に影響を及ぼさないと宣言することで、ブラウザにレンダリングの最適化余地を与える機能です。

コンテナクエリーは、封じ込めを定義するCSS Containment Level 3仕様で定義されています。将来的には幅だけでなく、スタイルやステートに応じたクエリーも書けることが提案されているなど、かなり夢のある仕様です。

実装ですが、Chromiumで2020年末から試験実装が始まっています。仕様の策定もまだ続いているので、早期にリリースされるわけではないでしょう。

ネスト、条件ブロック、スコープの仕様もドラフトが公開

CSSには様々な機能が求められています。要望の多くは、Sassや各種ライブラリ・ツールによって実現されてきた機能をCSSにも取り入れることです。そうした機能についても、仕様が公開され始めています。

構文の拡張で最も望まれているのが、ブロックのネストでしょう。これを実現するための仕様として、CSS Nesting仕様が提案され、昨年8月に最初のドラフトが公開されました。既存のCSSの構文との互換性から、Sassと全く同じ構文とはならなそうですが、導入されれば嬉しいと感じる方は多いでしょう。

メディアクエリーやフィーチャークエリーの拡張、そしてコンテナクエリーの登場により、複雑な条件をまとめる構文の需要も高まっています。昨年末に最初のドラフトが公開されたCSS Conditional Rules Level 5仕様では、@when@elseというルールが提案されています。

CSSの問題としてよく挙げられるスコープについても、CSS Cascading and Inheritance Level 6仕様で提案されており、昨年末に最初のドラフトが公開されました。提案されている@scopeルールを使うと、ある要素以下に対してのみスタイルを適用できるブロックを記述できます。また、要素ツリーの末端までではなく、ツリーの一部分にスコープを適用させることも検討されているようです。

これらはドラフトが出たばかりで、構文を含めまだまだこれからという状態です。すぐに実装され使えるというまでにはならないでしょうが、実現すればいまのCSSとは大きく異なるコードになりそうです。

CSSが大きく進み始めている

以上、CSSについて、今年サポートされそうな機能ほか、そう遠くないうちに来そうな機能、まだまだだけれど大きな変化を呼びそうな機能について紹介しました。

カスケードレイヤーのようにCSSの根幹に手を入れた機能が、もうすぐ使えるところまで来ています。渇望されてきた:has()擬似クラス、コンテナークエリーなども実装が始まっています。

絵に描いた餅だとずっと思っていたら、それがポンっと実体化してしまった。CSSにはそんな驚きが増えてきました。

実装が進んでいる機能は、効果的な使い方を探る段階に来ているのかもしれません。カスケードレイヤーは今後のCSS設計に大きな影響を与えるでしょうし、レイヤーのうまい切り分け方を考えはじめていくと良さそうです。

新しい機能は強力ですが、CSSの複雑さを高めることにもなります。:has()やコンテナクエリーなどは、よく考えて使わなければ、場当たり的で読みにくく管理性も悪いコードを生み出しかねません。今後は今よりもずっと、CSSの仕様や性質の理解が必要になっていくでしょう。

おすすめ記事

記事・ニュース一覧