パンくずリストは、プラグインなしで作れます。functions.phpに関数を1つ用意して、テンプレートから呼ぶだけ。プラグインに依存しないので、テーマを配布・納品するときも安心です。
まずは完成コードから。
/**
* パンくずリストを出力する。
* 各項目は ['label' => 表示名, 'url' => リンク先(最後の項目はnull)]
*/
function my_breadcrumb() {
if (is_front_page()) {
return; // トップでは出さない
}
$items = [['label' => 'ホーム', 'url' => home_url('/')]];
if (is_home()) {
// 投稿一覧
$items[] = ['label' => get_the_title(get_option('page_for_posts')) ?: 'お知らせ', 'url' => null];
} elseif (is_category() || is_tag() || is_tax()) {
$items[] = ['label' => single_term_title('', false), 'url' => null];
} elseif (is_post_type_archive()) {
$items[] = ['label' => post_type_archive_title('', false), 'url' => null];
} elseif (is_search()) {
$items[] = ['label' => '「' . get_search_query() . '」の検索結果', 'url' => null];
} elseif (is_404()) {
$items[] = ['label' => 'ページが見つかりません', 'url' => null];
} elseif (is_page()) {
// 親ページをたどる
foreach (array_reverse(get_post_ancestors(get_the_ID())) as $ancestor) {
$items[] = ['label' => get_the_title($ancestor), 'url' => get_permalink($ancestor)];
}
$items[] = ['label' => get_the_title(), 'url' => null];
} elseif (is_singular()) {
$type = get_post_type();
if ($type === 'post') {
// 投稿はカテゴリを1つ挟む
$cats = get_the_category();
if ($cats) {
$items[] = ['label' => $cats[0]->name, 'url' => get_category_link($cats[0]->term_id)];
}
} else {
// カスタム投稿タイプは一覧ページを挟む
$obj = get_post_type_object($type);
if ($obj && $obj->has_archive) {
$items[] = ['label' => $obj->labels->name, 'url' => get_post_type_archive_link($type)];
}
}
$items[] = ['label' => get_the_title(), 'url' => null];
}
// 出力
echo '<nav class="breadcrumb" aria-label="現在位置"><ol>';
foreach ($items as $item) {
echo '<li>';
if ($item['url']) {
echo '<a href="' . esc_url($item['url']) . '">' . esc_html($item['label']) . '</a>';
} else {
echo '<span aria-current="page">' . esc_html($item['label']) . '</span>';
}
echo '</li>';
}
echo '</ol></nav>';
}テンプレートからは1行で呼び出します。
<?php my_breadcrumb(); ?>コードの考え方
やっていることはシンプルです。
- 配列を組み立てる——「ホーム」から始めて、階層順に項目を足す
- 最後の項目はリンクにしない(今いるページなので)
- まとめて出力する
表示するHTMLと組み立てを分けているのがポイントです。olとliで階層を表し、aria-labelとaria-currentを付けておくと、読み上げでも位置が伝わります。
CSSを当てる
.breadcrumb ol {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
list-style: none;
padding: 0;
margin: 0 0 1.5rem;
font-size: 0.875rem;
color: #666;
}
.breadcrumb li + li::before {
content: "›";
margin-right: 0.5em;
color: #aaa;
}
.breadcrumb a { color: inherit; }区切り記号はCSSの::beforeで入れるのがコツです。HTMLに>を書きこむと、読み上げで余計に読まれたりコピー時に混ざったりします。
構造化データを付ける
パンくずをGoogleに理解させると、検索結果にパンくずが表示されることがあります。JSON-LDで出力します。
function my_breadcrumb_jsonld($items) {
$list = [];
foreach ($items as $i => $item) {
$entry = [
'@type' => 'ListItem',
'position' => $i + 1,
'name' => $item['label'],
];
if ($item['url']) {
$entry['item'] = $item['url'];
}
$list[] = $entry;
}
$data = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $list,
];
echo '<script type="application/ld+json">'
. wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
. '</script>';
}上のmy_breadcrumb()の中で、HTMLを出力したあとにこの関数を呼びます。
投稿一覧ページの名前を正しく出す
「設定」→「表示設定」で投稿ページを固定ページに割り当てている場合、その固定ページのタイトルをパンくずに使うのが自然です。
$blog_id = (int) get_option('page_for_posts');
$label = $blog_id ? get_the_title($blog_id) : 'お知らせ';投稿の詳細ページでも、カテゴリの前にこの階層を挟むとより正確になります(サイトの構造に合わせて調整してください)。
プラグインを使う場合との比較
- 自作——出力するHTMLを完全に決められる。プラグインが増えない。テーマに同梱できる
- プラグイン——設定画面で調整できる。分岐の作りこみが不要
SEO系プラグインにはパンくず機能が含まれていることが多いので、すでに入れているなら重複させないのが大事です。自作するなら、プラグイン側の機能はオフにしておきます。
まとめ
- パンくずは
functions.phpに関数1つで自作できる。プラグイン依存を増やさない - 配列に階層を積んで、最後の項目だけリンクなし——これが実装の型
ol+aria-currentでマークアップし、区切り記号はCSSの::beforeで入れる- 構造化データ(BreadcrumbList)を付けると検索結果に出ることがある。表示内容と一致させる
今どこにいるかが分かる導線は、回遊率にも直結します。