モーフィングナビ

ハンバーガーアイコンが×へモーフし、角の円が全面へ広がるメニュー遷移。clip-path とSVGの変形で、ヘッダーの開閉インタラクションを上質に演出します。

#css#javascript#svg#navigation

ライブデモ

使用例(お題: カフェ MOON BREW)

この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- MOON BREW: カフェのヘッダー。ハンバーガー→×へモーフし、メニューが全面展開 -->
<div class="mb-stage">
  <header class="mb-bar">
    <span class="mb-logo">☕ MOON BREW</span>
    <button class="mb-toggle" aria-label="メニューを開閉" aria-expanded="false">
      <svg viewBox="0 0 32 32" width="26" height="26" aria-hidden="true">
        <path class="mb-line mb-line--top" d="M6 10 H26" />
        <path class="mb-line mb-line--mid" d="M6 16 H26" />
        <path class="mb-line mb-line--bot" d="M6 22 H26" />
      </svg>
    </button>
  </header>

  <!-- 店内ビジュアル -->
  <div class="mb-hero">
    <span class="mb-hero-kicker">OPEN 8:00 - 20:00</span>
    <h2 class="mb-hero-title">朝の一杯を、<br>あなたの定番に。</h2>
    <p class="mb-hero-text">自家焙煎の深煎りブレンドと、<br>焼きたてのスコーンでお出迎え。</p>
  </div>

  <!-- 円→全面へモーフするメニューパネル -->
  <nav class="mb-panel" aria-hidden="true">
    <ul class="mb-links">
      <li style="--i:0"><a href="#" class="mb-link">Home</a></li>
      <li style="--i:1"><a href="#" class="mb-link">Menu</a></li>
      <li style="--i:2"><a href="#" class="mb-link">Beans</a></li>
      <li style="--i:3"><a href="#" class="mb-link">Story</a></li>
      <li style="--i:4"><a href="#" class="mb-link">Access</a></li>
    </ul>
  </nav>

  <p class="mb-hint">右上のアイコンをクリック。線が×へモーフし、円から面へ広がるナビ。</p>
</div>
CSS
* { box-sizing: border-box; }

:root {
  --cream: #f5ede1;
  --brown: #2b1d12;
  --amber: #c98a3b;
  --ease: cubic-bezier(.7, 0, .2, 1);
}

body {
  margin: 0;
  min-height: 400px;
  display: grid;
  place-items: center;
  font-family: "Hiragino Mincho ProN", "Georgia", serif;
  background: #e7dccb;
}

.mb-stage {
  position: relative;
  width: min(560px, 94vw);
  height: 360px;
  border-radius: 20px;
  overflow: hidden;
  background: var(--cream);
  box-shadow: 0 22px 54px rgba(43, 29, 18, .3);
}

.mb-bar {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
}
.mb-logo { font-size: 17px; font-weight: 700; color: var(--brown); letter-spacing: .08em; }

.mb-toggle {
  border: none;
  background: transparent;
  padding: 4px;
  cursor: pointer;
  color: var(--brown);
  border-radius: 8px;
}
.mb-toggle:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* ハンバーガーの線 */
.mb-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  transition: transform .45s var(--ease), opacity .3s ease, stroke .3s ease;
  transform-origin: 16px 16px;
}
.mb-stage.is-open .mb-toggle { color: var(--cream); }
.mb-stage.is-open .mb-line--top { transform: translateY(6px) rotate(45deg); }
.mb-stage.is-open .mb-line--bot { transform: translateY(-6px) rotate(-45deg); }
.mb-stage.is-open .mb-line--mid { opacity: 0; transform: scaleX(0); }

/* 店内ヒーロー */
.mb-hero {
  position: relative;
  z-index: 1;
  padding: 18px 28px;
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
}
.mb-hero-kicker { font-size: 11px; letter-spacing: .28em; color: var(--amber); font-weight: 700; }
.mb-hero-title { margin: 12px 0 14px; font-size: 28px; line-height: 1.35; color: var(--brown); font-family: "Hiragino Mincho ProN", serif; }
.mb-hero-text { margin: 0; font-size: 13px; line-height: 1.9; color: #6b5640; }

/* 円→全面へモーフするパネル */
.mb-panel {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    radial-gradient(500px 300px at 80% 10%, #3a2818 0%, transparent 70%),
    var(--brown);
  display: grid;
  place-items: center;
  clip-path: circle(0% at calc(100% - 38px) 38px);
  transition: clip-path .6s var(--ease);
  pointer-events: none;
}
.mb-stage.is-open .mb-panel {
  clip-path: circle(150% at calc(100% - 38px) 38px);
  pointer-events: auto;
}

.mb-links { list-style: none; margin: 0; padding: 0; text-align: center; }
.mb-links li {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
.mb-stage.is-open .mb-links li {
  opacity: 1;
  transform: translateY(0);
  transition-delay: calc(180ms + var(--i) * 70ms);
}

.mb-link {
  display: inline-block;
  padding: 6px 10px;
  color: var(--cream);
  text-decoration: none;
  font-size: 25px;
  font-weight: 700;
  letter-spacing: .04em;
  transition: color .2s ease, letter-spacing .25s ease;
}
.mb-link:hover { color: var(--amber); letter-spacing: .12em; }
.mb-link:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 4px; }

.mb-hint {
  position: absolute;
  left: 24px;
  bottom: 16px;
  margin: 0;
  z-index: 1;
  max-width: 30ch;
  font-size: 11px;
  color: #9a836a;
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
}

@media (prefers-reduced-motion: reduce) {
  .mb-line, .mb-panel, .mb-links li { transition-duration: 1ms !important; }
}
JavaScript
// MOON BREW モーフィングナビ: トグルでアイコン変形+パネル展開(状態はクラスで一元管理)
(() => {
  const stage = document.querySelector('.mb-stage');
  const toggle = document.querySelector('.mb-toggle');
  const panel = document.querySelector('.mb-panel');
  if (!stage || !toggle || !panel) return; // null安全

  const setOpen = (open) => {
    stage.classList.toggle('is-open', open);
    toggle.setAttribute('aria-expanded', String(open));
    panel.setAttribute('aria-hidden', String(!open));
  };

  // 開閉トグル
  toggle.addEventListener('click', () => {
    setOpen(!stage.classList.contains('is-open'));
  });

  // パネル内リンクをクリックしたら閉じる
  panel.addEventListener('click', (e) => {
    if (e.target.closest('.mb-link')) {
      e.preventDefault();
      setOpen(false);
    }
  });

  // Escで閉じる
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && stage.classList.contains('is-open')) setOpen(false);
  });
})();

コード

HTML
<!-- モーフィングナビ: ハンバーガー→×へSVGパスをモーフし、メニューが展開する -->
<div class="mn-stage">
  <header class="mn-bar">
    <span class="mn-logo">◑ Lumen</span>

    <button class="mn-toggle" aria-label="メニューを開閉" aria-expanded="false">
      <svg viewBox="0 0 32 32" width="28" height="28" aria-hidden="true">
        <!-- 3本線。JSでクラス付与しパスをモーフ -->
        <path class="mn-line mn-line--top" d="M6 10 H26" />
        <path class="mn-line mn-line--mid" d="M6 16 H26" />
        <path class="mn-line mn-line--bot" d="M6 22 H26" />
      </svg>
    </button>
  </header>

  <!-- モーフして広がるパネル -->
  <nav class="mn-panel" aria-hidden="true">
    <ul class="mn-links">
      <li style="--i:0"><a href="#" class="mn-link">Home</a></li>
      <li style="--i:1"><a href="#" class="mn-link">Works</a></li>
      <li style="--i:2"><a href="#" class="mn-link">Studio</a></li>
      <li style="--i:3"><a href="#" class="mn-link">Journal</a></li>
      <li style="--i:4"><a href="#" class="mn-link">Contact</a></li>
    </ul>
  </nav>

  <p class="mn-hint">右上のアイコンをクリック。線が×へモーフし、円から面へ広がるナビ。</p>
</div>
CSS
* { box-sizing: border-box; }

:root {
  --ink: #0e1020;
  --paper: #f7f5ef;
  --accent: #ff5a3c;
  --ease: cubic-bezier(.7, 0, .2, 1);
}

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(700px 340px at 90% 0%, #1b1f3a 0%, transparent 60%),
    #0a0b14;
}

.mn-stage {
  position: relative;
  width: min(640px, 92vw);
  height: 300px;
  border-radius: 20px;
  overflow: hidden;
  background: var(--paper);
  box-shadow: 0 24px 60px rgba(0,0,0,.5);
}

.mn-bar {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
}
.mn-logo { font-size: 18px; font-weight: 800; color: var(--ink); letter-spacing: .02em; }

.mn-toggle {
  border: none;
  background: transparent;
  padding: 4px;
  cursor: pointer;
  color: var(--ink);
  border-radius: 8px;
}
.mn-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ハンバーガーの線 */
.mn-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  transition: transform .45s var(--ease), opacity .3s ease, stroke .3s ease;
  transform-origin: 16px 16px;
}

/* 開いたとき×へモーフ(線を回転&中央線を消す) */
.mn-stage.is-open .mn-toggle { color: var(--paper); }
.mn-stage.is-open .mn-line--top { transform: translateY(6px) rotate(45deg); }
.mn-stage.is-open .mn-line--bot { transform: translateY(-6px) rotate(-45deg); }
.mn-stage.is-open .mn-line--mid { opacity: 0; transform: scaleX(0); }

/* モーフして広がるパネル: 右上の小さな円→全面へ */
.mn-panel {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--ink);
  display: grid;
  place-items: center;
  clip-path: circle(0% at calc(100% - 38px) 38px);
  transition: clip-path .6s var(--ease);
  pointer-events: none;
}
.mn-stage.is-open .mn-panel {
  clip-path: circle(150% at calc(100% - 38px) 38px);
  pointer-events: auto;
}

.mn-links {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
}
.mn-links li {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
  transition-delay: 0ms;
}
.mn-stage.is-open .mn-links li {
  opacity: 1;
  transform: translateY(0);
  /* 各リンクを順に立ち上げる */
  transition-delay: calc(180ms + var(--i) * 70ms);
}

.mn-link {
  display: inline-block;
  padding: 7px 10px;
  color: var(--paper);
  text-decoration: none;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .01em;
  transition: color .2s ease, letter-spacing .25s ease;
}
.mn-link:hover { color: var(--accent); letter-spacing: .08em; }
.mn-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.mn-hint {
  position: absolute;
  left: 24px;
  bottom: 18px;
  margin: 0;
  z-index: 1;
  max-width: 30ch;
  font-size: 12px;
  color: #5a5c6a;
}

@media (prefers-reduced-motion: reduce) {
  .mn-line, .mn-panel, .mn-links li { transition-duration: 1ms !important; }
}
JavaScript
// モーフィングナビ: トグルでアイコン変形+パネル展開(状態はクラスで一元管理)
(() => {
  const stage = document.querySelector('.mn-stage');
  const toggle = document.querySelector('.mn-toggle');
  const panel = document.querySelector('.mn-panel');
  if (!stage || !toggle || !panel) return; // null安全

  const setOpen = (open) => {
    stage.classList.toggle('is-open', open);
    toggle.setAttribute('aria-expanded', String(open));
    panel.setAttribute('aria-hidden', String(!open));
  };

  // 開閉トグル
  toggle.addEventListener('click', () => {
    setOpen(!stage.classList.contains('is-open'));
  });

  // パネル内リンクをクリックしたら閉じる
  panel.addEventListener('click', (e) => {
    if (e.target.closest('.mn-link')) {
      e.preventDefault();
      setOpen(false);
    }
  });

  // Escで閉じる
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && stage.classList.contains('is-open')) setOpen(false);
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「モーフィングナビ」の効果を追加してください。

# 追加してほしい効果
モーフィングナビ(ページ遷移 / View Transitions)
ハンバーガーアイコンが×へモーフし、角の円が全面へ広がるメニュー遷移。clip-path とSVGの変形で、ヘッダーの開閉インタラクションを上質に演出します。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- モーフィングナビ: ハンバーガー→×へSVGパスをモーフし、メニューが展開する -->
<div class="mn-stage">
  <header class="mn-bar">
    <span class="mn-logo">◑ Lumen</span>

    <button class="mn-toggle" aria-label="メニューを開閉" aria-expanded="false">
      <svg viewBox="0 0 32 32" width="28" height="28" aria-hidden="true">
        <!-- 3本線。JSでクラス付与しパスをモーフ -->
        <path class="mn-line mn-line--top" d="M6 10 H26" />
        <path class="mn-line mn-line--mid" d="M6 16 H26" />
        <path class="mn-line mn-line--bot" d="M6 22 H26" />
      </svg>
    </button>
  </header>

  <!-- モーフして広がるパネル -->
  <nav class="mn-panel" aria-hidden="true">
    <ul class="mn-links">
      <li style="--i:0"><a href="#" class="mn-link">Home</a></li>
      <li style="--i:1"><a href="#" class="mn-link">Works</a></li>
      <li style="--i:2"><a href="#" class="mn-link">Studio</a></li>
      <li style="--i:3"><a href="#" class="mn-link">Journal</a></li>
      <li style="--i:4"><a href="#" class="mn-link">Contact</a></li>
    </ul>
  </nav>

  <p class="mn-hint">右上のアイコンをクリック。線が×へモーフし、円から面へ広がるナビ。</p>
</div>

【CSS】
* { box-sizing: border-box; }

:root {
  --ink: #0e1020;
  --paper: #f7f5ef;
  --accent: #ff5a3c;
  --ease: cubic-bezier(.7, 0, .2, 1);
}

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(700px 340px at 90% 0%, #1b1f3a 0%, transparent 60%),
    #0a0b14;
}

.mn-stage {
  position: relative;
  width: min(640px, 92vw);
  height: 300px;
  border-radius: 20px;
  overflow: hidden;
  background: var(--paper);
  box-shadow: 0 24px 60px rgba(0,0,0,.5);
}

.mn-bar {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
}
.mn-logo { font-size: 18px; font-weight: 800; color: var(--ink); letter-spacing: .02em; }

.mn-toggle {
  border: none;
  background: transparent;
  padding: 4px;
  cursor: pointer;
  color: var(--ink);
  border-radius: 8px;
}
.mn-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ハンバーガーの線 */
.mn-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  transition: transform .45s var(--ease), opacity .3s ease, stroke .3s ease;
  transform-origin: 16px 16px;
}

/* 開いたとき×へモーフ(線を回転&中央線を消す) */
.mn-stage.is-open .mn-toggle { color: var(--paper); }
.mn-stage.is-open .mn-line--top { transform: translateY(6px) rotate(45deg); }
.mn-stage.is-open .mn-line--bot { transform: translateY(-6px) rotate(-45deg); }
.mn-stage.is-open .mn-line--mid { opacity: 0; transform: scaleX(0); }

/* モーフして広がるパネル: 右上の小さな円→全面へ */
.mn-panel {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--ink);
  display: grid;
  place-items: center;
  clip-path: circle(0% at calc(100% - 38px) 38px);
  transition: clip-path .6s var(--ease);
  pointer-events: none;
}
.mn-stage.is-open .mn-panel {
  clip-path: circle(150% at calc(100% - 38px) 38px);
  pointer-events: auto;
}

.mn-links {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
}
.mn-links li {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
  transition-delay: 0ms;
}
.mn-stage.is-open .mn-links li {
  opacity: 1;
  transform: translateY(0);
  /* 各リンクを順に立ち上げる */
  transition-delay: calc(180ms + var(--i) * 70ms);
}

.mn-link {
  display: inline-block;
  padding: 7px 10px;
  color: var(--paper);
  text-decoration: none;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .01em;
  transition: color .2s ease, letter-spacing .25s ease;
}
.mn-link:hover { color: var(--accent); letter-spacing: .08em; }
.mn-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.mn-hint {
  position: absolute;
  left: 24px;
  bottom: 18px;
  margin: 0;
  z-index: 1;
  max-width: 30ch;
  font-size: 12px;
  color: #5a5c6a;
}

@media (prefers-reduced-motion: reduce) {
  .mn-line, .mn-panel, .mn-links li { transition-duration: 1ms !important; }
}

【JavaScript】
// モーフィングナビ: トグルでアイコン変形+パネル展開(状態はクラスで一元管理)
(() => {
  const stage = document.querySelector('.mn-stage');
  const toggle = document.querySelector('.mn-toggle');
  const panel = document.querySelector('.mn-panel');
  if (!stage || !toggle || !panel) return; // null安全

  const setOpen = (open) => {
    stage.classList.toggle('is-open', open);
    toggle.setAttribute('aria-expanded', String(open));
    panel.setAttribute('aria-hidden', String(!open));
  };

  // 開閉トグル
  toggle.addEventListener('click', () => {
    setOpen(!stage.classList.contains('is-open'));
  });

  // パネル内リンクをクリックしたら閉じる
  panel.addEventListener('click', (e) => {
    if (e.target.closest('.mn-link')) {
      e.preventDefault();
      setOpen(false);
    }
  });

  // Escで閉じる
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && stage.classList.contains('is-open')) setOpen(false);
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。