JSがないと無理だったUIが、CSSだけでできる時代に

スクロールするとヘッダーがスッと消えて、上に戻るとふわっと再表示される。 スマホサイトやSNSアプリ、企業サイトなどでよく見かけるこの動きは、長らく「JavaScriptでしか作れないUI」の代表例でした。

しかし最近、CSSだけで同じことを実現できる仕組みが登場し始めています。 それが Scroll State Queries(スクロール状態クエリ) と呼ばれる、モダンCSSの新機能です。

本記事では、 「なぜ今まではJSが必須だったのか?」「従来のJavaScript実装はどう書くのか?」 といった基礎部分をしっかり整理します。 次にCSSだけでヘッダー消えるUIを再現していきます。

Contents

なぜ今「CSSだけでUI」が注目されているのか?

最近のWeb業界では、「JavaScriptを使わずにCSSだけでどこまでUIを作れるか」が注目されています。 その背景には、以下のような理由があります。

  • パフォーマンスの向上:JSのscrollイベントやDOM操作は、スマホで重たくなる場合がある
  • コードの簡素化:UIのためだけにJavaScriptを書く量を減らしたい
  • 宣言的UIの流れ:HTML+CSSだけで「状態」を表現しようという流れ(例::has(), @layer, :checked など)
  • アクセシビリティの改善:CSSベースなら、JSエラーで壊れにくく、フォールバックもシンプル

こうした流れの中で登場したのが、Scroll-linked Animations APIScroll State Queries。 特に「scroll-state」は、まさに“CSSでJavaScriptの代わりをする”ような機能です。

なぜ従来はCSSだけで実装できなかったのか?

理由はシンプルで、CSSは「ユーザーのスクロール方向」や「スクロール量」を取得できなかったからです。

CSSで使えるのは「要素の状態」や「マウスホバー」などに限られており、 「今、ユーザーがどちらにスクロールしているか?」といった情報はJavaScriptでしか取得できませんでした。

つまり、今までの仕組み

  • スクロール量を window.scrollY で取得
  • 前回の値と比較して「上方向 or 下方向」を判定
  • ヘッダーに .hidden クラスをつけたり外したりする

こうしたロジックを、CSSだけで書くことは技術的に不可能でした。

【従来の方法】JavaScriptでヘッダーを隠す実装例

HTML/CSS/JavaScriptを1つにまとめています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Scroll Hide Header</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      height: 200vh; /* 動きを確認するために長めに */
    }
    header {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 60px;
      background: #fff;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      transition: transform 0.3s ease;
    }
    header.hide {
      transform: translateY(-100%);
    }
  </style>
</head>
<body>

<header id="site-header">
  My Website
</header>

<script>
  let lastScroll = 0;
  const header = document.getElementById('site-header');

  window.addEventListener('scroll', () => {
    const current = window.scrollY;
    if (current > lastScroll) {
      header.classList.add('hide');
    } else {
      header.classList.remove('hide');
    }
    lastScroll = current;
  });
</script>

</body>
</html>

JavaScriptでの実装にはどんな課題がある?

スクロール方向を検知してヘッダーを隠すJavaScript実装は一般的で、今でも多くのサイトで使われています。 しかし、実際の現場では次のような課題や欠点もあります。

① スクロールイベントは負荷が高くなることがある

window.addEventListener('scroll') は、スクロールするたびに何度も実行されます。 処理が重いと、スマホやスペックの低い環境ではカクつきにつながることがあります。

② コード量が増える・HTML+CSSの世界から離れる

ヘッダーの状態管理(表示/非表示)、クラスの付け外し、前回スクロール値の保持など… 見た目の制御のために、CSSではなくJavaScriptを追加で書かなくてはなりません。

③ 実装方法によっては“ガタつく”“ちらつく”ことも

スクロール位置の計算タイミングや、transformの値が合わない場合など、 表示/非表示の切り替わりでチラつき、ブレ、ワンテンポ遅れるといった問題が起きます。

④ CSSとJavaScriptの管理が分かれて保守しにくい

スタイルはCSSに、スクロール判定はJSに、と担当する領域が分かれるため、修正時に影響範囲を考える必要があります。 チーム制作や保守プロジェクトでは、別の人がJSを書き換えてデザイン崩れが起きるケースも珍しくありません。

CSSだけで実CSSだけで実現できる理由:Scroll State Queriesをもう少し詳しく

JavaScriptなしで「スクロールしたらヘッダーを隠す」UIを可能にするのが、モダンCSSの Scroll State Queries です。 これは CSS が「スクロールしている状態」や「スクロールの方向」までも判定できるようにする新しい仕様です。

まず前提として、CSSには次のような新機能が追加されつつあります。

  • container-type: scroll-state; → 要素のスクロール状態を監視できるようにする設定
  • @container scroll-state(…) → スクロール方向や位置に応じてCSSを切り替える

たとえば次のような書き方です

html {
  container-type: scroll-state;
}

@container scroll-state(scrolled: top) {
  /* ページの一番上にいるときだけ適用されるCSS */
}

@container scroll-state(scrolled: bottom) {
  /* 下方向へスクロールしたときに適用されるCSS */
}

従来のCSSでは、hover / focus / :checked のような「要素の状態」しか扱えませんでした。 しかしこの Scroll State Queries によって、CSSだけで「ページのスクロール状態」に反応できるようになります。ダーをCSSで隠す実装を書いていきます。

CSSだけでヘッダーを隠す実装例(JavaScriptなし)

いよいよ本題です。以下のコードは、スクロール方向に応じてヘッダーが隠れるUIをCSSだけで実装したコードです。

JavaScriptは一切使用していません。
Chrome Canary など scroll-state 対応ブラウザで動作します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>CSS Scroll State Header</title>
  <style>
    /* ① ページ全体を “スクロール状態を監視できるコンテナ” にする */
    html {
      container-type: scroll-state;
    }

    body {
      margin: 0;
      padding: 0;
      height: 200vh; /* スクロールを確認できるよう高さを確保 */
      font-family: sans-serif;
    }

    /* ② 基本のヘッダースタイル(固定表示) */
    header {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 60px;
      background: #ffffff;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      display: flex;
      align-items: center;
      padding: 0 20px;
      font-weight: bold;
      transition: transform 0.3s ease;
    }

    /* ③ Scroll State Queries:下方向にスクロール中ならヘッダーを隠す */
    @container scroll-state(scrolled: down) {
      header {
        transform: translateY(-100%);
      }
    }

    /* (任意)上方向スクロール中 → ヘッダーを再表示 */
    @container scroll-state(scrolled: up) {
      header {
        transform: translateY(0);
      }
    }
  </style>
</head>

<body>
  <header>
    CSS Only Header
  </header>

  <main style="padding: 80px 20px;">
    <h1>Scroll to Hide Header (CSS Only)</h1>
    <p>スクロールするとヘッダーがCSSだけで隠れます。上方向にスクロールすると再表示されます。</p>
    <p>対応ブラウザ:Chrome Canary / Safari Technology Preview など(実験的機能)</p>
  </main>
</body>
</html>

動作のポイント解説

ポイント内容
container-type: scroll-state;この1行で「スクロール状態を検知できるコンテナ」としてhtml要素を有効化
@container scroll-state(scrolled: down)ページが下方向へスクロールしているときだけCSSを適用
transform: translateY(-100%);ヘッダーを上に隠す(position: fixedと組み合わせる)
JavaScriptなしscrollY の計算やイベントリスナーが不要

従来のJS版とCSS版の比較

項目JavaScript版CSS版 Scroll State Queries
コード量JS+CSS両方必要CSSのみで完結
描画負荷scrollイベントが頻繁に発火 (場合によってカクつく)ブラウザ側で最適化されたスクロール検知
保守性JSとCSSが別ファイルになることが多いCSSファイルだけでUI制御できる
対応状況すべてのブラウザで動作まだ一部の実験的ブラウザのみ対応
実務で使えるか即戦力まだ学習・検証フェーズ向け

対応ブラウザは?今すぐ使えるのか問題

Scroll State Queries は非常に魅力的な機能ですが、2025年11月時点ではまだ一部のブラウザのみ対応です。

ブラウザ対応状況備考
Chrome (安定版)× 未対応今後実装予定
Chrome Canary◎ 対応 (フラグONで有効)chrome://flags → “Experimental Web Platform Features” を有効にする
Safari Technology Preview◎ 対応macOSの開発者向けブラウザ
Firefox× 未対応議論中・実装予定なし
iOS版ブラウザ× 未対応(Safari TPのみ)モバイル対応は今後

つまり、現時点では実運用ではなく「学習・検証フェーズ向け」の技術です。

非対応ブラウザではどうなる?壊れないの?

安心してください。Scroll State Queries に対応していないブラウザでは、以下のように動きます:

  • CSSの @container scroll-state(...) 部分が無視される
  • ヘッダーは「固定表示されたまま」動かない
  • つまり、UIが壊れるのではなく、普通の固定ヘッダーとして表示されるだけ

このように機能が無視されるだけでレイアウト崩壊しないため、CSSのプログレッシブ・エンハンスメントに非常に向いています。

フォールバック(JS版と併用するならどうする?)

「CSS対応ブラウザではScroll State Queriesで動かしたいけど、対応してないブラウザではJSを使う」という方法も可能です。

方法としては:

  • 1. CSSでScroll State Queriesを記述しておく(対応ブラウザ用)
  • 2. JavaScript側では @container が無視される環境のみ処理する
  • 3. CSS.supports() を使うと判定できる
if (!CSS.supports("container-type: scroll-state")) {
  // Scroll State CSSが使えないブラウザ用Fallback
  window.addEventListener('scroll', () => {
    // ここに従来のJS版処理を書く
  });
}

こうすることで、CSS対応ブラウザでは「CSSだけで動くUI」、非対応ブラウザでは「JSで動くUI」というハイブリッド構成にできます。

まとめ:CSSは“デザイン言語”から“UI制御の言語”へ

  • スクロールでヘッダーが消えるUIは、長らくJavaScriptでしか作れなかった
  • Scroll State Queries によってCSSだけでスクロール方向を検知できる時代が始まった
  • まだ実験的機能だが、先取りして学ぶ価値は非常に高い
  • CSSは「見た目を整えるもの」から、「インタラクションを制御するもの」へ進化している
  • URLをコピーしました!
Contents