WordPress のテーマでナビゲーションを出力する中核 API が wp_nav_menu()
です。メニューの登録、設置、クラス名の調整、アクセシビリティ対応、マークアップの置き換え(walker
)まで、制作現場でよく使う手順を順番にまとめます。
メニューの「設置場所」を登録する
まずは管理画面の「外観 → メニュー」で割り当てられる「テーマの場所」を用意します。functions.php
に登録処理を書き、ヘッダーやフッターなど複数のロケーションを定義します。
<?php
add_action('after_setup_theme', function () {
register_nav_menus([
'header' => 'ヘッダーナビ',
'footer' => 'フッターナビ',
]);
});
テンプレートで出力する(最小構成)
登録した場所に紐づくメニューを、テンプレート(例:header.php
)で出力します。最小限なら以下の一行で OK です。
<?php
wp_nav_menu([
'theme_location' => 'header',
]);
HTML 構造とクラス名を整える
BEM などの命名に合わせたいときは、ラッパー要素やリストのクラス、コンテナ有無を指定します。container
を false
にすると余計な div を出しません。
<?php
wp_nav_menu([
'theme_location' => 'header',
'container' => false,
'menu_class' => 'gnav__list',
'menu_id' => 'gnav',
'depth' => 2, // ドロップダウンまで
]);
メニューが未設定のときの挙動を制御する
本番で空のナビを見せないために、未割り当て時の「フォールバック」を止めたり、任意のメッセージを表示します。
<?php
wp_nav_menu([
'theme_location' => 'header',
'container' => false,
'fallback_cb' => '__return_empty_string', // 何も出さない
]);
任意のラッパーマークアップに置き換える(items_wrap)
デフォルトは <ul id="%1$s" class="%2$s">%3$s</ul>
です。必要なら nav
と組み合わせた独自構造に差し替えます。
<?php
wp_nav_menu([
'theme_location' => 'header',
'container' => 'nav',
'container_class'=> 'gnav',
'items_wrap' => '<ul class="gnav__list" role="list">%3$s</ul>',
]);
現在ページのハイライトを制御する
WordPress は自動で current-menu-item
などを付与します。追加で自前のクラスを足したいときはフィルターで調整します。
<?php
add_filter('nav_menu_css_class', function ($classes, $item, $args, $depth) {
if (in_array('current-menu-item', $classes, true)) {
$classes[] = 'is-active';
}
return $classes;
}, 10, 4);
サブメニュー(ドロップダウン)のマークアップを整える
サブメニューは .menu-item-has-children
と .sub-menu
が自動で出力されます。アクセシビリティのためにトグルボタンと aria-expanded
を持たせると操作が明確になります。最も簡単なのは「リンクの後ろにトグルボタンを差し込む」方法です。
<?php
add_filter('walker_nav_menu_start_el', function ($item_output, $item, $depth, $args) {
if (in_array('menu-item-has-children', $item->classes ?? [], true) && $depth === 0) {
$button = '<button class="gnav__toggle" aria-expanded="false" aria-label="サブメニューを開閉"><span aria-hidden="true">▼</span></button>';
$item_output = preg_replace('/<a[^>]*>.*?<\/a>/s', '$0' . $button, $item_output, 1);
}
return $item_output;
}, 10, 4);
独自マークアップが必要な場合の最終手段(walker)
Walker_Nav_Menu
を継承して「各 li / a / ul の出力」を完全に制御できます。まずは最小限の骨格を作り、必要な断面だけ上書きします。
<?php
class My_Gnav_Walker extends Walker_Nav_Menu {
public function start_lvl(&$output, $depth = 0, $args = null) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul class=\"gnav__submenu\" role=\"list\">\n";
}
public function start_el(&$output, $item, $depth = 0, $args = null, $id = 0) {
$classes = implode(' ', array_filter($item->classes));
$output .= "<li class=\"gnav__item {$classes}\">";
$output .= '<a class="gnav__link" href="' . esc_url($item->url) . '">'
. esc_html($item->title) . '</a>';
}
public function end_el(&$output, $item, $depth = 0, $args = null) {
$output .= "</li>\n";
}
}
作成した Walker は 'walker' => new My_Gnav_Walker()
を渡して有効化します。
<?php
wp_nav_menu([
'theme_location' => 'header',
'container' => false,
'walker' => new My_Gnav_Walker(),
]);
SVG アイコンや外部リンク判定を差し込みたい
メニュー項目が外部サイトならアイコンを付けたい、といった要件は walker_nav_menu_start_el
フィルターで実現できます。parse_url()
でホストを比較し、該当時のみ SVG を追記します。
<?php
add_filter('walker_nav_menu_start_el', function ($item_output, $item) {
$host = wp_parse_url(home_url(), PHP_URL_HOST);
$link = wp_parse_url($item->url, PHP_URL_HOST);
if ($link && $link !== $host) {
$icon = '<svg class="icon-external" width="12" height="12" aria-hidden="true">...</svg>';
$item_output = str_replace('</a>', ' ' . $icon . '</a>', $item_output);
$item_output = str_replace('<a ', '<a target="_blank" rel="noopener" ', $item_output);
}
return $item_output;
}, 10, 2);
ブロックテーマ(FSE)でも使えるのか
サイトエディター主体のブロックテーマでも、PHP テンプレートを併用するなら wp_nav_menu()
は利用できます。完全にブロック化した構成では「ナビゲーション」ブロックが推奨ですが、細かい制御が必要な場合や後方互換を考えるなら従来 API を使う設計も現実的です。
パフォーマンスとアクセシビリティの注意点
巨大な多階層メニューは DOM が重くなりやすく、モバイルでは展開制御の JavaScript が描画を遅らせることがあります。初期表示は 1 階層に留め、サブメニューはトグルで遅延展開する方針が安全です。キーボード操作の配慮として、トグルボタンは aria-expanded
の状態変化を伴わせ、フォーカスリングを CSS で隠しすぎないようにします。
よく使う引数の早見(説明のみ)
theme_location
は登録した場所を指定、menu
は特定 ID/名前のメニューを直接出力、container
と container_class
はラッパー要素の制御、menu_class
と menu_id
は ul の属性、depth
は階層の深さ、fallback_cb
は未割り当て時のコールバック、items_wrap
は ul テンプレート、walker
は完全カスタムを行うクラス指定、という役割です。
まとめと実用テンプレート
最低限の設置、BEM 風クラス、フォールバック抑止、アクセシビリティ配慮を兼ねた現場向けテンプレートは次の通りです。まずはこれをベースに、必要になった段階で items_wrap
とフィルターや walker を追加していくと、安全に拡張できます。
<?php
// functions.php
add_action('after_setup_theme', function () {
register_nav_menus(['header' => 'ヘッダーナビ']);
});
add_filter('nav_menu_css_class', function ($classes) {
if (in_array('current-menu-item', $classes, true)) $classes[] = 'is-active';
return $classes;
}, 10, 1);
// header.php
?>
<nav class="gnav" aria-label="グローバルナビ">
<?php
wp_nav_menu([
'theme_location' => 'header',
'container' => false,
'items_wrap' => '<ul class="gnav__list" role="list">%3$s</ul>',
'menu_class' => 'gnav__list',
'fallback_cb' => '__return_empty_string',
'depth' => 2,
]);
?>
</nav>
wp_nav_menu()
は「登録 → 出力 → 微調整 → 必要なら walker」の順に組み立てると迷いません。まずはコンテナやクラスの整形から着手し、挙動の変更はフィルター、構造の置き換えは walker で段階的に行うのがメンテナンス性の高い実装です。