ハンバーガー→×モーフ

3本線がtransformで滑らかに×へ変形するメニュートグル。aria-expandedも同期し、モバイルナビの開閉ボタンにそのまま使えます。

#js#css#transform#a11y

ライブデモ

使用例(お題: アイドルグループ Sakura)

この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- Sakura: モバイルサイト風。ハンバーガー→×でメニュー開閉 -->
<div class="sk">
  <header class="sk__bar">
    <span class="sk__logo">🌸 Sakura</span>
    <button class="burger" type="button" aria-expanded="false" aria-label="メニューを開く" aria-controls="sk-menu">
      <span class="burger__box" aria-hidden="true">
        <span class="burger__line"></span>
        <span class="burger__line"></span>
        <span class="burger__line"></span>
      </span>
    </button>
  </header>

  <nav class="sk__menu" id="sk-menu" aria-hidden="true">
    <a href="#">ホーム</a>
    <a href="#">メンバー</a>
    <a href="#">ライブ・イベント</a>
    <a href="#">楽曲 / MV</a>
    <a href="#">ニュース</a>
  </nav>

  <section class="sk__hero">
    <p class="sk__eyebrow">2nd SINGLE</p>
    <h1 class="sk__title">「春風セレナーデ」<br>配信中</h1>
    <p class="sk__lead">右上のメニューを押すとナビが開きます。</p>
  </section>
</div>
CSS
/* Sakura アイドル テーマ: 桜ピンク/白/淡グレー */
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
  background:
    radial-gradient(circle at 85% 0%, #ffe3ee 0%, transparent 55%),
    #fff7fb;
  color: #6b4a58;
}

.sk {
  position: relative;
  height: 400px;
  overflow: hidden;
}

.sk__bar {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  background: rgba(255,247,251,.85);
  backdrop-filter: blur(4px);
  border-bottom: 1px solid #ffe0ec;
}
.sk__logo {
  font-size: 18px;
  font-weight: 800;
  color: #e85a92;
}

/* 主役: ハンバーガー→×トグル */
.burger {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
.burger__box {
  position: relative;
  width: 26px;
  height: 18px;
}
.burger__line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 3px;
  border-radius: 3px;
  background: #e85a92;
  transition: transform .35s cubic-bezier(.65,0,.35,1), opacity .2s ease;
}
.burger__line:nth-child(1) { top: 0; }
.burger__line:nth-child(2) { top: 50%; transform: translateY(-50%); }
.burger__line:nth-child(3) { bottom: 0; }

/* open状態: 上下を中央へ寄せて×に、中央は消す */
.burger.is-open .burger__line:nth-child(1) {
  top: 50%;
  transform: translateY(-50%) rotate(45deg);
}
.burger.is-open .burger__line:nth-child(2) { opacity: 0; }
.burger.is-open .burger__line:nth-child(3) {
  bottom: 50%;
  transform: translateY(50%) rotate(-45deg);
}

/* スライドインするメニュー */
.sk__menu {
  position: absolute;
  z-index: 2;
  top: 77px;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  background: #fff;
  box-shadow: 0 16px 30px rgba(232,90,146,.18);
  transform: translateY(-12px);
  opacity: 0;
  pointer-events: none;
  transition: transform .3s ease, opacity .3s ease;
}
.sk__menu.is-open {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}
.sk__menu a {
  padding: 14px 24px;
  font-size: 14px;
  font-weight: 600;
  color: #6b4a58;
  text-decoration: none;
  border-bottom: 1px solid #ffeef5;
}
.sk__menu a:hover { background: #fff0f6; color: #e85a92; }

.sk__hero {
  height: 100%;
  display: grid;
  align-content: center;
  text-align: center;
  gap: 12px;
  padding: 0 24px;
}
.sk__eyebrow {
  margin: 0;
  font-size: 12px;
  letter-spacing: .2em;
  color: #e85a92;
  font-weight: 700;
}
.sk__title {
  margin: 0;
  font-size: 30px;
  font-weight: 800;
  line-height: 1.35;
  color: #4a3340;
}
.sk__lead {
  margin: 0;
  font-size: 13px;
  color: #b08a9b;
}

@media (prefers-reduced-motion: reduce) {
  .burger__line, .sk__menu { transition: none; }
}
JavaScript
// Sakuraモバイルナビ: クラスのトグルで×へモーフし、メニュー開閉とARIAを同期
(() => {
  const btn = document.querySelector('.burger');
  const menu = document.querySelector('.sk__menu');
  if (!btn) return; // null安全

  btn.addEventListener('click', () => {
    const open = btn.classList.toggle('is-open');
    btn.setAttribute('aria-expanded', String(open));
    btn.setAttribute('aria-label', open ? 'メニューを閉じる' : 'メニューを開く');
    // メニュー本体の表示と支援技術向け属性も同期
    if (menu) {
      menu.classList.toggle('is-open', open);
      menu.setAttribute('aria-hidden', String(!open));
    }
  });

  // メニュー内リンクのクリックでデモ遷移を防ぎつつ閉じる
  if (menu) menu.addEventListener('click', (e) => {
    if (!e.target.closest('a')) return;
    e.preventDefault();
    btn.classList.remove('is-open');
    btn.setAttribute('aria-expanded', 'false');
    btn.setAttribute('aria-label', 'メニューを開く');
    menu.classList.remove('is-open');
    menu.setAttribute('aria-hidden', 'true');
  });
})();

コード

HTML
<!-- ハンバーガー→×モーフ: 3本線が滑らかに×へ変形 -->
<div class="stage">
  <button class="burger" type="button" aria-label="メニューを開く" aria-expanded="false">
    <span class="burger__box" aria-hidden="true">
      <span class="burger__line burger__line--top"></span>
      <span class="burger__line burger__line--mid"></span>
      <span class="burger__line burger__line--bot"></span>
    </span>
  </button>
  <p class="hint"><span class="state">closed</span> — クリックで切り替え</p>
</div>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(circle at 50% 40%, #232946 0%, #0e1020 75%);
  color: #fffffe;
}

.stage {
  display: grid;
  place-items: center;
  gap: 24px;
}

/* ボタン枠 */
.burger {
  --size: 56px;
  width: 80px;
  height: 80px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 20px;
  cursor: pointer;
  background: linear-gradient(145deg, #2b3157, #1a1d36);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255,255,255,.06);
  transition: transform .2s ease;
}
.burger:active { transform: scale(.94); }

.burger__box {
  position: relative;
  width: 34px;
  height: 22px;
}

/* 3本の線。中央基準で transform して × へ */
.burger__line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, #eebbc3, #b8c1ff);
  transition: transform .4s cubic-bezier(.68,-0.6,.32,1.6), opacity .25s ease;
}
.burger__line--top { top: 0;   transform-origin: center; }
.burger__line--mid { top: 50%; transform: translateY(-50%); }
.burger__line--bot { bottom: 0; transform-origin: center; }

/* 開いた状態(is-open)で×に変形 */
.burger.is-open .burger__line--top {
  transform: translateY(9.5px) rotate(45deg);
}
.burger.is-open .burger__line--mid {
  opacity: 0;
  transform: translateY(-50%) scaleX(0);
}
.burger.is-open .burger__line--bot {
  transform: translateY(-9.5px) rotate(-45deg);
}

.hint {
  margin: 0;
  font-size: 13px;
  color: rgba(255, 255, 254, .55);
}
.state {
  font-weight: 700;
  color: #eebbc3;
}

@media (prefers-reduced-motion: reduce) {
  .burger__line { transition: opacity .2s ease; }
}
JavaScript
// ハンバーガー→×: クラスのトグルでCSSアニメを駆動し、ARIA属性も同期
(() => {
  const btn = document.querySelector('.burger');
  const state = document.querySelector('.state');
  if (!btn) return; // null安全

  btn.addEventListener('click', () => {
    const open = btn.classList.toggle('is-open');
    btn.setAttribute('aria-expanded', String(open));
    btn.setAttribute('aria-label', open ? 'メニューを閉じる' : 'メニューを開く');
    if (state) state.textContent = open ? 'open' : 'closed';
  });
})();

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

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

# 追加してほしい効果
ハンバーガー→×モーフ(マイクロインタラクション)
3本線がtransformで滑らかに×へ変形するメニュートグル。aria-expandedも同期し、モバイルナビの開閉ボタンにそのまま使えます。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- ハンバーガー→×モーフ: 3本線が滑らかに×へ変形 -->
<div class="stage">
  <button class="burger" type="button" aria-label="メニューを開く" aria-expanded="false">
    <span class="burger__box" aria-hidden="true">
      <span class="burger__line burger__line--top"></span>
      <span class="burger__line burger__line--mid"></span>
      <span class="burger__line burger__line--bot"></span>
    </span>
  </button>
  <p class="hint"><span class="state">closed</span> — クリックで切り替え</p>
</div>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(circle at 50% 40%, #232946 0%, #0e1020 75%);
  color: #fffffe;
}

.stage {
  display: grid;
  place-items: center;
  gap: 24px;
}

/* ボタン枠 */
.burger {
  --size: 56px;
  width: 80px;
  height: 80px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 20px;
  cursor: pointer;
  background: linear-gradient(145deg, #2b3157, #1a1d36);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255,255,255,.06);
  transition: transform .2s ease;
}
.burger:active { transform: scale(.94); }

.burger__box {
  position: relative;
  width: 34px;
  height: 22px;
}

/* 3本の線。中央基準で transform して × へ */
.burger__line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, #eebbc3, #b8c1ff);
  transition: transform .4s cubic-bezier(.68,-0.6,.32,1.6), opacity .25s ease;
}
.burger__line--top { top: 0;   transform-origin: center; }
.burger__line--mid { top: 50%; transform: translateY(-50%); }
.burger__line--bot { bottom: 0; transform-origin: center; }

/* 開いた状態(is-open)で×に変形 */
.burger.is-open .burger__line--top {
  transform: translateY(9.5px) rotate(45deg);
}
.burger.is-open .burger__line--mid {
  opacity: 0;
  transform: translateY(-50%) scaleX(0);
}
.burger.is-open .burger__line--bot {
  transform: translateY(-9.5px) rotate(-45deg);
}

.hint {
  margin: 0;
  font-size: 13px;
  color: rgba(255, 255, 254, .55);
}
.state {
  font-weight: 700;
  color: #eebbc3;
}

@media (prefers-reduced-motion: reduce) {
  .burger__line { transition: opacity .2s ease; }
}

【JavaScript】
// ハンバーガー→×: クラスのトグルでCSSアニメを駆動し、ARIA属性も同期
(() => {
  const btn = document.querySelector('.burger');
  const state = document.querySelector('.state');
  if (!btn) return; // null安全

  btn.addEventListener('click', () => {
    const open = btn.classList.toggle('is-open');
    btn.setAttribute('aria-expanded', String(open));
    btn.setAttribute('aria-label', open ? 'メニューを閉じる' : 'メニューを開く');
    if (state) state.textContent = open ? 'open' : 'closed';
  });
})();

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

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