ハンバーガーメニュー(Hamburger Menu)— スライドインナビ

ナビゲーション 中級

このコンポーネントについて

ハンバーガーメニューは、画面上部のナビゲーションバーに3本線アイコン(☰)を表示し、クリック・タップするとメニューパネルがスライドインして展開するUIパターンです。スマートフォンやタブレット向けのサイトで広く採用されており、画面幅が限られたモバイル環境でナビゲーションをコンパクトに収めるために使われます。

このページでは、右側からスライドインするメニューパネルを実装します。メニューを開くと半透明のオーバーレイが背景に表示され、アイコンが × に変形して開いていることを視覚的に伝えます。オーバーレイのクリックや Escape キーでも閉じられるため、操作性の高い実装になっています。

  • スライドインパネル — CSS Transition で右端からパネルがスライドイン・アウトする
  • アイコン変形 — 3本線が × に変形し、開閉の状態を視覚的に示す
  • オーバーレイ背景 — 展開時に背景を薄暗くし、クリックで閉じられる
  • キーボード操作 — Escape キーでメニューを閉じられる
  • アクセシビリティ対応aria-expandedaria-controls でスクリーンリーダーに開閉状態と対象要素を通知する
  • スクロールロック — メニュー表示中はページのスクロールをロックして誤操作を防ぐ

実装のポイント・注意点

パネルの表示・非表示は display: none ではなく is-open クラスの付け外しで制御しています。display: none を使うと要素が即座に消えてしまい、CSS の transition が発火する前に非表示になるためスライドアニメーションが動きません。代わりに transform: translateX(100%)(画面右外に待機)→ translateX(0)(表示)の切り替えでアニメーションを実現しています。

オーバーレイも同様に、visibility: hidden; opacity: 0; を初期状態として is-open で両方を戻すことでフェードアニメーションを実現しています。visibility: hidden にすると要素が非表示になりつつクリックも透過するため、display: none と異なり transition が機能します。

アクセシビリティ対応として aria-expanded をトグルボタンに付与しています。値が "false" のときスクリーンリーダーは「閉じています」、"true" のときは「開いています」と読み上げます。aria-label も「メニューを開く」→「メニューを閉じる」に切り替えることで、アイコンだけのボタンでも意味が伝わります。

メニュー展開中に背景をスクロールさせないよう body.hb-scroll-lock { overflow: hidden } を使っています。メニューを閉じた際に必ずクラスを除去してスクロールを復元することを忘れないでください。

※ このデモではメニューパネルに position: absolute を使ってデモエリア内に収めています。実際のサイトに組み込む場合はサンプルソースのとおり position: fixed を使い、ビューポート全体に対して表示させてください。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

☰ アイコンをクリックしてメニューを開いてください。

オーバーレイ(薄暗い背景)をクリックするか、Escape キーでメニューを閉じます。

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ハンバーガーメニュー サンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<!-- ナビゲーションバー -->
<nav class="hb-nav" role="navigation" aria-label="メインナビゲーション">
  <div class="hb-nav__bar">
    <a href="#" class="hb-nav__logo">SiteLogo</a>
    <button
      class="hb-nav__toggle"
      id="hb-toggle"
      type="button"
      aria-expanded="false"
      aria-controls="hb-menu"
      aria-label="メニューを開く"
    >
      <span class="hb-nav__line"></span>
      <span class="hb-nav__line"></span>
      <span class="hb-nav__line"></span>
    </button>
  </div>
</nav>

<!-- オーバーレイ(背景暗幕) -->
<div class="hb-overlay" id="hb-overlay" aria-hidden="true"></div>

<!-- メニューパネル -->
<div class="hb-menu" id="hb-menu" role="dialog" aria-label="ナビゲーションメニュー">
  <ul class="hb-menu__list">
    <li><a href="#" class="hb-menu__item">ホーム</a></li>
    <li><a href="#" class="hb-menu__item">サービス</a></li>
    <li><a href="#" class="hb-menu__item">料金プラン</a></li>
    <li><a href="#" class="hb-menu__item">会社概要</a></li>
    <li><a href="#" class="hb-menu__item">お問い合わせ</a></li>
  </ul>
</div>

<!-- ページ本文 -->
<main class="hb-main">
  <h1>ページコンテンツ</h1>
  <p>右上のハンバーガーアイコンをクリックしてメニューを開いてください。</p>
</main>

<script src="./script.js"></script>
</body>
</html>
:root {
  --hb-nav-bg: #1A2332;
  --hb-nav-height: 56px;
  --hb-menu-width: 260px;
  --hb-duration: 0.3s;
  --hb-overlay-bg: rgba(0, 0, 0, 0.45);
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: sans-serif;
  color: #1A2332;
}

/* メニュー表示中はページのスクロールをロック */
body.hb-scroll-lock { overflow: hidden; }

/* ===== ナビゲーションバー ===== */
.hb-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: var(--hb-nav-bg);
}

.hb-nav__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: var(--hb-nav-height);
  padding: 0 20px;
  max-width: 1100px;
  margin: 0 auto;
}

.hb-nav__logo {
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  text-decoration: none;
}

/* ===== ハンバーガーボタン ===== */
.hb-nav__toggle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  padding: 6px;
  background: transparent;
  border: none;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.15s;
}

.hb-nav__toggle:hover { background: rgba(255, 255, 255, 0.1); }

.hb-nav__line {
  display: block;
  width: 100%;
  height: 2px;
  background: #fff;
  border-radius: 2px;
  transition: transform var(--hb-duration) ease, opacity var(--hb-duration) ease;
  transform-origin: center;
}

/* 開いているとき: 3本線を × に変形 */
.hb-nav__toggle[aria-expanded="true"] .hb-nav__line:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.hb-nav__toggle[aria-expanded="true"] .hb-nav__line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}
.hb-nav__toggle[aria-expanded="true"] .hb-nav__line:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* ===== オーバーレイ(背景暗幕) ===== */
/* display: none だと transition が効かないため visibility + opacity で制御する */
.hb-overlay {
  position: fixed;
  inset: 0;
  background: var(--hb-overlay-bg);
  z-index: 200;
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--hb-duration) ease, visibility var(--hb-duration) ease;
}

.hb-overlay.is-open {
  visibility: visible;
  opacity: 1;
}

/* ===== メニューパネル ===== */
.hb-menu {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: var(--hb-menu-width);
  background: #fff;
  box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15);
  z-index: 300;
  transform: translateX(100%); /* 初期状態: 画面右外に待機 */
  transition: transform var(--hb-duration) ease;
  overflow-y: auto;
}

.hb-menu.is-open {
  transform: translateX(0); /* スライドイン */
}

/* ===== メニューリスト ===== */
.hb-menu__list {
  list-style: none;
  margin: 0;
  /* ナビバーの高さ分だけ上部に余白を取る */
  padding: calc(var(--hb-nav-height) + 12px) 0 16px;
}

.hb-menu__item {
  display: block;
  padding: 14px 24px;
  color: #1A2332;
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
  border-bottom: 1px solid #F0F4F8;
  transition: background 0.15s, color 0.15s;
}

.hb-menu__item:hover {
  background: #F4F6F9;
  color: #2B7FE8;
}

/* ===== ページ本文 ===== */
.hb-main {
  padding: calc(var(--hb-nav-height) + 24px) 24px 24px;
  max-width: 960px;
  margin: 0 auto;
}
var toggle  = document.getElementById('hb-toggle');
var menu    = document.getElementById('hb-menu');
var overlay = document.getElementById('hb-overlay');

// メニューを開く
function openMenu() {
  menu.classList.add('is-open');
  overlay.classList.add('is-open');
  toggle.setAttribute('aria-expanded', 'true');
  toggle.setAttribute('aria-label', 'メニューを閉じる');
  document.body.classList.add('hb-scroll-lock');
}

// メニューを閉じる
function closeMenu() {
  menu.classList.remove('is-open');
  overlay.classList.remove('is-open');
  toggle.setAttribute('aria-expanded', 'false');
  toggle.setAttribute('aria-label', 'メニューを開く');
  document.body.classList.remove('hb-scroll-lock');
}

// ハンバーガーボタンのクリックでトグル
toggle.addEventListener('click', function() {
  if (toggle.getAttribute('aria-expanded') === 'true') {
    closeMenu();
  } else {
    openMenu();
  }
});

// オーバーレイをクリックで閉じる
overlay.addEventListener('click', closeMenu);

// Escape キーで閉じる
document.addEventListener('keydown', function(e) {
  if (e.key === 'Escape' && toggle.getAttribute('aria-expanded') === 'true') {
    closeMenu();
  }
});

AI用プロンプト

以下のプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。ライブラリ指定や列数変更など、要件を追記して使うのがおすすめです。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

# ハンバーガーメニュー 作成依頼

## 概要
スマートフォン対応のハンバーガーメニューを作成してください。
クリックで右側からスライドインするナビゲーションパネルです。

## 要件
- ナビバー上部にハンバーガーアイコン(3本線)を表示する
- アイコンをクリックするとメニューパネルが右からスライドイン表示される
- 開いているときアイコンを × に変形させる
- メニュー展開時に背後に半透明オーバーレイを表示し、クリックで閉じられる
- Escape キーでメニューを閉じられる
- aria-expanded でスクリーンリーダーに開閉状態を通知する
- メニュー表示中はページのスクロールをロックする

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
- ナビバー: 左にロゴ、右にハンバーガーボタンを配置
- メニューパネル: 画面右端から幅260px程度でスライドイン(position: fixed)
- アイコン変形: CSS Transition で1本目・3本目を回転させて × を作る
- オーバーレイ: rgba(0,0,0,0.45) で背後をぼかし、クリックで閉じる
- メニュー項目: ホーム・サービス・料金プラン・会社概要・お問い合わせ

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。