【初心者向け】モダンなハンバーガーメニュー5選!コピペで使えるコード付き

目次

はじめに

Web制作を始めて間もない方なら、誰もが一度は悩むハンバーガーメニューの実装。

HTMLとCSSの基礎は理解できても、スムーズなアニメーションやレスポンシブ対応となると、思うように実装できないものですよね。

特に、ポートフォリオサイトではモダンで印象的なUIを実現したくても、JavaScriptを使った実装方法や、アクセシビリティに配慮したコーディングとなると、なかなか難しいものです。

この記事では、現場で使える5種類のハンバーガーメニューを、コピー&ペーストで実装できるようにご紹介します。各実装例は、以下の要素を重視して解説していきます:

  • モダンなアニメーション効果
  • レスポンシブ対応
  • アクセシビリティへの配慮
  • 実装時の注意点
  • トラブルシューティング

パターン1:基本的なスライドインメニュー

まずは、最もポピュラーな左からスライドインするハンバーガーメニューの実装方法をご紹介します。

See the Pen Untitled by ryoma (@hwjgdjpk-the-decoder) on CodePen.

<div class="header">
  <button class="hamburger" aria-label="メニュー" aria-controls="nav-menu" aria-expanded="false">
    <span class="hamburger__line"></span>
    <span class="hamburger__line"></span>
    <span class="hamburger__line"></span>
  </button>

  <nav id="nav-menu" class="nav" aria-hidden="true">
    <ul class="nav__list">
      <li class="nav__item"><a href="#" class="nav__link">ホーム</a></li>
      <li class="nav__item"><a href="#" class="nav__link">about</a></li>
      <li class="nav__item"><a href="#" class="nav__link">サービス</a></li>
      <li class="nav__item"><a href="#" class="nav__link">お問い合わせ</a></li>
    </ul>
  </nav>
</div>
.header {
  position: relative;
  padding: 20px;
}

.hamburger {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 100;
  width: 48px;
  height: 48px;
  border: none;
  background: transparent;
  cursor: pointer;
}

.hamburger__line {
  position: absolute;
  left: 11px;
  width: 26px;
  height: 2px;
  background-color: #333;
  transition: all .4s;
}

.hamburger__line:nth-of-type(1) {
  top: 14px;
}
.hamburger__line:nth-of-type(2) {
  top: 23px;
}
.hamburger__line:nth-of-type(3) {
  top: 32px;
}

/* メニューオープン時 */
.hamburger.active .hamburger__line:nth-of-type(1) {
  transform: translateY(9px) rotate(-45deg);
}
.hamburger.active .hamburger__line:nth-of-type(2) {
  opacity: 0;
}
.hamburger.active .hamburger__line:nth-of-type(3) {
  transform: translateY(-9px) rotate(45deg);
}

.nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 300px;
  height: 100vh;
  background-color: #fff;
  box-shadow: 2px 0 4px rgba(0,0,0,.1);
  transform: translateX(-100%);
  transition: transform .4s;
  z-index: 90;
}

.nav.active {
  transform: translateX(0);
}

.nav__list {
  margin: 0;
  padding: 100px 0 0;
  list-style: none;
}

.nav__item {
  padding: 0 20px;
}

.nav__link {
  display: block;
  padding: 15px 0;
  color: #333;
  text-decoration: none;
  border-bottom: 1px solid #eee;
}
// script.js
document.addEventListener('DOMContentLoaded', () => {
  const hamburger = document.querySelector('.hamburger');
  const nav = document.querySelector('.nav');

  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    nav.classList.toggle('active');

    // アクセシビリティ対応
    const isOpen = hamburger.classList.contains('active');
    hamburger.setAttribute('aria-expanded', isOpen);
    nav.setAttribute('aria-hidden', !isOpen);
  });

  // メニューの外側をクリックした時の処理
  document.addEventListener('click', (e) => {
    if (!e.target.closest('.nav') && !e.target.closest('.hamburger') && nav.classList.contains('active')) {
      hamburger.classList.remove('active');
      nav.classList.remove('active');
      hamburger.setAttribute('aria-expanded', false);
      nav.setAttribute('aria-hidden', true);
    }
  });
});

実装のポイント解説:

  1. アクセシビリティへの配慮
  • aria-label属性でボタンの役割を明示
  • aria-expanded属性でメニューの開閉状態を通知
  • aria-hidden属性でスクリーンリーダーへの表示制御
  1. スムーズなアニメーション
  • transformとtransitionを使用した滑らかな動き
  • ハンバーガーアイコンの×への変形アニメーション
  1. レスポンシブ対応
  • vw/vh単位を使用した画面サイズ対応
  • メディアクエリは不要な最小限の実装
  1. UX向上のための実装
  • メニュー外クリックでの閉じる機能
  • タッチデバイスでの操作性考慮

パターン2:オーバーレイ&円形展開メニュー

2つ目は、画面全体をカバーするオーバーレイタイプと、ボタンを起点に円形に展開するスタイリッシュなメニューをご紹介します。

See the Pen Untitled by ryoma (@hwjgdjpk-the-decoder) on CodePen.


<div class="header">
  <button class="hamburger-overlay" aria-label="メニュー" aria-controls="overlay-menu" aria-expanded="false">
    <span class="hamburger-overlay__line"></span>
    <span class="hamburger-overlay__line"></span>
    <span class="hamburger-overlay__line"></span>
  </button>

  <nav id="overlay-menu" class="nav-overlay" aria-hidden="true">
    <div class="nav-overlay__content">
      <ul class="nav-overlay__list">
        <li class="nav-overlay__item"><a href="#" class="nav-overlay__link">ホーム</a></li>
        <li class="nav-overlay__item"><a href="#" class="nav-overlay__link">サービス</a></li>
        <li class="nav-overlay__item"><a href="#" class="nav-overlay__link">works</a></li>
        <li class="nav-overlay__item"><a href="#" class="nav-overlay__link">お問い合わせ</a></li>
      </ul>
    </div>
  </nav>
</div>
/* overlay-styles.css */
.hamburger-overlay {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  width: 48px;
  height: 48px;
  border: none;
  background: transparent;
  cursor: pointer;
}

.hamburger-overlay__line {
  position: absolute;
  left: 11px;
  width: 26px;
  height: 2px;
  background-color: #333;
  transition: all .6s;
}

.hamburger-overlay__line:nth-of-type(1) { top: 14px; }
.hamburger-overlay__line:nth-of-type(2) { top: 23px; }
.hamburger-overlay__line:nth-of-type(3) { top: 32px; }

.hamburger-overlay.active .hamburger-overlay__line {
  background-color: #fff;
}

.hamburger-overlay.active .hamburger-overlay__line:nth-of-type(1) {
  transform: translateY(9px) rotate(-45deg);
}
.hamburger-overlay.active .hamburger-overlay__line:nth-of-type(2) {
  opacity: 0;
}
.hamburger-overlay.active .hamburger-overlay__line:nth-of-type(3) {
  transform: translateY(-9px) rotate(45deg);
}

.nav-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.95);
  visibility: hidden;
  opacity: 0;
  transition: all .6s;
  z-index: 900;
}

.nav-overlay.active {
  visibility: visible;
  opacity: 1;
}

.nav-overlay__content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  text-align: center;
}

.nav-overlay__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-overlay__item {
  opacity: 0;
  transform: translateY(20px);
  transition: all .6s;
}

.nav-overlay.active .nav-overlay__item {
  opacity: 1;
  transform: translateY(0);
}

.nav-overlay.active .nav-overlay__item:nth-child(1) { transition-delay: 0.1s; }
.nav-overlay.active .nav-overlay__item:nth-child(2) { transition-delay: 0.2s; }
.nav-overlay.active .nav-overlay__item:nth-child(3) { transition-delay: 0.3s; }
.nav-overlay.active .nav-overlay__item:nth-child(4) { transition-delay: 0.4s; }

.nav-overlay__link {
  display: inline-block;
  padding: 20px;
  color: #fff;
  font-size: 24px;
  text-decoration: none;
  transition: color .3s;
}

.nav-overlay__link:hover {
  color: #4a90e2;
}
// overlay-script.js
document.addEventListener('DOMContentLoaded', () => {
  const hamburger = document.querySelector('.hamburger-overlay');
  const nav = document.querySelector('.nav-overlay');

  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    nav.classList.toggle('active');

    const isOpen = hamburger.classList.contains('active');
    hamburger.setAttribute('aria-expanded', isOpen);
    nav.setAttribute('aria-hidden', !isOpen);

    // メニューオープン時に背景スクロールを防止
    document.body.style.overflow = isOpen ? 'hidden' : '';
  });

  // ESCキーでメニューを閉じる
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && nav.classList.contains('active')) {
      hamburger.classList.remove('active');
      nav.classList.remove('active');
      hamburger.setAttribute('aria-expanded', false);
      nav.setAttribute('aria-hidden', true);
      document.body.style.overflow = '';
    }
  });
});

実装のポイント:

  1. スムーズなオーバーレイ表示
  • visibility と opacity の組み合わせによるスムーズな表示/非表示
  • transition-delay を使用したメニュー項目の段階的な表示
  1. アクセシビリティ強化
  • ESCキーでのメニュー閉じる機能
  • スクロール制御による背景固定
  • キーボード操作のサポート
  1. 視覚的な工夫
  • メニュー項目の段階的なフェードイン
  • ホバー時のインタラクション
  • 背景の透過による奥行き表現

パターン3:モーフィングアニメーションメニュー

3つ目は、ハンバーガーアイコンがスムーズに変形するモーフィングアニメーションを実装したメニューです。

See the Pen Untitled by ryoma (@hwjgdjpk-the-decoder) on CodePen.

<div class="header">
  <button class="hamburger-morph" aria-label="メニュー" aria-controls="morph-menu" aria-expanded="false">
    <svg class="hamburger-morph__icon" width="48" height="48" viewBox="0 0 100 100">
      <path class="hamburger-morph__line" d="M 20,29 H 80 C 80,29 94.5,28.817352 94.532987,66.711331 94.543142,77.980673 90.966081,81.670246 85.259173,81.668997 79.552261,81.667751 75.000211,74.999942 75.000211,74.999942 L 25.000021,25.000058" />
      <path class="hamburger-morph__line" d="M 20,50 H 80" />
      <path class="hamburger-morph__line" d="M 20,71 H 80 C 80,71 94.5,71.182648 94.532987,33.288669 94.543142,22.019327 90.966081,18.329754 85.259173,18.331003 79.552261,18.332249 75.000211,25.000058 75.000211,25.000058 L 25.000021,74.999942" />
    </svg>
  </button>

  <nav id="morph-menu" class="nav-morph" aria-hidden="true">
    <div class="nav-morph__wrapper">
      <ul class="nav-morph__list">
        <li class="nav-morph__item">
          <a href="#" class="nav-morph__link">
            <span class="nav-morph__text">Home</span>
            <span class="nav-morph__hover">ホーム</span>
          </a>
        </li>
        <li class="nav-morph__item">
          <a href="#" class="nav-morph__link">
            <span class="nav-morph__text">About</span>
            <span class="nav-morph__hover">私たちについて</span>
          </a>
        </li>
        <li class="nav-morph__item">
          <a href="#" class="nav-morph__link">
            <span class="nav-morph__text">Works</span>
            <span class="nav-morph__hover">制作実績</span>
          </a>
        </li>
        <li class="nav-morph__item">
          <a href="#" class="nav-morph__link">
            <span class="nav-morph__text">Contact</span>
            <span class="nav-morph__hover">お問い合わせ</span>
          </a>
        </li>
      </ul>
    </div>
  </nav>
</div>
.hamburger-morph {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  width: 48px;
  height: 48px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}

.hamburger-morph__icon {
  width: 100%;
  height: 100%;
}

.hamburger-morph__line {
  fill: none;
  stroke: #000;
  stroke-width: 6;
  transition: stroke-dasharray 600ms cubic-bezier(0.4, 0, 0.2, 1),
              stroke-dashoffset 600ms cubic-bezier(0.4, 0, 0.2, 1);
}

.hamburger-morph__line:nth-child(1) {
  stroke-dasharray: 60 207;
}

.hamburger-morph__line:nth-child(2) {
  stroke-dasharray: 60 60;
}

.hamburger-morph__line:nth-child(3) {
  stroke-dasharray: 60 207;
}

.hamburger-morph.active .hamburger-morph__line:nth-child(1) {
  stroke-dasharray: 90 207;
  stroke-dashoffset: -134;
}

.hamburger-morph.active .hamburger-morph__line:nth-child(2) {
  stroke-dasharray: 1 60;
  stroke-dashoffset: -30;
}

.hamburger-morph.active .hamburger-morph__line:nth-child(3) {
  stroke-dasharray: 90 207;
  stroke-dashoffset: -134;
}

.nav-morph {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background: rgba(29, 29, 31, 0.98);
  clip-path: circle(0% at calc(100% - 44px) 44px);
  transition: clip-path 0.7s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 900;
}

.nav-morph.active {
  clip-path: circle(150% at calc(100% - 44px) 44px);
}

.nav-morph__wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

.nav-morph__list {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}

.nav-morph__item {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.nav-morph.active .nav-morph__item {
  opacity: 1;
  transform: translateY(0);
}

.nav-morph.active .nav-morph__item:nth-child(1) { transition-delay: 0.3s; }
.nav-morph.active .nav-morph__item:nth-child(2) { transition-delay: 0.4s; }
.nav-morph.active .nav-morph__item:nth-child(3) { transition-delay: 0.5s; }
.nav-morph.active .nav-morph__item:nth-child(4) { transition-delay: 0.6s; }

.nav-morph__link {
  position: relative;
  display: inline-block;
  padding: 20px;
  font-size: 28px;
  color: #fff;
  text-decoration: none;
  overflow: hidden;
}

.nav-morph__text,
.nav-morph__hover {
  display: block;
  transition: transform 0.3s ease;
}

.nav-morph__hover {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  transform: translateY(0%);
}

.nav-morph__link:hover .nav-morph__text {
  transform: translateY(-100%);
}

.nav-morph__link:hover .nav-morph__hover {
  transform: translateY(-100%);
}
document.addEventListener('DOMContentLoaded', () => {
  const hamburger = document.querySelector('.hamburger-morph');
  const nav = document.querySelector('.nav-morph');

  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    nav.classList.toggle('active');

    const isOpen = hamburger.classList.contains('active');
    hamburger.setAttribute('aria-expanded', isOpen);
    nav.setAttribute('aria-hidden', !isOpen);
    document.body.style.overflow = isOpen ? 'hidden' : '';
  });

  // メニューリンクのホバーエフェクト用のイベントリスナー
  const menuLinks = document.querySelectorAll('.nav-morph__link');
  menuLinks.forEach(link => {
    link.addEventListener('mouseenter', () => {
      link.querySelector('.nav-morph__text').style.transform = 'translateY(-100%)';
      link.querySelector('.nav-morph__hover').style.transform = 'translateY(-100%)';
    });

    link.addEventListener('mouseleave', () => {
      link.querySelector('.nav-morph__text').style.transform = 'translateY(0)';
      link.querySelector('.nav-morph__hover').style.transform = 'translateY(0)';
    });
  });
});

実装のポイント:

  1. SVGを使用したモーフィングアニメーション
  • stroke-dasharray と stroke-dashoffset を使用した滑らかな変形
  • cubic-bezier による緩急のある動き
  1. クリエイティブなメニュー展開
  • clip-path を使用した円形展開アニメーション
  • 順次表示されるメニュー項目
  1. インタラクティブな要素
  • 二重テキストによるホバーエフェクト
  • スムーズなテキストスライド

これらのコードは、モダンなWebサイトやポートフォリオサイトにそのまま実装できます。

パターン4:スライド&フェードメニュー

4つ目は、スライドとフェードを組み合わせた高級感のあるアニメーションメニューです。

See the Pen Untitled by ryoma (@hwjgdjpk-the-decoder) on CodePen.

<div class="header">
  <button class="hamburger-fade" aria-label="メニュー" aria-controls="fade-menu" aria-expanded="false">
    <div class="hamburger-fade__wrapper">
      <span class="hamburger-fade__line"></span>
      <span class="hamburger-fade__line"></span>
      <span class="hamburger-fade__line"></span>
    </div>
  </button>

  <nav id="fade-menu" class="nav-fade" aria-hidden="true">
    <div class="nav-fade__bg"></div>
    <div class="nav-fade__wrapper">
      <ul class="nav-fade__list">
        <li class="nav-fade__item">
          <span class="nav-fade__number">01</span>
          <a href="#" class="nav-fade__link">Home</a>
        </li>
        <li class="nav-fade__item">
          <span class="nav-fade__number">02</span>
          <a href="#" class="nav-fade__link">About</a>
        </li>
        <li class="nav-fade__item">
          <span class="nav-fade__number">03</span>
          <a href="#" class="nav-fade__link">Works</a>
        </li>
        <li class="nav-fade__item">
          <span class="nav-fade__number">04</span>
          <a href="#" class="nav-fade__link">Contact</a>
        </li>
      </ul>
      <div class="nav-fade__info">
        <p class="nav-fade__address">東京都渋谷区○○ 1-2-3</p>
        <p class="nav-fade__tel">03-1234-5678</p>
      </div>
    </div>
  </nav>
</div>
/* fade-styles.css */
.hamburger-fade {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  width: 60px;
  height: 60px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}

.hamburger-fade__wrapper {
  position: relative;
  width: 30px;
  height: 20px;
  margin: 20px auto;
}

.hamburger-fade__line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background-color: #333;
  transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
}

.hamburger-fade__line:nth-child(1) { top: 0; }
.hamburger-fade__line:nth-child(2) { top: 9px; }
.hamburger-fade__line:nth-child(3) { top: 18px; }

.hamburger-fade.active .hamburger-fade__line {
  background-color: #fff;
}

.hamburger-fade.active .hamburger-fade__line:nth-child(1) {
  transform: translateY(9px) rotate(45deg);
}

.hamburger-fade.active .hamburger-fade__line:nth-child(2) {
  opacity: 0;
  transform: translateX(20px);
}

.hamburger-fade.active .hamburger-fade__line:nth-child(3) {
  transform: translateY(-9px) rotate(-45deg);
}

.nav-fade {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  visibility: hidden;
  z-index: 900;
}

.nav-fade__bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.95);
  opacity: 0;
  transition: opacity 0.5s cubic-bezier(0.23, 1, 0.32, 1);
}

.nav-fade.active {
  visibility: visible;
}

.nav-fade.active .nav-fade__bg {
  opacity: 1;
}

.nav-fade__wrapper {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 5vh 10vw;
}

.nav-fade__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-fade__item {
  position: relative;
  margin-bottom: 2vh;
  padding-left: 60px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
}

.nav-fade.active .nav-fade__item {
  opacity: 1;
  transform: translateY(0);
}

.nav-fade.active .nav-fade__item:nth-child(1) { transition-delay: 0.2s; }
.nav-fade.active .nav-fade__item:nth-child(2) { transition-delay: 0.3s; }
.nav-fade.active .nav-fade__item:nth-child(3) { transition-delay: 0.4s; }
.nav-fade.active .nav-fade__item:nth-child(4) { transition-delay: 0.5s; }

.nav-fade__number {
  position: absolute;
  left: 0;
  color: #666;
  font-size: 14px;
  font-family: 'Roboto', sans-serif;
}

.nav-fade__link {
  display: inline-block;
  color: #fff;
  font-size: 32px;
  text-decoration: none;
  transition: color 0.3s ease;
}

.nav-fade__link:hover {
  color: #4a90e2;
}

.nav-fade__info {
  margin-top: auto;
  padding-left: 60px;
  color: #666;
  font-size: 14px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0.6s;
}

.nav-fade.active .nav-fade__info {
  opacity: 1;
  transform: translateY(0);
}

.nav-fade__address,
.nav-fade__tel {
  margin: 5px 0;
}

@media (max-width: 768px) {
  .nav-fade__link {
    font-size: 24px;
  }

  .nav-fade__item {
    padding-left: 40px;
    margin-bottom: 1.5vh;
  }

  .nav-fade__info {
    padding-left: 40px;
  }
}
// fade-script.js
document.addEventListener('DOMContentLoaded', () => {
  const hamburger = document.querySelector('.hamburger-fade');
  const nav = document.querySelector('.nav-fade');

  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    nav.classList.toggle('active');

    const isOpen = hamburger.classList.contains('active');
    hamburger.setAttribute('aria-expanded', isOpen);
    nav.setAttribute('aria-hidden', !isOpen);
    document.body.style.overflow = isOpen ? 'hidden' : '';
  });

  // メニューリンクにホバーエフェクトを追加
  const menuLinks = document.querySelectorAll('.nav-fade__link');
  menuLinks.forEach(link => {
    link.addEventListener('mouseover', () => {
      const number = link.parentElement.querySelector('.nav-fade__number');
      number.style.color = '#4a90e2';
    });

    link.addEventListener('mouseout', () => {
      const number = link.parentElement.querySelector('.nav-fade__number');
      number.style.color = '#666';
    });
  });
});

実装のポイント:

  1. 高級感のある演出
  • 番号とテキストの組み合わせ
  • 連続的なアニメーション
  • 洗練された配色
  1. レスポンシブ対応
  • フォントサイズの可変
  • パディングの調整
  • メディアクエリによる最適化
  1. インタラクティブ要素
  • 番号とリンクの連動したホバーエフェクト
  • スムーズなフェードイン
  • 背景のオーバーレイ効果

パターン5:グリッドアニメーションメニュー

5つ目は、グリッドレイアウトを活用したモダンなメニューです。

メニュー展開時にグリッドが動的に変化する演出が特徴です。

See the Pen Untitled by ryoma (@hwjgdjpk-the-decoder) on CodePen.

<div class="header">
  <button class="hamburger-grid" aria-label="メニュー" aria-controls="grid-menu" aria-expanded="false">
    <div class="hamburger-grid__dots">
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
      <span class="hamburger-grid__dot"></span>
    </div>
  </button>

  <nav id="grid-menu" class="nav-grid" aria-hidden="true">
    <div class="nav-grid__content">
      <div class="nav-grid__sections">
        <section class="nav-grid__section">
          <h2 class="nav-grid__title">Menu</h2>
          <ul class="nav-grid__list">
            <li><a href="#" class="nav-grid__link">Home</a></li>
            <li><a href="#" class="nav-grid__link">About</a></li>
            <li><a href="#" class="nav-grid__link">Works</a></li>
            <li><a href="#" class="nav-grid__link">Contact</a></li>
          </ul>
        </section>

        <section class="nav-grid__section">
          <h2 class="nav-grid__title">Social</h2>
          <ul class="nav-grid__list">
            <li><a href="#" class="nav-grid__link">Twitter</a></li>
            <li><a href="#" class="nav-grid__link">Instagram</a></li>
            <li><a href="#" class="nav-grid__link">GitHub</a></li>
          </ul>
        </section>
      </div>
    </div>
  </nav>
</div>
.hamburger-grid {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  width: 50px;
  height: 50px;
  padding: 10px;
  border: none;
  background: transparent;
  cursor: pointer;
}

.hamburger-grid__dots {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  width: 100%;
  height: 100%;
}

.hamburger-grid__dot {
  width: 100%;
  height: 100%;
  background-color: #333;
  border-radius: 50%;
  transition: transform 0.3s ease, background-color 0.3s ease;
}

.hamburger-grid.active .hamburger-grid__dot {
  background-color: #fff;
}

.hamburger-grid.active .hamburger-grid__dot:nth-child(1) {
  transform: scale(0);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(2) {
  transform: translateY(8px);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(3) {
  transform: scale(0);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(4) {
  transform: translateX(8px);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(5) {
  transform: scale(1.2);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(6) {
  transform: translateX(-8px);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(7) {
  transform: scale(0);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(8) {
  transform: translateY(-8px);
}
.hamburger-grid.active .hamburger-grid__dot:nth-child(9) {
  transform: scale(0);
}

.nav-grid {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background-color: #1a1a1a;
  visibility: hidden;
  opacity: 0;
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 900;
}

.nav-grid.active {
  visibility: visible;
  opacity: 1;
}

.nav-grid__content {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
  max-width: 1200px;
  height: 100%;
  margin: 0 auto;
  padding: 100px 40px;
}

.nav-grid__sections {
  grid-column: span 12;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 40px;
}

.nav-grid__section {
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.nav-grid.active .nav-grid__section {
  opacity: 1;
  transform: translateY(0);
}

.nav-grid.active .nav-grid__section:nth-child(1) {
  transition-delay: 0.2s;
}
.nav-grid.active .nav-grid__section:nth-child(2) {
  transition-delay: 0.3s;
}

.nav-grid__title {
  margin: 0 0 20px;
  color: #666;
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.nav-grid__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-grid__list li {
  margin-bottom: 15px;
  overflow: hidden;
}

.nav-grid__link {
  display: inline-block;
  color: #fff;
  font-size: 24px;
  text-decoration: none;
  transform: translateY(100%);
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              color 0.3s ease;
}

.nav-grid.active .nav-grid__link {
  transform: translateY(0);
}

.nav-grid__link:hover {
  color: #4a90e2;
}

@media (max-width: 768px) {
  .nav-grid__content {
    padding: 80px 20px;
  }

  .nav-grid__sections {
    grid-template-columns: 1fr;
  }

  .nav-grid__link {
    font-size: 20px;
  }
}
document.addEventListener('DOMContentLoaded', () => {
  const hamburger = document.querySelector('.hamburger-grid');
  const nav = document.querySelector('.nav-grid');
  const links = document.querySelectorAll('.nav-grid__link');

  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    nav.classList.toggle('active');

    const isOpen = hamburger.classList.contains('active');
    hamburger.setAttribute('aria-expanded', isOpen);
    nav.setAttribute('aria-hidden', !isOpen);
    document.body.style.overflow = isOpen ? 'hidden' : '';

    // リンクのアニメーションディレイを設定
    if (isOpen) {
      links.forEach((link, index) => {
        link.style.transitionDelay = `${0.2 + (index * 0.1)}s`;
      });
    } else {
      links.forEach(link => {
        link.style.transitionDelay = '0s';
      });
    }
  });

  // アクセシビリティのためのキーボードナビゲーション
  nav.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && nav.classList.contains('active')) {
      hamburger.click();
    }
  });
});

実装のポイント:

  1. グリッドレイアウトの活用
  • CSS Gridによる柔軟なレイアウト
  • レスポンシブ対応の簡略化
  • セクション分けによる整理された構造
  1. ドットアニメーション
  • 個別のドットの動きによる視覚的な楽しさ
  • スケールと移動を組み合わせた演出
  • 滑らかな色の変化
  1. 段階的なアニメーション
  • セクションごとの表示タイミング制御
  • リンクの順次表示
  • スムーズな出現と消失

さいごに

ここまで5種類のモダンなハンバーガーメニューの実装方法をご紹介してきました。それぞれのデザインや動きの特徴を活かして、サイトの雰囲気に合わせて使い分けていただければと思います。

実装時の注意点

アクセシビリティに関しては、aria属性を適切に使用し、キーボード操作やスクリーンリーダーでの利用に配慮することが重要です。特にモバイルデバイスでの操作性を重視し、タッチデバイスでの挙動確認も忘れずに行いましょう。

パフォーマンス面では、アニメーションにtransformとopacityを優先的に使用することで、スムーズな動作を実現できます。また、不要なDOM操作を最小限に抑えることで、軽量な実装を維持することができます。

カスタマイズのポイント

今回ご紹介したコードは基本的な実装例ですが、実際のプロジェクトに導入する際は、サイトのデザインに合わせて配色やフォント、アニメーション速度などを調整してください。さらに、検索機能や言語切替など、必要に応じて機能を追加することで、よりユーザーフレンドリーなナビゲーションを実現できます。

関連記事

あわせて読みたい
忙しい社会人でもできる!JavaScript習得のための最適な学習順序 はじめに 「JavaScriptの修正を頼まれた…でも、どこから手を付ければいいんだろう」「HTML/CSSはできるのに、JavaScriptになると全然わからない…」「休日は勉強している...
あわせて読みたい
【初心者向け】EJSで始める効率的なHTML管理術 はじめに 「また全ページのヘッダーを修正しないと…」「共通パーツのコピペ作業、ミスが怖いな…」 Web制作の現場で、こんな不安や手間を感じたことはありませんか? 特...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次