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>
参考リンク
このページの著者
経験:Webアプリ/業務システム
得意:PHP・JavaScript・MySQL・CSS
個人実績:フォーム生成基盤/クイズ学習プラットフォーム 等
詳しいプロフィールはこちら! もちもちみかんのプロフィール