スクロールの状態にマッチする3つ目のコンテナクエリー「Scroll State Container Queries」
UI開発者 加藤コンテナークエリーにはコンテナー要素のサイズに応じてスタイルを変更するサイズクエリーと、コンテナー要素のスタイルに応じてスタイルを変更するスタイルクエリーの2種類があります。実はもう1つ「Scroll State Container Queries」が追加されようとしています。
※ 現在はまだEditor's Draftの段階で正式な仕様ではなく、この記事で書く内容は変更される可能性がありますのでご注意ください。
Scroll State Container Queriesはコンテナー要素のスクロールに基づくさまざまな状態にマッチするコンテナークエリーです。(以降はスクロールクエリーと書きます)
スクロールクエリーでは以下の3種類の状態にマッチします。
stuck
snapped
scrollable
Google ChromeのDevRelとして活動されているAdam Argyle氏がCodePenでデモを公開されていたので、そちらも掲載しつつ1つずつ簡単に説明します。スクロールクエリーはGoogle Chromeのデベロッパー向けビルドであるChrome Canaryで実装されていますので、気になる方はChrome Canaryをインストールして試してみてください。
stuck
stuckは、position: stickyを指定したコンテナー要素が、指定した方向に吸着している状態にマッチします。
@container scroll-state(stuck: top) {
/* コンテナが上部に吸着している状態のスタイル */
}
sticky
な要素が吸着しているかどうかによってスタイルを変えるには、Intersection Observerなどを使ってJavaScriptでクラスの付け替えをする必要がありましたが、スクロールクエリーが実装されればCSSだけで実現できるようになります。
See the Pen Sticky State Queries - Nav Shadow On Stuck by Adam Argyle (@argyleink) on CodePen.
snapped
snapped
はスクロールスナップによってスナップした状態にマッチします。
@container scroll-state(snapped: x) {
/* x軸方向にスナップしている状態のスタイル */
}
stuck
と同様にスナップした領域をハイライトしたり、コンテンツの表示を切り替えたりしたい場合はJavaScriptでscrollsnapchange
イベントなどを使う必要がありましたが、CSSだけで実現できるようになります。
See the Pen snapChanging triggered captions - CSS container query by Adam Argyle (@argyleink) on CodePen.
scrollable
scrollable
はコンテナー要素が指定した方向にスクロール可能な状態にマッチします。
@container scroll-state(scrollable: bottom) {
/* 下方向にスクロール可能な状態のスタイル */
}
例えば、ボックスの縁にグラデーションをつけるなど、スクロールクエリーを使うことで視覚的にスクロールが可能なことをユーザーに伝えるスタイルを適用できます。
元のデモが古い仕様で作られていたため、当社のアカウントでForkしつつ、新しい仕様に合わせて修正しました。
See the Pen Scrollable Visual Affordances with Scroll State Queries by ミツエーリンクス (@mitsue-links) on CodePen.
なぜコンテナークエリーなのか?
私は以前からposition: sticky
な要素が吸着している状態かどうかをCSSで判定したいと思っており、:stuck
のような擬似クラスがあればいいのになーと考えていましたが、追加されたのはコンテナークエリーでした。
なぜ擬似クラスではなくコンテナークエリーなのかについてはExplainerに記載があります。さまざまな理由があるようですが、特に問題になりやすいのがブラウザ内部のレイアウト処理との関係です。
例えば、以下のようなHTMLがあるとします。
<div class="parent">
<div class="container">
<div class="child"></div>
</div>
</div>
もし:stuck
のような擬似クラスだったとすると、子要素だけでなく、自分自身、兄弟要素、さらにはhas()
と組み合わせることで先祖要素にさかのぼってスタイルを変更することができます。
.parent:has(.container:stuck) {
background-color: red;
}
柔軟なスタイル設定ができるため良いように見えますが、柔軟性が高いことでブラウザ側のレイアウト処理が複雑になり、無限ループに陥りやすいという問題があります。以下は擬似クラスだとこんなこともできてしまうという極端な例です。
.container {
position: sticky;
}
.container:stuck {
position: static;
}
対してコンテナークエリーの場合はコンテナー要素の子孫要素のみに限定することができます。
.container {
container-type: scroll-state;
}
@container scroll-state(stuck: top) {
.parent { /* 親要素のCSS宣言を書いても反映されない */
background-color: red;
}
}
コンテナークエリーを使えば全ての問題が解決するわけではないと思いますが、container-typeを使うことで無限ループを軽減し、ブラウザ内部のレイアウトロジックをシンプルに保つことができます。
まとめ
前述した通り、Scroll State Container QueriesはまだEditor's Draftの段階であり、正式な仕様ではありません。Chrome Canaryでは実装されましたが、他のブラウザでは実装の予定もまだないようです。
しかし、このコンテナークエリーが実装されればCSSだけで表現できる幅が広がり、JavaScript実行時のレイアウト処理が減ることでパフォーマンスの向上、さらにはユーザビリティの向上にもつながるため、期待したいと思います。