Pagination

Pagination は、1画面に収まらない量の一覧コンテンツを複数のページに分割し、ユーザーが任意のページへ移動できるようにするナビゲーション系のコンポーネントです。より多くのコンテンツが存在することを示しながら、全体の中での現在位置の把握や、目的のページへの効率的なアクセスの手段を提供します。

1. 概要 (Overview)

説明

Pagination は、検索結果や管理画面のテーブルなど大量のデータを一覧表示する場面で、1ページあたりの情報量を制限します。情報過多による認知負荷や読み込み時間の増大を抑制するために使えます。

またページ番号は、全体の中の現在位置を示すランドマークとして機能します。「あのアイテムは3ページ目にあった」のように位置を記憶し、一度見たコンテンツへの再訪が容易になります。

Pagination は ListItemPrevious / NextTruncationCounter の要素で構成されます。

  • List: ページ番号の Item を横一列に並べる領域
  • Item: 個々のページへのリンク。現在位置を示す Item はリンクにしない
  • Previous / Next: 前後のページへ移動する要素
  • Truncation: 省略したページ番号の存在を示す記号(…)
  • Counter: 全体量と現在位置を示すテキスト(例: 100件中 1〜20件を表示)

リンク

  • Storybook(実装後に個別ストーリーへのリンクに差し替え)
  • GitHub(実装後に packages/pagination へのリンクに差し替え)

2. 構成要素 (Anatomy)

List

ページ番号の Item を横一列に並べる領域です。リスト(<ul> 要素)としてマークアップします。<ol> ではなく <ul> を用いるのは、Truncation でページ番号が飛ぶと <ol> の自動採番(1, 2, 3…)が実際のページ番号とずれてしまうためです。ページ番号は各 Item のテキストとして明示します。

必須/任意: 必須(Numbered 形式の場合)

Item

個々のページへのリンクです。原則として先頭ページと末尾ページの Item は常に表示し、目的のページへ直接移動できるようにします。

現在位置を示す Item はリンクにせず、テキストとして表示します。また、aria-current="page" を設定し、現在地であることが視覚以外でも伝わるようにします。

必須/任意: 必須(Numbered 形式の場合)

Previous / Next

前後のページへ移動する要素です。ページを1つずつ順に読み進める、Pagination の最も基本的な操作を担います。

List の <ul> には含めず、外側に配置してマークアップします。リストの読み上げに含まれる項目をページ番号の Item だけに揃え、種類の異なる操作である Previous / Next が混ざらないようにするためです。

先頭ページでは Previous を、末尾ページでは Next を出力しません(詳細は「5. ステート (States)」の Disabled を参照)。

必須/任意: 任意(Simple 形式では必須。端のページでの扱いは上記のとおり)

Truncation

表示しきれないページ番号を省略していることを示す記号(…)です。装飾的な要素であり、インタラクティブにはしません。省略されたページへは、近くのページ番号を経由するか、検索・絞り込みで到達します。

必須/任意: 任意

Counter

コンテンツの全体量と現在位置を示すテキストです(例: 「100件中 1〜20件を表示」「5 / 100」)。全体量が見えることで、ユーザーは「あとどれくらいあるのか」を判断できます。ページ番号を表示しない Simple 形式では、現在位置を伝える唯一の手がかりになるため特に重要です。

必須/任意: 任意(Simple 形式では推奨)

3. 使い分け (Usage)

いつ使うか

ユーザーが目的を持ってコンテンツを探す一覧(検索結果、管理画面のテーブル、ディレクトリなど)で使います。

  • 1ページに収めるには多すぎる件数(目安として25件超)を分割したい場面で使います。
  • 全体量の把握、離れたページへの移動、特定のページへの再訪が必要な場面に向きます。
  • 一覧の下部に配置します。
  • 合計が1ページに収まる場合は、Pagination 自体を表示しません。

いつ使わないか

  • 記事などの線形コンテンツを途中で分割しない: 1つの読み物を複数ページに分けると、クリックのコストが読了を妨げます。1ページの長いコンテンツとして提供します。
  • 受動的な閲覧が中心のコンテンツ: フィードやギャラリーのように、特定のゴールなしに同質のアイテムを次々に眺めるコンテンツでは、無限スクロールや「もっと見る」ボタンの方が閲覧の流れを妨げません。
  • 全体件数を取得できない実装のリスト: ページ番号の算出には全体件数の取得(オフセット型ページネーション)が前提になります。カーソル型ページネーションで実装されるリストでは、ページ番号を持たない Simple 形式や「もっと見る」ボタンを検討します。

代替手段との使い分け

一覧の続きを表示する手段は Pagination だけではありません。ユーザーの行動タイプと技術的な前提から選択します。

手段向いている場面留意点
Pagination (Numbered)目的探索型の一覧。位置の把握・再訪・離れたページへの移動が必要オフセット型の実装が前提。データ量が増えるとパフォーマンスに影響するため、採用時はエンジニアと要件を確認する
Pagination (Simple)前後の移動が中心の一覧。データテーブルのフッターなど離れたページへ直接移動できない。Counter で現在位置を補う
もっと見るボタン続きは見たいが全体を辿る必要がない一覧。フッターへの到達性を保ちたいページ全体量の手がかりが薄れるため件数表示を併用する
無限スクロールエンタメ・フィードなど受動的な閲覧。モバイル中心の体験位置の再訪が困難。フッターに到達できなくなる。キーボード・支援技術での利用が難しい

一覧から詳細ページへ移動して戻る操作(一覧と詳細の往復)が想定される場合は、どの手段でも、戻ったときに元のスクロール位置・ページ位置を復元します。先頭にリセットされると、ユーザーは見ていた場所を探し直すことになります。

類似コンポーネントとの違い

  • Tab: 並列な分類の切り替えに使います。分類ではなく、明確な序列(順序)を持つコンテンツの分割には Pagination を使います。

4. バリエーション (Variants)

Modifier class などの実装仕様は、inhouse-components-web への実装時に確定します。

Format

Numbered

ページ番号の List と Previous / Next を組み合わせた、Pagination のデフォルトの形式です。全体量の把握と任意のページへの移動を提供します。

Simple

Previous / Next と Counter のみで構成する形式です。ページ番号を持たないため、カーソル型ページネーションの実装でも使用できます。前後の移動が中心になるデータテーブルのフッターや、表示幅が限られるモバイルに向きます。

表示するページ番号の数

Numbered 形式では、表示するページ番号を画面幅に応じて制限し、超えた分は Truncation(…)で省略します。

  • 先頭ページと末尾ページは常に表示し、現在ページの前後に一定数のページ番号を表示します。
  • Truncation を含めた表示数の目安は、狭い画面で最大7、広い画面で最大14です。
  • 現在ページが先頭・末尾の両方から離れている場合は、両側に Truncation を置きます。
  • モバイルでは、現在ページの前後に表示する数を0〜1に絞るか、Simple 形式の使用を検討します。ページ番号のような小さなターゲットが水平に密集すると誤操作を招きやすいためです。

実装

実装のためのマークアップ例です。スタイル(Mixin-based / Class-based)は実装後に追記します。

Numbered

<nav aria-label="検索結果">
  <a href="?page=4">前のページ</a>
  <ul>
    <li><a href="?page=1">1</a></li>
    <li aria-hidden="true">…</li>
    <li><a href="?page=4">4</a></li>
    <li><span aria-current="page">5</span></li>
    <li><a href="?page=6">6</a></li>
    <li aria-hidden="true">…</li>
    <li><a href="?page=10">10</a></li>
  </ul>
  <a href="?page=6">次のページ</a>
</nav>

Simple

<nav aria-label="検索結果">
  <a href="?page=4">前のページ</a>
  <span>5 / 10</span>
  <a href="?page=6">次のページ</a>
</nav>

5. ステート (States)

Default

Item が現在ページではない通常の状態です。移動可能なリンクであることが伝わる見た目にします。

Hover

Item にマウスカーソルが重なっている状態です。背景色の変化などで操作可能であることを示します。

Focus

キーボード操作などでフォーカスを得たときの状態です。Focus Ring を表示します。

Current

Item が現在ページを示している状態です。リンクにせず、aria-current="page" を設定します。視覚的には色だけに頼らず、背景色や形状の変化を組み合わせて現在地を示します。

Disabled

Pagination は Disabled ステートを持ちません。先頭ページの Previous、末尾ページの Next は、非活性(disabled)にして残すのではなく出力しません。操作できない要素を置かないことで、支援技術のユーザーが無効なコントロールを辿る負担をなくします。

  • 出力しない場合も、要素分の領域は保持します。Previous / Next の消失によるレイアウトシフトと、連続操作中の誤クリックを防ぐためです。
  • URL が変わらない動的な読み込み(<button> での実装)では、端のページに到達した瞬間にフォーカス中の要素が消えることがあります。フォーカスが失われないよう、移動先(Counter や一覧の見出しなど)を定めます。

6. アクセシビリティ (Accessibility)

このコンポーネント固有のチェック項目です。コントラスト・フォーカス可視化など全コンポーネント共通の原則はアクセシビリティガイドラインを参照してください。

  • 全体を <nav> 要素で囲み、aria-label にはナビゲーションの対象が伝わる名前を設定する(例: 「検索結果」「記事一覧」)。支援技術が nav 要素を「ナビゲーション」と読み上げるため、「ページネーション」のような役割の重複する名前は避ける
  • 現在ページの Item に aria-current="page" を設定する
  • Previous / Next は List(<ul>)に含めずマークアップする(読み上げに含まれるリスト項目をページ番号の Item だけに揃え、種類の異なる操作が混ざらないように)
  • Truncation(…)は aria-hidden="true" を設定し、インタラクティブにしない
  • URL が変わる遷移には <a> 要素を、URL が変わらない動的な読み込みには <button> 要素を使う
  • アイコンのみの Previous / Next には accessible name を設定する。<a> / <button>aria-label を設定するか、アイコン自体に代替テキスト(<img>alt<svg>role="img" + aria-label など)を持たせる。視覚的に隠したテキストは、隠し方によって accessible name にならないことがあるため、それだけに頼らない
  • Item 同士の間隔とターゲットサイズを十分に確保する(数字のみのリンクはクリック対象が小さくなりやすいため)
  • 一覧にチェックボックスなどの選択状態がある場合、ページ移動で選択が保持されるかどうかをユーザーが予測・確認できるようにする

7. ライティング (Writing)

  • Previous / Next のラベルは、移動先が伝わる表現にする(例: 「前のページ」「次のページ」)。「戻る」「進む」はブラウザの履歴操作と混同されるため避ける
  • Counter は全体量と現在位置の両方が伝わる書式にする。Numbered 形式では件数を基準にし(例: 「100件中 1〜20件を表示」)、Simple 形式ではページ数を基準にする(例: 「5 / 100」= 現在ページ / 総ページ数)
  • ページ番号は数字のみで表記し、「1ページ」「P.1」のような単位は付けない