モーフィングナビ
ハンバーガーアイコンが×へモーフし、角の円が全面へ広がるメニュー遷移。clip-path とSVGの変形で、ヘッダーの開閉インタラクションを上質に演出します。
ライブデモ
使用例(お題: カフェ 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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。