2026年7月23日
2026年7月23日
WordPressのテーマでメガメニューを実装するCSS/JSのサンプル(クラシックテーマ向け)
メガメニューは、プラグインを使わなくても「メニュー項目にクラスを付ける → CSSでサブメニューを横並びの大きなパネルにする → JavaScriptで開閉とキーボード操作を補う」の3段階で作れます。この記事では、クラシックテーマの wp_nav_menu() が出力するHTMLを前提に、子テーマに追加するだけで動くCSSとJavaScriptのサンプルを示し、テーマごとに調整が必要な箇所と動作確認の方法を説明します。
この方法が向いているケース
ここで扱うのは、管理画面の「外観」→「メニュー」で作ったメニューを表示するクラシックテーマです。こうしたテーマでは、メニューが ul.menu の中に li.menu-item が並び、子を持つ項目には menu-item-has-children クラス、子メニューには ul.sub-menu が付くという共通の構造で出力されます。この構造を手がかりにすれば、テーマが違ってもほぼ同じコードが使えます。
ブロックテーマのナビゲーションブロックは出力するHTMLもクラス名も異なるため、このサンプルはそのままでは動きません。ブロックテーマでメガメニューが必要な場合は、ブロックテーマ向けのメガメニュー系プラグインを検討するか、ナビゲーションブロックのサブメニューをCSSで広げる方向で考えます。管理画面から設定したい場合は Max Mega Menu のようなプラグインもありますが、この記事は「自分で書いて仕組みを把握したい」人向けの手作り方式に絞っています。
作業は子テーマで行い、始める前に子テーマのファイルをバックアップしてください。
ステップ1:メニューの構造を作り、クラスを付ける
メガメニューにしたい親項目の下に、2階層目として「列の見出し」になる項目を並べ、さらにその下の3階層目に実際のリンクを入れます。
- 製品(親項目。ここにクラスを付ける)
- サーバー(列1の見出し)
- 共用サーバー / VPS / 専用サーバー
- ドメイン(列2の見出し)
- 取得 / 移管 / DNS設定
- サポート(列3の見出し)
- よくある質問 / お問い合わせ
次に、親項目に mega-menu クラスを付けます。メニュー編集画面の右上にある「表示オプション」を開き、「CSSクラス」にチェックを入れると、各項目の設定欄にCSSクラスの入力欄が現れます。「製品」の欄に mega-menu と入力して保存します。
ここでつまずきやすいのが、テーマ側でメニューの階層数が制限されている場合です。wp_nav_menu() の depth 引数が 2 になっていると、3階層目は出力されません。保存後にサイトを開き、開発者ツールで li.mega-menu の中に ul.sub-menu が2段入っているか確認してください。3階層目が見当たらないときは、テーマの設定画面かテンプレートの wp_nav_menu() の呼び出しを確認します。
ステップ2:CSSでパネルを組む
子テーマに assets/css/mega-menu.css を作り、次の内容を書きます。
/* パネルの基準をナビゲーション全体にする */
.main-navigation {
position: relative;
}
.main-navigation .mega-menu {
position: static;
}
/* 2階層目をパネルとして隠しておく */
.main-navigation .mega-menu > .sub-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
z-index: 100;
width: auto;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1.5rem 2rem;
margin: 0;
padding: 2rem;
list-style: none;
background: #fff;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
/* 開いた状態(JSで is-open を付ける) */
.main-navigation .mega-menu.is-open > .sub-menu {
display: grid;
}
/* マウス操作ができる画面幅ではホバーとフォーカスでも開く */
@media (hover: hover) and (min-width: 960px) {
.main-navigation .mega-menu:hover > .sub-menu,
.main-navigation .mega-menu:focus-within > .sub-menu {
display: grid;
}
}
/* 3階層目は常に表示し、テーマのドロップダウン指定を打ち消す */
.main-navigation .mega-menu > .sub-menu .sub-menu {
display: block;
position: static;
box-shadow: none;
padding: 0;
margin-top: 0.5rem;
list-style: none;
}
.main-navigation .mega-menu > .sub-menu > li > a {
font-weight: bold;
}
/* スマートフォン幅では縦に積む */
@media (max-width: 959px) {
.main-navigation .mega-menu > .sub-menu {
position: static;
grid-template-columns: 1fr;
padding: 1rem;
box-shadow: none;
}
}
.mega-toggle {
margin-left: 0.25rem;
padding: 0.25rem 0.5rem;
background: none;
border: 0;
cursor: pointer;
}
.mega-toggle::before {
content: "▾";
}
.mega-toggle[aria-expanded="true"]::before {
content: "▴";
}
ポイントは位置の基準です。通常のドロップダウンは親の li を基準に開きますが、メガメニューはナビゲーションの横幅いっぱいに広げたいので、li.mega-menu を position: static にして、外側の .main-navigation を基準にしています。.main-navigation の部分は、使っているテーマでメニューを囲んでいる要素のクラス名に置き換えてください。開発者ツールで ul.menu の親要素を見れば分かります。
テーマ自身がドロップダウン用に .sub-menu へ width や left、opacity、visibility などを指定していると、上のCSSが効かないことがあります。パネルが見えないときは、開発者ツールで .sub-menu に当たっているCSSを一覧し、打ち消すべき指定を同じ詳細度以上のセレクターで上書きします。
ステップ3:JavaScriptで開閉ボタンとキーボード操作を補う
ホバーだけで開くメニューは、タッチ端末やキーボード操作では使いにくくなります。そこで、親項目の横に開閉ボタンを追加し、aria-expanded で状態を伝えるようにします。子テーマに assets/js/mega-menu.js を作ります。
document.addEventListener('DOMContentLoaded', function () {
var items = document.querySelectorAll('.mega-menu.menu-item-has-children');
if (!items.length) {
return;
}
function setOpen(item, open) {
var button = item.querySelector(':scope > .mega-toggle');
item.classList.toggle('is-open', open);
if (button) {
button.setAttribute('aria-expanded', open ? 'true' : 'false');
}
}
function closeAll(except) {
items.forEach(function (item) {
if (item !== except) {
setOpen(item, false);
}
});
}
items.forEach(function (item, index) {
var link = item.querySelector(':scope > a');
var panel = item.querySelector(':scope > .sub-menu');
if (!link || !panel) {
return;
}
panel.id = panel.id || 'mega-panel-' + index;
var button = document.createElement('button');
button.type = 'button';
button.className = 'mega-toggle';
button.setAttribute('aria-expanded', 'false');
button.setAttribute('aria-controls', panel.id);
// 読み上げ用のラベル。textContent で入れるのでHTMLとして解釈されない
var label = document.createElement('span');
label.className = 'screen-reader-text';
label.textContent = link.textContent.trim() + 'のサブメニュー';
button.appendChild(label);
link.after(button);
button.addEventListener('click', function () {
var willOpen = !item.classList.contains('is-open');
closeAll(item);
setOpen(item, willOpen);
});
});
// Escキーで閉じ、フォーカスを開閉ボタンに戻す
document.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') {
return;
}
items.forEach(function (item) {
if (item.classList.contains('is-open')) {
setOpen(item, false);
var button = item.querySelector(':scope > .mega-toggle');
if (button) {
button.focus();
}
}
});
});
// メニューの外をクリックしたら閉じる
document.addEventListener('click', function (event) {
if (!event.target.closest('.mega-menu')) {
closeAll(null);
}
});
});
ボタンのラベルを innerHTML ではなく textContent で入れているのは、メニュー名に記号が含まれていてもHTMLとして解釈させないためです。screen-reader-text は多くのテーマが用意している「画面には出さず読み上げだけさせる」ためのクラスですが、テーマにない場合は次のCSSを追加します。
.screen-reader-text {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
テーマによっては、すでにサブメニュー用の開閉ボタンを自前のJavaScriptで追加しています。その場合ボタンが二重になるので、テーマのボタンを使い、このサンプルからはボタン生成部分を外して is-open の付け外しだけに絞る、といった調整が必要です。
ステップ4:子テーマで読み込む
CSSとJavaScriptは wp_enqueue_scripts フックで読み込みます。ファイルの場所は子テーマを指す get_stylesheet_directory_uri() を使います(親テーマを指す get_template_directory_uri() と取り違えると404になります)。
<?php
// 子テーマの functions.php に追記
add_action( 'wp_enqueue_scripts', 'mychild_enqueue_mega_menu' );
function mychild_enqueue_mega_menu() {
$dir = get_stylesheet_directory();
$uri = get_stylesheet_directory_uri();
wp_enqueue_style(
'mychild-mega-menu',
$uri . '/assets/css/mega-menu.css',
array(),
filemtime( $dir . '/assets/css/mega-menu.css' )
);
wp_enqueue_script(
'mychild-mega-menu',
$uri . '/assets/js/mega-menu.js',
array(),
filemtime( $dir . '/assets/js/mega-menu.js' ),
true
);
}
バージョン番号に filemtime() でファイルの更新時刻を渡しているので、ファイルを編集するとURLの ver= が変わり、ブラウザの古いキャッシュが使われ続ける問題を避けられます。最後の true はスクリプトをフッターで読み込む指定です。
ステップ5:動作確認のチェックリスト
実装後は、次の操作をひととおり試します。
- パソコンでマウスを乗せるとパネルが開き、外すと閉じる
- Tabキーで「製品」→開閉ボタン→パネル内のリンクと移動でき、Escで閉じる
- スマートフォン幅で開閉ボタンをタップすると縦に並んだパネルが開く
- 開発者ツールで開閉ボタンの
aria-expandedがtrue/falseに切り替わる - パネルが本文やスライダーの下に隠れない(隠れる場合は
z-indexを調整)
キャッシュ系プラグインやCDNを使っている場合、CSSやJavaScriptを更新しても古いファイルが配信されることがあります。反映されないときはキャッシュを削除してから確認します。
まとめ
- メガメニューは、親項目に
mega-menuクラスを付け、2階層目を列見出し、3階層目をリンクにする構成で作ります。 - CSSでは
li.mega-menuをposition: staticにして、ナビゲーション全体を基準にパネルを広げます。セレクターはテーマの構造に合わせて置き換えます。 - JavaScriptで開閉ボタンと
aria-expanded、Escキーでのクローズを補うと、タッチ端末やキーボードでも使えます。 - 読み込みは子テーマの functions.php から
wp_enqueue_scriptsで行い、変更前にバックアップを取っておきます。 - このサンプルはクラシックテーマ向けです。ブロックテーマのナビゲーションブロックには別の方法が必要です(2026年10月現在)。