これまで、Webサイトの実務制作において「画面をスクロールしてヘッダーが上部に固定(sticky)されたら背景色をつける」「スクロールスナップで現在表示されている要素だけを強調する」といった演出を実装する場合、JavaScript(ScrollイベントやIntersection Observer)を使うのが当たり前でした。
しかし、CSSの最新仕様(コンテナークエリー)として登場した「scroll-state()」を使えば、これらをCSSだけで完結できるようになります。
今回は、フロントエンドで今最も注目されているこの最新機能について、実務で今すぐ使える具体的なコード例と合わせて解説します。
1. scroll-state() とは?何ができるの?
scroll-state() は、CSSコンテナークエリー(Container Queries)の拡張機能です。要素の「スクロールの状態(固定されているか、スナップされているか、スクロール可能か)」をブラウザが自動で検知し、その状態に応じたスタイルをCSSだけで適用できます。
指定できる主な状態(クエリー)には以下の4つがあります。
- stuck: position: sticky で要素が画面端に固定されている状態
- snapped: scroll-snap-type によって要素がピタッとスナップ停止している状態
- scrollable: 対象のエリアが上下左右にスクロール可能な状態(スクロールバーが出ているか)
- scrolled: ユーザーがどの方向にスクロールしたかの検知(スクロール方向の判定)
実務で特に需要が高い「stuck(固定検知)」と「snapped(スナップ検知)」の実装パターンを見ていきましょう。
2. 実装パターン①:ヘッダーが固定(sticky)されたら色を変える
もっとも一般的な「スクロールしたら追従ヘッダーの見た目(背景色や文字サイズ)を変える」実装です。
HTML構造
状態を監視するための親コンテナー(コンテナー要素)の中に、固定したいヘッダーを配置します。
<div class="nav-container">
<nav class="sticky-header">
<h1>Global Navigation</h1>
</nav>
</div>
CSSコード
親要素に container-type: scroll-state を指定して監視対象にし、子要素側で @container scroll-state(stuck: top) を使って固定時のスタイルを定義します。
/* 1. 親要素をスクロール状態の監視コンテナーに設定 */
.nav-container {
container-type: scroll-state;
}
/* 2. 通常時のヘッダースタイル(上部にsticky配置) */
.sticky-header {
position: sticky;
top: 0;
background-color: transparent; /* 初期は透明 */
transition: background-color 0.3s ease;
}
/* 3. 上部(top)に固定(stuck)された瞬間に適用するスタイル */
@container scroll-state(stuck: top) {
.sticky-header {
background-color: #ffffff; /* 固定されたら白背景にする */
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* 影をつける */
}
}
3. 実装パターン②:スクロールスナップで中央にある要素を強調する
カルーセル(スライダー)や縦スクロールの全画面演出で、現在アクティブ(ピタッと止まっている)パネルだけを大きくしたり、文字を表示したりする演出です。
HTML構造
スクロールさせる外枠のコンテナーと、その中にある各セクションを用意します。
<div class="timeline-container">
<section class="card">Card 1</section>
<section class="card">Card 2</section>
<section class="card">Card 3</section>
</div>
CSSコード
親にスクロールスナップの設定と、コンテナーの設定を両方持たせることで、個々の子要素がスナップされたかを検知できます。
/* 1. スクロールスナップの親コンテナーを設定 */
.timeline-container {
scroll-snap-type: y mandatory;
overflow-y: scroll;
height: 400px;
container-type: scroll-state; /* 監視コンテナー化 */
}
/* 2. 各カードの通常スタイル */
.card {
scroll-snap-align: center;
opacity: 0.5;
transform: scale(0.9);
transition: all 0.3s ease;
}
/* 3. y軸(縦方向)にスナップ(停止)した要素に適用するスタイル */
@container scroll-state(snapped: y) {
.card {
opacity: 1;
transform: scale(1); /* アクティブな要素だけ拡大・強調 */
}
}
4. 実務導入における注意点とブラウザ対応状況
この scroll-state() は非常に強力ですが、実務に投入する際は以下の点に留意する必要があります。
- ブラウザの対応状況: 主要ブラウザ(Chromium系、Safari、Firefox)で順次実装が進んでいますが、古い環境のユーザー向けには「機能しなくてもレイアウトが崩れない(Progressive Enhancement)」設計にする必要があります。
- JavaScriptとの使い分け: アニメーションのトリガーにするだけであればCSSで十分ですが、スクロール状況をサーバーにログ送信したり、複雑な条件分岐を行ったりする場合は、従来通りJavaScript(Intersection Observer)との併用が必要です。
まとめ:CSSによる表現力は次のステージへ
これまでは「重い」「記述が面倒」と言われがちだったスクロール連動演出ですが、scroll-state() の登場によって、コード量は圧倒的に削減され、パフォーマンスもネイティブブラウザ準拠で非常に滑らかになります。
Web制作の工数削減とパフォーマンス向上を両立できる素晴らしい機能です。モダンブラウザの普及に合わせて、ぜひ皆さんのプロジェクトでも先取りして試してみてください。

