htmx 逆引きレシピ
ページングするには?

公開日:
最終更新日:

大量データの一覧は、全件を一度に表示せず「ページ単位」で分割すると、表示も操作も軽くなります。
htmxなら、ページ遷移なしで一覧だけを差し替えつつ、URLも更新して「戻る/共有」を効かせるページングが手軽に作れます。

このページでは「管理画面の大量一覧」「監査ログ一覧」「検索結果の分割表示」の3例で、hx-get / hx-target / hx-swap / hx-push-url を使った実務向けページングをデモ付きで解説します。
ページ番号は常に一定個数(例:5個)にして、UIがズレない工夫もあわせて紹介します。

※デモの関係上、hx-push-url="false"にしてます。

使用するhtmx属性

  • hx-get:ページ番号などの条件をGETで送り、該当ページのHTML(一覧+ページャ)を取得する
  • hx-target:返ってきたHTMLを差し替える先の要素(一覧コンテナ)を指定する
  • hx-swap:差し替え方を指定(例:innerHTMLで一覧コンテナの中身だけ更新)
  • hx-push-url:URLをページ番号付きに更新して、戻る/共有できる状態にする(例:true

利用シーン

  • 「管理画面の大量一覧」:ユーザー/申請/商品などの大量データを、軽快にページ分割で表示したい
  • 「監査ログ一覧」:増え続ける時系列ログを、ページ単位で追いやすく表示したい
  • 「検索結果の分割表示」:検索語+ページ番号をURLに乗せて、戻る/共有が効く検索UIにしたい

① 管理画面の大量一覧(ページング)

管理画面の大量一覧を、ページ単位で取得して表示する基本形です。
一覧だけ差し替えるので軽く、URLも更新するので「戻る/お気に入り」も効きます。
※デモの関係上、hx-push-url="false"にしてます。「戻る/お気に入り」をやりたい場合はhx-push-url="true"にしてください。

HTML

<div class="DEMO">

	<h4>① 管理画面の大量一覧(ページング)</h4>

	<div
		id="DEMO_ADMIN_LIST"
		hx-get="/htmx/demo/_paging_1.php?page=1"
		hx-trigger="load"
		hx-target="this"
		hx-swap="innerHTML"
		hx-push-url="false"
	>
		<span class="HTMX-NOTE">読み込み中...</span>
	</div>

</div>

PHP

<?php
// 型を厳密に扱う
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ関数
function h(string $s): string {
	// 特殊文字をエスケープする
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// GETのpageを受け取る(なければ1)
$pageRaw = (string)($_GET['page'] ?? '1');

// 前後空白を除去する
$pageRaw = trim($pageRaw);

// pageが数字でなければ1にする
if (!preg_match('/^\d+$/', $pageRaw)) $pageRaw = '1';

// pageを整数にする
$page = (int)$pageRaw;

// 1未満なら1にする
if ($page < 1) $page = 1;

// 1ページの表示件数
$perPage = 10;

// 全件数(デモ)
$total = 137;

// 総ページ数を計算する
$pages = (int)ceil($total / $perPage);

// pageが最大を超えたら最大に丸める
if ($page > $pages) $page = $pages;

// 先頭の開始位置(0始まり)
$offset = ($page - 1) * $perPage;

// このページの終了位置(含まない)
$end = min($offset + $perPage, $total);

// 見出しを表示する
echo '<div class="RESULT-HEAD">';

// タイトルを表示する
echo '<strong>管理画面の大量一覧</strong>';

// 件数表示を出す
echo ' ' . h((string)$total) . '件';

// ページ表示を出す
echo ' (' . h((string)$page) . '/' . h((string)$pages) . ')';

// 見出しを閉じる
echo '</div>';

// テーブル装飾用div開始
echo '<div class="TABLE_WRAPPER">';

// テーブル開始
echo '<table class="TABLE">';

// ヘッダ開始
echo '<thead><tr><th>ID</th><th>ユーザー名</th><th>権限</th><th>更新日</th></tr></thead>';

// 本体開始
echo '<tbody>';

// 1行ずつ生成して出す(デモ:DBのsliceの代わり)
for ($i = $offset + 1; $i <= $end; $i++) {

	// IDを作る
	$id = $i;

	// 名前を作る
	$name = 'User-' . str_pad((string)$i, 4, '0', STR_PAD_LEFT);

	// 権限を作る
	$role = ($i % 5 === 0) ? 'Admin' : 'Member';

	// 日付(ダミー)を作る
	$ymd = date('Y-m-d', strtotime('-' . ($i % 30) . ' days'));

	// 1行出力する
	echo '<tr>';

	// ID列
	echo '<td>' . h((string)$id) . '</td>';

	// 名前列
	echo '<td>' . h($name) . '</td>';

	// 権限列
	echo '<td>' . h($role) . '</td>';

	// 日付列
	echo '<td>' . h($ymd) . '</td>';

	// 行を閉じる
	echo '</tr>';
}

// tbody終了
echo '</tbody>';

// table終了
echo '</table>';

// div終了
echo '</div>';

// ページャ開始
echo '<nav class="PAGER" aria-label="ページング">';

// 前へリンク(1ページなら無効化)
if ($page > 1) {

	// 前ページ番号を作る
	$prev = $page - 1;

	// URLを作る
	$url = '/htmx/demo/_paging_1.php?page=' . $prev;

	// 前へリンクを出す(hx-getで取得して、push-urlでURL更新)
	echo '<a class="PAGER-BTN" href="' . h($url) . '" hx-get="' . h($url) . '" hx-target="#DEMO_ADMIN_LIST" hx-swap="innerHTML" hx-push-url="false">前へ</a>';

} else {

	// 無効ボタンを出す
	echo '<span class="PAGER-BTN is-disabled">前へ</span>';

}

// 表示するページ番号の固定数(常に5個)
$window = 5;

// 総ページ数が5未満の場合の開始ページ(1固定)
$start = 1;

// 総ページ数が5未満の場合の終了ページ(総ページ数)
$stop = $pages;

// 総ページ数が5以上なら、現在ページを中心に5個出す
if ($pages >= $window) {

	// 中心から左右に何個ずつ出すか(5なら2)
	$half = (int)floor($window / 2);

	// 仮の開始ページ(現在-2)
	$start = $page - $half;

	// 仮の終了ページ(開始+4)
	$stop = $start + ($window - 1);

	// 開始が1未満なら1に寄せる
	if ($start < 1) {

		// 開始を1にする
		$start = 1;

		// 終了を5にする
		$stop = $window;
	}

	// 終了が最大ページを超えたら末尾に寄せる
	if ($stop > $pages) {

		// 終了を最大ページにする
		$stop = $pages;

		// 開始を「終了-4」にする
		$start = $stop - ($window - 1);
	}
}

// 5個ウィンドウのページ番号を出す
for ($p = $start; $p <= $stop; $p++) {

	// URLを作る
	$urlp = '/htmx/demo/_paging_admin.php?page=' . $p;

	// 現在ページならspanで出す
	if ($p === $page) {

		// 現在ページ表示
		echo '<span class="PAGER-NUM is-current">' . h((string)$p) . '</span>';

	} else {

		// リンク表示(クリックでそのページへ)
		echo '<a class="PAGER-NUM" href="' . h($urlp) . '" hx-get="' . h($urlp) . '" hx-target="#DEMO_ADMIN_LIST" hx-swap="innerHTML" hx-push-url="true">' . h((string)$p) . '</a>';

	}
}

// 総ページ数が5未満なら、足りない分だけダミー枠を埋めて幅を固定する
if ($pages < $window) {

	// 足りない個数を計算する
	$pad = $window - $pages;

	// 足りない分だけダミーを出す
	for ($i = 0; $i < $pad; $i++) {

		// ダミー(クリック不可)を出す
		echo '<span class="PAGER-NUM is-disabled" aria-hidden="true">-</span>';
	}
}

// 次へリンク(最終ページなら無効化)
if ($page < $pages) {

	// 次ページ番号を作る
	$next = $page + 1;

	// URLを作る
	$urln = '/htmx/demo/_paging_1.php?page=' . $next;

	// 次へリンクを出す
	echo '<a class="PAGER-BTN" href="' . h($urln) . '" hx-get="' . h($urln) . '" hx-target="#DEMO_ADMIN_LIST" hx-swap="innerHTML" hx-push-url="false">次へ</a>';

} else {

	// 無効ボタンを出す
	echo '<span class="PAGER-BTN is-disabled">次へ</span>';

}

// nav終了
echo '</nav>';

デモ

① 管理画面の大量一覧(ページング)

読み込み中...

解説

  • 一覧コンテナにhx-getを付け、初回はhx-trigger="load"で自動読み込みします。
  • ページャのリンクは同じendpointへhx-getし、hx-targetで一覧コンテナだけを更新します。
  • hx-swap="innerHTML"で、コンテナの中身(一覧+ページャ)をまとめて差し替えます。
  • hx-push-url="true"でURLに?page=が反映され、戻る/共有ができるようになります。※デモの関係上、hx-push-url="false"にしてます。
  • PHP側はpageを受け取り、該当範囲の行を生成し、ページャも同時に返します。
  • ページ番号は常に一定数(例:5個)になるようにウィンドウ表示にして、前へ/次への位置がズレないようにします。

② 監査ログ一覧(ページング)

監査ログのように時系列で増え続ける一覧を、ページ単位で読み直す例です。
表示更新は一覧部分だけ、URL更新も行うので追跡しやすいUIになります。
※デモの関係上、hx-push-url="false"にしてます。「戻る/お気に入り」をやりたい場合はhx-push-url="true"にしてください。

HTML

<div class="DEMO">

	<h4>② 監査ログ一覧(ページング)</h4>

	<div
		id="DEMO_AUDIT_LIST"
		hx-get="/htmx/demo/_paging_2.php?page=1"
		hx-trigger="load"
		hx-target="#DEMO_AUDIT_LIST"
		hx-swap="innerHTML"
		hx-push-url="false"
	>
		<span class="HTMX-NOTE">読み込み中...</span>
	</div>

</div>

PHP

<?php
// 型を厳密に扱う
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ関数
function h(string $s): string {
	// 特殊文字をエスケープする
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// GETのpageを受け取る(なければ1)
$pageRaw = (string)($_GET['page'] ?? '1');

// 前後空白を除去する
$pageRaw = trim($pageRaw);

// pageが数字でなければ1にする
if (!preg_match('/^\d+$/', $pageRaw)) $pageRaw = '1';

// pageを整数にする
$page = (int)$pageRaw;

// 1未満なら1にする
if ($page < 1) $page = 1;

// 1ページの表示件数
$perPage = 12;

// 全件数(デモ)
$total = 420;

// 総ページ数を計算する
$pages = (int)ceil($total / $perPage);

// pageが最大を超えたら最大に丸める
if ($page > $pages) $page = $pages;

// 先頭の開始位置(0始まり)
$offset = ($page - 1) * $perPage;

// このページの終了位置(含まない)
$end = min($offset + $perPage, $total);

// 見出しを表示する
echo '<div class="RESULT-HEAD">';

// タイトルを表示する
echo '<strong>監査ログ一覧</strong>';

// 件数表示を出す
echo ' ' . h((string)$total) . '件';

// ページ表示を出す
echo ' (' . h((string)$page) . '/' . h((string)$pages) . ')';

// 見出しを閉じる
echo '</div>';

// テーブル装飾用div開始
echo '<div class="TABLE_WRAPPER">';

// テーブル開始
echo '<table class="TABLE">';

// ヘッダ開始
echo '<thead><tr><th>日時</th><th>操作</th><th>対象</th><th>操作者</th></tr></thead>';

// 本体開始
echo '<tbody>';

// 操作の候補(デモ)
$actions = ['LOGIN', 'UPDATE', 'DELETE', 'EXPORT', 'APPROVE'];

// 操作者の候補(デモ)
$actors  = ['tanaka', 'sato', 'suzuki', 'kato'];

// 1行ずつ生成して出す
for ($i = $offset + 1; $i <= $end; $i++) {

	// ログID(逆順の雰囲気を出す)
	$logNo = $total - $i + 1;

	// 日時(ダミー)
	$ts = date('Y-m-d H:i:s', strtotime('-' . $logNo . ' minutes'));

	// 操作を選ぶ
	$action = $actions[$logNo % count($actions)];

	// 対象を作る
	$target = 'REQ-' . str_pad((string)(10000 + $logNo), 5, '0', STR_PAD_LEFT);

	// 操作者を選ぶ
	$actor = $actors[$logNo % count($actors)];

	// 行を開始する
	echo '<tr>';

	// 日時列
	echo '<td>' . h($ts) . '</td>';

	// 操作列
	echo '<td><code>' . h($action) . '</code></td>';

	// 対象列
	echo '<td>' . h($target) . '</td>';

	// 操作者列
	echo '<td>' . h($actor) . '</td>';

	// 行を閉じる
	echo '</tr>';
}

// tbody終了
echo '</tbody>';

// table終了
echo '</table>';

// div終了
echo '</div>';

// ページャ開始
echo '<nav class="PAGER" aria-label="ページング">';

// 前へ
if ($page > 1) {

	// 前ページ
	$prev = $page - 1;

	// URL
	$url = '/htmx/demo/_paging_2.php?page=' . $prev;

	// 出力
	echo '<a class="PAGER-BTN" href="' . h($url) . '" hx-get="' . h($url) . '" hx-target="#DEMO_AUDIT_LIST" hx-swap="innerHTML" hx-push-url="false">前へ</a>';

} else {

	// 無効
	echo '<span class="PAGER-BTN is-disabled">前へ</span>';

}

// (ここまでの「前へ」出力の直後に入れる想定)

// 表示するページ番号の固定数(常に5個)
$window = 5;

// 総ページ数が5未満の場合の開始ページ(1固定)
$start = 1;

// 総ページ数が5未満の場合の終了ページ(総ページ数)
$stop = $pages;

// 総ページ数が5以上なら、現在ページを中心に5個出す
if ($pages >= $window) {

	// 中心から左右に何個ずつ出すか(5なら2)
	$half = (int)floor($window / 2);

	// 仮の開始ページ(現在-2)
	$start = $page - $half;

	// 仮の終了ページ(開始+4)
	$stop = $start + ($window - 1);

	// 開始が1未満なら1に寄せる
	if ($start < 1) {

		// 開始を1にする
		$start = 1;

		// 終了を5にする
		$stop = $window;
	}

	// 終了が最大ページを超えたら末尾に寄せる
	if ($stop > $pages) {

		// 終了を最大ページにする
		$stop = $pages;

		// 開始を「終了-4」にする
		$start = $stop - ($window - 1);
	}
}

// 5個ウィンドウのページ番号を出す
for ($p = $start; $p <= $stop; $p++) {

	// URLを作る
	$urlp = '/htmx/demo/_paging_admin.php?page=' . $p;

	// 現在ページならspanで出す
	if ($p === $page) {

		// 現在ページ表示
		echo '<span class="PAGER-NUM is-current">' . h((string)$p) . '</span>';

	} else {

		// リンク表示(クリックでそのページへ)
		echo '<a class="PAGER-NUM" href="' . h($urlp) . '" hx-get="' . h($urlp) . '" hx-target="#DEMO_ADMIN_LIST" hx-swap="innerHTML" hx-push-url="true">' . h((string)$p) . '</a>';

	}
}

// 総ページ数が5未満なら、足りない分だけダミー枠を埋めて幅を固定する
if ($pages < $window) {

	// 足りない個数を計算する
	$pad = $window - $pages;

	// 足りない分だけダミーを出す
	for ($i = 0; $i < $pad; $i++) {

		// ダミー(クリック不可)を出す
		echo '<span class="PAGER-NUM is-disabled" aria-hidden="true">-</span>';
	}
}
// 次へ
if ($page < $pages) {

	// 次ページ
	$next = $page + 1;

	// URL
	$urln = '/htmx/demo/_paging_2.php?page=' . $next;

	// 出力
	echo '<a class="PAGER-BTN" href="' . h($urln) . '" hx-get="' . h($urln) . '" hx-target="#DEMO_AUDIT_LIST" hx-swap="innerHTML" hx-push-url="false">次へ</a>';

} else {

	// 無効
	echo '<span class="PAGER-BTN is-disabled">次へ</span>';

}

// nav終了
echo '</nav>';

デモ

② 監査ログ一覧(ページング)

読み込み中...

解説

  • 一覧コンテナをhx-targetにして、ページ切り替え時はそこだけを更新します。
  • ページャリンクはhx-getで次のページを取得し、hx-swap="innerHTML"で差し替えます。
  • hx-push-url="true"でページ番号がURLに残るため、戻る操作やログ共有がスムーズになります。※デモの関係上、hx-push-url="false"にしてます。
  • PHP側はpageに応じて表示範囲を決め、ログ行+ページャをHTMLとして返します。
  • ページ番号の数を固定(例:5個)にすると、操作位置が毎回ズレず、監査ログのような連続閲覧で快適です。

③ 検索結果の分割表示(検索+ページング)

検索フォームの結果をページ分割して表示する例です。
検索語とページ番号をURLに残せるので、戻る/お気に入りが効く検索UIになります。
※デモの関係上、hx-push-url="false"にしてます。「戻る/お気に入り」をやりたい場合はhx-push-url="true"にしてください。

HTML

<div class="DEMO">

	<h4>③ 検索結果の分割表示(検索+ページング)</h4>

	<form
		id="DEMO_SEARCH_FORM"
		class="FORM"
		hx-get="/htmx/demo/_paging_3.php"
		hx-target="#DEMO_SEARCH_RESULT"
		hx-swap="innerHTML"
		hx-push-url="false"
	>
		<label>
			検索
			<input type="text" name="q" placeholder="例:申請">
		</label>

		<button type="submit" class="BTN">検索</button>
	</form>

	<div id="DEMO_SEARCH_RESULT" class="FORM-RESULT" aria-live="polite">
		<span class="HTMX-NOTE">キーワードを入力して検索してください。</span>
	</div>

</div>

PHP

<?php
// 型を厳密に扱う
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ関数
function h(string $s): string {
	// 特殊文字をエスケープする
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// GETのqを受け取る(なければ空)
$q = (string)($_GET['q'] ?? '');

// 前後空白を除去する
$q = trim($q);

// GETのpageを受け取る(なければ1)
$pageRaw = (string)($_GET['page'] ?? '1');

// 前後空白を除去する
$pageRaw = trim($pageRaw);

// pageが数字でなければ1にする
if (!preg_match('/^\d+$/', $pageRaw)) $pageRaw = '1';

// pageを整数にする
$page = (int)$pageRaw;

// 1未満なら1にする
if ($page < 1) $page = 1;

// キーワード未入力なら案内を返して終了する
if ($q === '') {

	// 案内を表示する
	echo '<span class="HTMX-NOTE">キーワードを入力して検索してください。</span>';

	// ここで終了する
	exit;
}

// 1ページの表示件数
$perPage = 8;

// (デモ)検索結果総数をそれっぽく決める(本番はDBの件数)
$total = max(12, 60 - (int)mb_strlen($q) * 3);

// 総ページ数を計算する
$pages = (int)ceil($total / $perPage);

// pageが最大を超えたら最大に丸める
if ($page > $pages) $page = $pages;

// 先頭の開始位置(0始まり)
$offset = ($page - 1) * $perPage;

// このページの終了位置(含まない)
$end = min($offset + $perPage, $total);

// 見出しを表示する
echo '<div class="RESULT-HEAD">';

// 検索語を表示する
echo '<strong>検索:</strong> ' . h($q);

// 件数表示
echo ' ' . h((string)$total) . '件';

// ページ表示
echo ' (' . h((string)$page) . '/' . h((string)$pages) . ')';

// 見出しを閉じる
echo '</div>';

// リスト開始
echo '<ol class="HTMX-LIST H375">';

// 1件ずつ出す(デモ:タイトルを生成)
for ($i = $offset + 1; $i <= $end; $i++) {

	// タイトルを作る
	$title = '検索結果 ' . $i . ':' . $q . ' に関する項目';

	// 1件を出す
	echo '<li>' . h($title) . '</li>';
}

// リスト終了
echo '</ol>';

// ページャ開始
echo '<nav class="PAGER" aria-label="ページング">';

// 前へ
if ($page > 1) {

	// 前ページ
	$prev = $page - 1;

	// URL(qを保持)
	$url = '/htmx/demo/_paging_3.php?q=' . rawurlencode($q) . '&page=' . $prev;

	// 出力
	echo '<a class="PAGER-BTN" href="' . h($url) . '" hx-get="' . h($url) . '" hx-target="#DEMO_SEARCH_RESULT" hx-swap="innerHTML" hx-push-url="false">前へ</a>';

} else {

	// 無効
	echo '<span class="PAGER-BTN is-disabled">前へ</span>';

}

// (ここまでの「前へ」出力の直後に入れる想定)

// 表示するページ番号の固定数(常に5個)
$window = 5;

// 総ページ数が5未満の場合の開始ページ(1固定)
$start = 1;

// 総ページ数が5未満の場合の終了ページ(総ページ数)
$stop = $pages;

// 総ページ数が5以上なら、現在ページを中心に5個出す
if ($pages >= $window) {

	// 中心から左右に何個ずつ出すか(5なら2)
	$half = (int)floor($window / 2);

	// 仮の開始ページ(現在-2)
	$start = $page - $half;

	// 仮の終了ページ(開始+4)
	$stop = $start + ($window - 1);

	// 開始が1未満なら1に寄せる
	if ($start < 1) {

		// 開始を1にする
		$start = 1;

		// 終了を5にする
		$stop = $window;
	}

	// 終了が最大ページを超えたら末尾に寄せる
	if ($stop > $pages) {

		// 終了を最大ページにする
		$stop = $pages;

		// 開始を「終了-4」にする
		$start = $stop - ($window - 1);
	}
}

// 5個ウィンドウのページ番号を出す
for ($p = $start; $p <= $stop; $p++) {

	// URLを作る
	$urlp = '/htmx/demo/_paging_admin.php?page=' . $p;

	// 現在ページならspanで出す
	if ($p === $page) {

		// 現在ページ表示
		echo '<span class="PAGER-NUM is-current">' . h((string)$p) . '</span>';

	} else {

		// リンク表示(クリックでそのページへ)
		echo '<a class="PAGER-NUM" href="' . h($urlp) . '" hx-get="' . h($urlp) . '" hx-target="#DEMO_ADMIN_LIST" hx-swap="innerHTML" hx-push-url="true">' . h((string)$p) . '</a>';

	}
}

// 総ページ数が5未満なら、足りない分だけダミー枠を埋めて幅を固定する
if ($pages < $window) {

	// 足りない個数を計算する
	$pad = $window - $pages;

	// 足りない分だけダミーを出す
	for ($i = 0; $i < $pad; $i++) {

		// ダミー(クリック不可)を出す
		echo '<span class="PAGER-NUM is-disabled" aria-hidden="true">-</span>';
	}
}

// 次へ
if ($page < $pages) {

	// 次ページ
	$next = $page + 1;

	// URL(qを保持)
	$urln = '/htmx/demo/_paging_3.php?q=' . rawurlencode($q) . '&page=' . $next;

	// 出力
	echo '<a class="PAGER-BTN" href="' . h($urln) . '" hx-get="' . h($urln) . '" hx-target="#DEMO_SEARCH_RESULT" hx-swap="innerHTML" hx-push-url="false">次へ</a>';

} else {

	// 無効
	echo '<span class="PAGER-BTN is-disabled">次へ</span>';

}

// nav終了
echo '</nav>';

デモ

③ 検索結果の分割表示(検索+ページング)

キーワードを入力して検索してください。

解説

  • フォームにhx-getを付け、入力されたqをGETで送って結果HTMLを取得します。
  • 結果はhx-targetで指定した結果エリアだけを差し替え、ページ全体は再読み込みしません。
  • ページャリンクはqを保持したURL(?q=...&page=...)をhx-getします。
  • hx-push-url="true"でURLが検索条件を反映し、戻る/共有・再訪問がしやすくなります。※デモの関係上、hx-push-url="false"にしてます。
  • PHP側はq未入力なら案内を返し、入力ありなら件数計算→該当範囲→ページャの順でHTMLを返します。
  • ページ番号は一定数(例:5個)に固定すると、検索結果をめくる操作でもボタン位置が安定します。

ページャーCSS

ページャーのCSSです。必要に応じてご使用ください。
※画面上はインデントがズレてますが、コピー時は揃えられてます。

CSS

<style>
/* =========================
	PAGER
========================= */
.PAGER{
	display					: flex;
	align-items				: center;
	justify-content			: center;
	gap						: 0.5rem;
	flex-wrap				: wrap;

	margin					: 1rem 0 0;
	padding					: 0.75rem 0.5rem;

	border-top				: 1px solid rgba( 0, 0, 0, 0.06 );
}

/* ボタン(前へ/次へ) */
.PAGER .PAGER-BTN{
	display					: inline-flex;
	align-items				: center;
	justify-content			: center;

	min-width				: 4.5rem;
	height					: var( --OBJ_HEIGHT );
	padding					: 0 0.9rem;

	background				: var( --WHITE );
	border					: 1px solid rgba( 0, 0, 0, 0.10 );
	border-radius			: 999px;

	color					: var( --TEXT, #222 );
	text-decoration			: none;

	cursor					: pointer;
	transition				: var( --TRANSITION );
}

/* ページ番号 */
.PAGER .PAGER-NUM{
	display					: inline-flex;
	align-items				: center;
	justify-content			: center;

	min-width				: var( --OBJ_HEIGHT );
	height					: var( --OBJ_HEIGHT );
	padding					: 0 0.65rem;

	background				: var( --WHITE );
	border					: 1px solid rgba( 0, 0, 0, 0.10 );
	border-radius			: 999px;

	color					: var( --TEXT, #222 );
	text-decoration			: none;

	cursor					: pointer;
	transition				: var( --TRANSITION );
}

/* 省略記号 */
.PAGER .PAGER-DOTS{
	padding					: 0 0.25rem;
	opacity					: 0.65;
}

/* hover/focus */
.PAGER .PAGER-BTN:hover,
.PAGER .PAGER-NUM:hover{
	transform				: translateY( -1px );
	box-shadow				: 0 6px 16px rgba( 0, 0, 0, 0.10 );
}

.PAGER .PAGER-BTN:focus-visible,
.PAGER .PAGER-NUM:focus-visible{
	outline					: 3px solid rgba( 245, 124, 0, 0.35 );
	outline-offset			: 2px;
}

/* 現在ページ */
.PAGER .PAGER-NUM.is-current{
	background				: rgba( 245, 124, 0, 0.12 );
	border-color			: rgba( 245, 124, 0, 0.35 );
	font-weight			: 700;

	cursor					: default;
	pointer-events			: none;
}

/* 無効(前へ/次へ) */
.PAGER .PAGER-BTN.is-disabled{
	opacity					: 0.45;

	cursor					: default;
	pointer-events			: none;
}

/* レスポンシブ微調整 */
@media ( max-width: 480px ){
	.PAGER{
		gap					: 0.4rem;
	}
	.PAGER .PAGER-BTN{
		min-width			: 4.0rem;
		padding				: 0 0.75rem;
	}
}
</style>

このページの著者

もちもちみかん(システムエンジニア)

社内SEとしてグループ企業向けの業務アプリを要件定義〜運用まで一気通貫で担当しています。

経験:Webアプリ/業務システム

得意:PHP・JavaScript・MySQL・CSS

個人実績:フォーム生成基盤クイズ学習プラットフォーム

詳しいプロフィールはこちら!  もちもちみかんのプロフィール

もちもちみかん0系くん
TOPへ

もちもちみかん.comとは


このサイトでは、コーディングがめんどうくさい人向けのお助けツールとして、フォームやCSSをノーコードで生成できる、
 もちもちみかん.forms
 もちもちみかん.css1
 もちもちみかん.css2
と言ったジェネレーターを用意してます。

また、このサイトを通じて、「もちもちみかん」のかわいさを普及したいとかんがえてます!