プルして表示

「プルして表示」は、コンテンツ(検索バーや更新コントロールなど)が最初の読み込み時にスクロール可能な領域の上部に隠れており、ユーザーが下にプル(上にスクロール)すると表示される UI パターンです。このパターンは、モバイルアプリやウェブアプリで、検索バー、フィルタ、その他のセカンダリ コントロールなど、アクセス可能であるものの、すぐには表示されないようにする必要があるコントロールによく使用されます。

CSS プロパティ scroll-initial-target を使用すると、CSS のみでこのパターンを宣言的に実装できます。メイン コンテンツ要素に scroll-initial-target: nearest を設定すると、スクロール コンテナは、隠しコンテンツがスクロールされて表示されない状態でレンダリングされます。以前は、デベロッパーは JavaScript(Element.scrollIntoView())または URL フラグメント識別子(#content-id)を使用してこれを実現していましたが、どちらにも制限があり、実装が複雑でした。

実装方法

プルして表示パターンを実装するには:

  1. スクロール コンテナを確保する: ターゲット要素は、スクロール コンテナ(overflow: auto など、スクロールを可能にするオーバーフローがある要素)内にある必要があります。これは、ルート <html> 要素を含む任意の祖先要素にすることができます。
  2. 非表示要素を定義する: 非表示にするコンテンツ(検索バーなど)を、スクロール コンテナ内の最初の子孫として配置します。この要素は、最初の読み込み時にビュー外にスクロールされます。
  3. メイン コンテンツを定義する: メイン コンテンツ要素を非表示要素の直後に配置します。これは、ユーザーに最初に表示される要素です。
  4. メイン コンテンツをターゲットにする: メイン コンテンツ要素に scroll-initial-target: nearest を適用して、スクロール コンテナがスクロールされて表示され、その上の要素が非表示になるようにします。
  5. スクロール スナップを追加する: 非表示の要素が常に完全に表示されるか、完全に非表示になるように(部分的にスクロールされた状態にならないように)するには、スクロール コンテナに scroll-snap-type: y mandatory を追加し、非表示の要素とメイン コンテンツ要素の両方に scroll-snap-align: start を追加します。
/**
 * ANCESTOR: Define the scroll container.
 * Scroll snapping ensures the search bar is always
 * either fully visible or fully hidden.
 */
.scroll-container {
  height: 100vh;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
}

/**
 * HIDDEN ELEMENT: The search bar hidden above the fold on load.
 * It has scroll-snap-align so it snaps into place when pulled down.
 */
.search-bar {
  height: 60px;
  scroll-snap-align: start;
}

/**
 * MAIN CONTENT + TARGET: The element the user sees first.
 * scroll-snap-align makes it a valid snap point.
 * scroll-initial-target tells the browser to scroll here on
 * initial render, hiding the search bar above it.
 */
.main-content {
  scroll-snap-align: start;
  scroll-initial-target: nearest;
}

戦略的な実装とベスト プラクティス

  • 推奨: 読み込み時にスクロール可能領域の特定の部分にユーザーの注意を引き、最上部の検索バーなどの周辺 UI ユニットを意図的に非表示にする場合は、scroll-initial-target: nearest を使用します。
  • ユーザー補助フォーカスと混同しないでください。このプロパティは、ビジュアル ビューポートのみを移動します。キーボード フォーカスは移動しません。ターゲットがキーボード ユーザーの開始点となる場合は、element.focus() を手動で管理する必要があります。
  • 読み込み時にスムーズな「スクロール」アニメーションが必要な場合は、使用しないでください。このプロパティは離散的であり、レイアウト フェーズ中に位置を即座に設定します。
  • 同じスクロール可能なコンテナ内の複数の要素に scroll-initial-target を設定しないでください。複数の要素で scroll-initial-target: nearest が指定されている場合、ブラウザは DOM ツリーの順序で最初に表示される要素を選択します。
  • 優先順位を考慮する: URL フラグメント(例: example.com/#top)とコンテナレベルの scroll-start プロパティは、どちらも scroll-initial-target よりも優先されます。

代替戦略

API をまだサポートしていないブラウザでは、JavaScript のフォールバックを使用します。コンテンツをプルして表示する場合、メイン コンテンツをコンテナの start(上部)にバインドする必要があります。

/**
 * Progressive Enhancement Fallback
 */
document.addEventListener("DOMContentLoaded", () => {
  // Check for native CSS support
  if (!CSS.supports("scroll-initial-target", "nearest")) {
    const targetContent = document.querySelector('.main-content.target');
    if (targetContent) {
      // Use behavior: "instant" to mimic the native CSS behavior
      // 'block: start' should match your CSS 'scroll-snap-align' (or expected top position)
      targetContent.scrollIntoView({ behavior: 'instant', block: 'start' });
    }
  }
});