WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

ハンバーガーメニューの作り方|HTML/CSSだけで動く実装をコピペ解説

生徒

スマホサイトでよく見る、あの3本線のハンバーガーメニューを自作したいんです。JavaScriptなしでも作れますか?

ペン博士

作れるよ!実はHTMLとCSSだけで開いたり閉じたりできるんだ。3本線が×に変わるアニメも、スライドインも全部CSSでOK。もちろんJSを使う本格版も、この記事で最後まで一緒に作っていくね!

スマートフォンでWebサイトを見ると、画面の右上や左上に3本の横線が並んだアイコンが置いてあることがほとんどです。タップするとメニューがにゅっと開く、あの部品を「ハンバーガーメニュー」と呼びます。パンのバンズと具材を横から見た形に似ていることが名前の由来です。

「作るのが難しそう」「JavaScriptが必要なんでしょう?」と身構えてしまう方が多いのですが、実はHTMLとCSSだけで動くハンバーガーメニューを作れます。この記事では、まずJavaScriptを一切使わない純CSS版を完全なコードで組み立て、そのあとスライドインやオーバーレイなどのバリエーション、さらにJavaScript版、アクセシビリティ対応、レスポンシブ切り替えまで、一つずつコピペできる形で解説していきます。

コードはすべて動く状態で載せています。まずは何も考えずに貼り付けて動かし、そのあと「なぜ動くのか」を読んで理解を深めていくのがおすすめです。手を動かしながら読み進めてください。

最初に、この記事の結論をまとめておきます。

  • ハンバーガーメニューはJavaScriptなしでも作れる<input type="checkbox"><label>、そして :checked を組み合わせる「チェックボックスハック」が定番です。
  • 3本線は要素1つ+擬似要素で作るspan の本体と ::before / ::after で3本の線を表現し、開いたときに×へ変形させます。
  • 見た目の切り替えは全部CSSの:checkedで完結する。チェックが入った状態を基準に、メニューを表示したり線を回転させたりします。
  • 本格的なサイトではJavaScript版のほうが扱いやすいaria-expanded の切り替えやEscキーで閉じる、外側クリックで閉じるといった配慮を入れやすいためです。
  • アクセシビリティとレスポンシブの設計が仕上げの決め手button 要素の採用、キーボード操作、PCとスマホの表示切り替えまで整えて初めて実務レベルになります。

目次

ハンバーガーメニューとは?なぜ3本線なのかを理解しよう

ハンバーガーメニューとは、複数のナビゲーションリンクを1つのアイコンの中に隠しておき、タップ(クリック)で開閉できるようにしたUIパーツのことです。主にスマートフォンのように画面が狭い環境で、ナビゲーションの場所を節約する目的で使われます。

パソコンの画面は横に広いので、「ホーム」「サービス」「会社概要」「お問い合わせ」といったメニューを横一列に並べられます。しかしスマートフォンの縦長で狭い画面では、同じように並べると窮屈になり、コンテンツを表示する場所が減ってしまいます。そこで普段はメニューを隠しておき、必要なときだけ開くという発想が生まれました。それがハンバーガーメニューです。

たとえば横幅375pxほどのスマートフォンで、メニュー項目が5つも6つもあると、それだけで画面の上部が文字で埋め尽くされてしまいます。ユーザーが本当に読みたいのは記事や商品の情報であって、メニューではありません。ハンバーガーメニューは、限られた画面をコンテンツのために最大限あけるための、スマートフォン時代の合理的な解決策なのです。今ではほとんどのスマートフォン向けサイトで採用されている、いわば標準的なUIになっています。

3本線が「メニュー」を意味する理由

なぜ3本の横線なのでしょうか。これは1980年代のコンピュータUIにさかのぼる歴史のあるアイコンで、リストの項目が並んでいる様子を線で抽象化したものだと考えられています。文字を書かなくても「ここに項目のリストがある」と直感的に伝わるため、世界中のサイトやアプリで共通の記号として定着しました。

線が3本なのは、少なすぎると「メニュー」だと伝わりにくく、多すぎると別の意味(設定やリストなど)に見えてしまうためです。3本がもっともバランスよく「開くメニュー」を表せる本数として使われ続けています。

開閉の仕組みは「状態の切り替え」だけ

ハンバーガーメニューの動作を分解すると、実はとてもシンプルです。「閉じている状態」と「開いている状態」の2つを切り替えているだけです。閉じているときはメニューを隠し、開いているときはメニューを見せる。この状態の切り替えを、CSSでやるかJavaScriptでやるかの違いだけなのです。

この記事では、まずCSSだけで状態を切り替える方法から始めます。状態を持つ仕組みとして、チェックボックスの「オン・オフ」を利用するのがポイントです。

この記事で作るものの全体像

先に完成イメージを共有しておきます。これから作るのは、次のような部品です。

  • 画面右上に置く3本線のアイコン(タップできるボタン)
  • タップすると開くメニューパネル(縦にリンクが並ぶ)
  • 開いたときに3本線が×印に変形するアニメーション
  • もう一度タップ、または外側をタップで閉じる動作

これらを、まずはCSSだけで、次にJavaScriptでも実装していきます。それぞれ長所と短所があるので、両方を知っておくと現場で使い分けられます。


JavaScriptなしで作る仕組み「チェックボックスハック」とは

チェックボックスハックとは、画面上に隠したチェックボックスのオン・オフ状態を利用して、JavaScriptを使わずに開閉やタブ切り替えを実現するテクニックです。ハンバーガーメニューを純CSSで作る際の中心的な手法になります。

仕組みの核になるのは、次の3つの要素の組み合わせです。順番に役割を確認しましょう。

① チェックボックス(状態を覚える箱)

<input type="checkbox"> は、チェックが入っているか・いないかというオンとオフの2つの状態を自分で覚えてくれる部品です。この「状態を覚える」性質を、メニューの「開いている・閉じている」に対応させます。チェックが入っていれば開いている、入っていなければ閉じている、という具合です。

② ラベル(クリックのきっかけを作る)

<label> は本来、入力欄に説明ラベルを付けるための要素ですが、labelをクリックすると、結びついたチェックボックスのオン・オフが切り替わるという便利な性質があります。labelfor 属性に、チェックボックスの id と同じ値を指定すると両者が結びつきます。

この性質を使えば、チェックボックス自体は画面から隠しておき、代わりに3本線のアイコンを label にして「タップできるボタン」に見せられます。ユーザーはアイコンをタップしているつもりでも、裏ではチェックボックスが切り替わっているのです。

③ :checked 疑似クラス(状態に応じて見た目を変える)

:checked は、チェックが入っている状態のときだけスタイルを当てられるCSSの疑似クラスです。これと、隣の要素を指す「兄弟結合子」を組み合わせます。

/* チェックが入ったとき、その後ろにあるメニューを表示する */
#menu-toggle:checked ~ .menu {
  /* ここに「開いた状態」のスタイルを書く */
}

~(チルダ)は「同じ親を持つ、後ろにある兄弟要素」を指す記号です。+(プラス)は「すぐ隣の兄弟」だけを指しますが、~ は間に他の要素が挟まっていても後ろにあれば対象になります。この結合子のおかげで、チェックボックスの状態に応じて離れた場所のメニューを操作できるのです。

少し整理すると、CSSには要素どうしの位置関係を表す記号がいくつかあります。> は「直接の子」、+ は「すぐ次の兄弟」、~ は「後ろに続くすべての兄弟」を指します。ハンバーガーメニューでは、チェックボックスから見て後ろに置いたアイコンやメニューをまとめて操作したいので、~(後ろの兄弟すべて)が最適というわけです。ここを + にしてしまうと、間に別の要素が挟まった瞬間に効かなくなるので注意してください。

要素を並べる順番が超重要

チェックボックスハックには1つだけ守るべきルールがあります。それはチェックボックスを、影響を与えたい要素より前(上)に置くことです。CSSの兄弟結合子は「後ろの兄弟」しか指せないため、順番を逆にすると動きません。HTMLでは「チェックボックス → ラベル → メニュー」の順に並べるのが基本形になります。

この前提を頭に入れたうえで、次の章から実際に完全なコードを組み立てていきます。


純CSS版ハンバーガーメニューの完成コード【HTML編】

まずはHTMLの全体を作ります。ここではチェックボックス・ラベル(3本線)・メニュー本体の3点セットを、正しい順番で並べます。

<!-- ① 状態を覚えるチェックボックス(画面上は隠す) -->
<input type="checkbox" id="menu-toggle" class="menu-toggle">

<!-- ② 3本線のアイコン(labelでタップ可能に) -->
<label for="menu-toggle" class="menu-btn">
  <span class="menu-btn-line"></span>
</label>

<!-- ③ 開くメニュー本体 -->
<nav class="menu">
  <ul>
    <li><a href="#">ホーム</a></li>
    <li><a href="#">サービス</a></li>
    <li><a href="#">実績紹介</a></li>
    <li><a href="#">会社概要</a></li>
    <li><a href="#">お問い合わせ</a></li>
  </ul>
</nav>

ポイントを1つずつ確認します。

  • <input> を一番上に置く:後ろの兄弟要素(アイコンとメニュー)にCSSで影響を与えるためです。順番を逆にすると動きません。
  • idfor を一致させるid="menu-toggle"for="menu-toggle" が同じ値なので、ラベルをタップするとチェックボックスが切り替わります。
  • 3本線は span 1つで作る:本体を真ん中の線にして、残り2本は擬似要素で作ります。詳しくはCSS編で解説します。
  • メニューは <nav> でマークアップ:ナビゲーションであることを意味的に示すために navul/li を使います。

HTMLはこれだけです。この時点ではまだスタイルがないので、チェックボックスもリンクもそのまま画面に表示されています。次のCSS編で、一気にハンバーガーメニューらしい見た目と動きを付けていきます。


純CSS版ハンバーガーメニューの完成コード【CSS編】

いよいよCSSです。長く見えますが、やっていることは「チェックボックスを隠す」「3本線を作る」「開いたときの見た目を指定する」の3ステップだけです。まず完成形の全コードを載せ、そのあとブロックごとに解説します。

/* ===== ① チェックボックス本体は画面から隠す ===== */
.menu-toggle {
  display: none;
}

/* ===== ② 3本線ボタン(label)===== */
.menu-btn {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 100;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

/* 真ん中の線 */
.menu-btn-line,
.menu-btn-line::before,
.menu-btn-line::after {
  content: "";
  display: block;
  width: 28px;
  height: 3px;
  background: #333;
  border-radius: 2px;
  transition: all 0.3s ease;
}

/* 上下の線を擬似要素で作り、上下にずらす */
.menu-btn-line::before,
.menu-btn-line::after {
  position: absolute;
}
.menu-btn-line::before { transform: translateY(-9px); }
.menu-btn-line::after  { transform: translateY(9px); }

/* ===== ③ メニュー本体(初期状態=隠れている)===== */
.menu {
  position: fixed;
  top: 0;
  right: 0;
  width: 260px;
  height: 100%;
  background: #1e293b;
  padding: 80px 24px;
  box-sizing: border-box;
  transform: translateX(100%); /* 画面右の外へ隠す */
  transition: transform 0.3s ease;
  z-index: 90;
}
.menu ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu li { margin-bottom: 8px; }
.menu a {
  display: block;
  padding: 12px 8px;
  color: #fff;
  text-decoration: none;
  border-radius: 6px;
  transition: background 0.2s;
}
.menu a:hover { background: rgba(255,255,255,0.1); }

/* ===== ④ チェックが入ったとき(=開いたとき)===== */
/* メニューを画面内へスライドイン */
.menu-toggle:checked ~ .menu {
  transform: translateX(0);
}

/* 真ん中の線を消す */
.menu-toggle:checked ~ .menu-btn .menu-btn-line {
  background: transparent;
}
/* 上下の線を回転させて×にする */
.menu-toggle:checked ~ .menu-btn .menu-btn-line::before {
  transform: translateY(0) rotate(45deg);
}
.menu-toggle:checked ~ .menu-btn .menu-btn-line::after {
  transform: translateY(0) rotate(-45deg);
}

上のコードで、タップするとメニューが右からスライドインし、3本線が×に変わるハンバーガーメニューが完成します。行数は多いですが、丸暗記する必要はありません。「隠す」「線を作る」「開いた状態を書く」の3ステップに分けて眺めれば、意外と単純だと分かるはずです。ここから各ブロックを分解して理解していきましょう。読んだあとにもう一度コード全体を見返すと、それぞれの行が何をしているかがはっきりつかめるはずです。

① チェックボックスを隠す

.menu-toggle { display: none; }チェックボックスの四角い見た目を画面から消しています。ただし要素自体は存在し続けるので、チェック状態は変わらず記憶されます。ユーザーには見えなくても、裏でオン・オフのスイッチとして働いているのです。

なお display: none; で完全に消すと、キーボードのTabキーで選べなくなります。アクセシビリティを重視する場合は後述の方法で画面外へ逃がすやり方もありますが、まずは動きを理解するためにシンプルに隠しています。

② 3本線を1つのspanで作る

ここがCSSの見せ場です。3本の線を、それぞれ別のHTML要素で用意するのではなく、1つのspanを真ん中の線にして、上下の線は::before::afterという擬似要素で生成しています。擬似要素とは、CSSだけで「見えない要素を1つ追加する」機能です。

content: ""; を指定すると擬似要素が実体を持ちます。3本とも同じ幅・高さ・色にし、上の線は translateY(-9px) で上へ、下の線は translateY(9px) で下へずらすことで、等間隔の3本線が完成します。transition を全体にかけているので、後述の変形がなめらかにアニメーションします。

③ メニューを画面の外へ隠す

メニュー本体は position: fixed; で画面に固定し、transform: translateX(100%);自分の幅のぶんだけ右へずらして画面外に追い出していますdisplay: none; ではなく transform で外に出す理由は、位置の変化をアニメーションさせられるからです。display の切り替えはアニメーションできません。

④ :checkedで開いた状態を指定する

最後に、チェックが入ったときのスタイルをまとめて書きます。.menu-toggle:checked ~ .menuメニューのtransformtranslateX(0)に戻して画面内へ滑り込ませ、同時に真ん中の線を透明にし、上下の線を rotate(45deg)rotate(-45deg) で回転させて×印を作っています。

translateY0 に戻してから回転させるのがコツです。これで上下の線が中央に集まり、45度と-45度で交差して×になります。transition のおかげで、線がぐるっと回りながら×に変わるアニメーションになります。


3本線が「×」に変わるアニメーションを深掘りする

初心者が一番「おっ」と感じるのが、3本線が×に変わる動きです。ここではその変形が起きる仕組みを座標レベルで理解し、思いどおりに調整できるようにします。

なぜ真ん中の線を「透明」にするのか

×印は2本の斜め線でできています。つまり×を作るには線が2本あれば足りるのです。3本のうち真ん中の1本は余るので、background: transparent; で見えなくしています。display: none; ではなく透明にするのは、消える瞬間もアニメーションでふわっと見せるためです。

上下の線を中央に集めてから回す

閉じている状態では、上の線は -9px、下の線は 9px の位置にあります。開いた瞬間、両方を translateY(0) にしていったん中央の同じ高さに重ねてから、片方を +45度、もう片方を -45度 回転させます。すると2本が交差して×になるわけです。移動と回転を1つの transform にまとめて書くのがポイントです。

/* 移動と回転はスペース区切りで同時に指定できる */
.menu-toggle:checked ~ .menu-btn .menu-btn-line::before {
  transform: translateY(0) rotate(45deg);
}

注意点として、transform は複数の値を書くと右から順に適用される性質があります。translateY(0) rotate(45deg) の場合は「回転してから移動」ではなく「移動してから回転」に見えますが、値の順序を変えると軸のずれ方が変わるので、思いどおりに交差しないときは順序を入れ替えて調整してください。

アニメの速さと動きを調整する

動きの印象は transition の値で決まります。0.3s の部分を大きくするとゆっくり、小さくするとキビキビ動きます。ease の部分を cubic-bezier() に変えると、跳ねるような独特の動きも作れます。

/* ゆっくり・弾むような変形にしたいとき */
.menu-btn-line,
.menu-btn-line::before,
.menu-btn-line::after {
  transition: all 0.45s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}

動きにこだわりすぎると読み込みが重くなったり、酔いやすいユーザーに負担をかけたりします。速さは 0.3s 前後を基準に、控えめに設定するのが無難です。


開き方のバリエーション6種【全てコピペ可能】

メニューの「開き方」は好みや設計に合わせて選べます。ここでは代表的な6パターンを紹介します。どれも先ほどのHTMLはそのまま使い、.menuまわりのCSSを差し替えるだけで切り替えられます。

1. 右からスライドイン(先ほどの基本形)

すでに作った、画面の右端からメニューが滑り込むタイプです。もっとも定番で失敗が少ないので、迷ったらこれを選べば問題ありません。

.menu {
  right: 0;
  transform: translateX(100%);
  transition: transform 0.3s ease;
}
.menu-toggle:checked ~ .menu {
  transform: translateX(0);
}

2. 左からスライドイン

左端から出したいときは、位置を左に変え、隠す方向を逆にします。translateX(-100%)で左の外へ隠すのがポイントです。

.menu {
  left: 0;
  transform: translateX(-100%);
  transition: transform 0.3s ease;
}
.menu-toggle:checked ~ .menu {
  transform: translateX(0);
}

3. ふわっとフェードイン

スライドではなく、その場で透明度だけを変えて浮かび上がらせる落ち着いた演出です。opacityvisibility を組み合わせます。visibility を切り替えるのは、透明でもリンクを押せてしまう状態を防ぐためです。

.menu {
  right: 0;
  transform: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
.menu-toggle:checked ~ .menu {
  opacity: 1;
  visibility: visible;
}

4. 画面全体を覆うフルスクリーンオーバーレイ

メニューで画面いっぱいを覆うインパクトのあるタイプです。ブランドサイトなどでよく見かけます。幅と高さを100%にし、リンクを中央に大きく配置します。

.menu {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(15, 23, 42, 0.97);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
.menu ul { text-align: center; }
.menu a { font-size: 24px; }
.menu-toggle:checked ~ .menu {
  opacity: 1;
  visibility: visible;
}

5. 上からドロップダウン

ヘッダーの下からにゅっと下方向に降りてくるタイプです。translateY(-100%) で上の外へ隠しておき、開いたときに translateY(0) で降ろします。

.menu {
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  transform: translateY(-100%);
  transition: transform 0.3s ease;
}
.menu-toggle:checked ~ .menu {
  transform: translateY(0);
}

6. 本文をずらすプッシュ型

メニューが本文の上に重なるのではなく、本文全体を横に押しやってメニューのスペースを空けるタイプです。ページ全体を包むラッパー要素を1つ用意し、その transform を動かします。HTMLの構造だけ少し変わる点に注意してください。

<!-- メニュー以外の全コンテンツを .page で包む -->
<input type="checkbox" id="menu-toggle" class="menu-toggle">
<label for="menu-toggle" class="menu-btn"><span class="menu-btn-line"></span></label>
<nav class="menu"> ... </nav>
<div class="page">
  <!-- ここにページ本文が入る -->
</div>
.page {
  transition: transform 0.3s ease;
}
.menu-toggle:checked ~ .page {
  transform: translateX(-260px); /* メニュー幅ぶん左へ */
}

プッシュ型は動きが独特で印象的ですが、本文がずれるぶんレイアウト崩れが起きやすいので、横スクロールが出ないよう body { overflow-x: hidden; } を添えておくと安全です。

バリエーションの選び方

演出が派手なほど良いわけではありません。情報を素早く見せたいメディアやコーポレートサイトなら右スライドやドロップが無難で、世界観を打ち出したいブランドサイトならフルスクリーンオーバーレイが映えます。用途に合わせて選んでください。

開き方 印象 向いているサイト
右/左スライド 定番・軽快 コーポレート・メディア
フェード 落ち着き・上品 ポートフォリオ・士業
フルスクリーン インパクト大 ブランド・LP
ドロップダウン 自然・素直 情報量の多いサイト
プッシュ 独特・遊び 個性を出したいサイト

開いたときに背景を暗くする「オーバーレイ」を追加する

メニューを開いたとき、背後のコンテンツを半透明の黒で覆うと、メニューが浮き立ってプロらしい仕上がりになります。この暗幕は「オーバーレイ」と呼ばれ、CSSだけでも作れます。

暗幕用の要素を1つ足す

HTMLに、背景を覆うための空の要素を1つ加えます。これも label にしておくと、暗幕をタップしてメニューを閉じる機能まで同時に手に入ります。

<input type="checkbox" id="menu-toggle" class="menu-toggle">
<label for="menu-toggle" class="menu-btn"><span class="menu-btn-line"></span></label>
<label for="menu-toggle" class="overlay"></label> <!-- 暗幕 -->
<nav class="menu"> ... </nav>
.overlay {
  position: fixed;
  inset: 0; /* top/right/bottom/left をまとめて0に */
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
  z-index: 80; /* メニューより下、本文より上 */
}
.menu-toggle:checked ~ .overlay {
  opacity: 1;
  visibility: visible;
}

inset: 0;top: 0; right: 0; bottom: 0; left: 0; をまとめて書ける便利な指定で、画面いっぱいに広げられます。z-indexメニュー(90)より小さく、本文より大きい値(80)にして、暗幕がメニューの下・本文の上に重なるよう調整します。

この暗幕は「開いていることが分かりやすくなる」だけでなく、「どこをタップすれば閉じられるか」をユーザーに直感的に伝える役割も果たします。純CSS版でも label を使えば外側タップで閉じられるようになるのは大きな利点です。


純CSS版とJavaScript版はどう使い分ける?

ここまでで純CSS版が一通り完成しました。次の章からJavaScript版に進みますが、その前に2つの方式の違いと使い分けの基準をはっきりさせておきましょう。どちらが優れているという話ではなく、場面によって向き不向きがあります。

それぞれの長所と短所

まずは特徴を表で比べてみます。自分が作ろうとしているサイトに、どちらが合うかを考えながら読んでください。

観点 純CSS版 JavaScript版
作りやすさ 手軽・行数少なめ コードは増える
表示速度 軽い(JS読み込み不要) わずかに重い
Escキーで閉じる 苦手 得意
外側クリックで閉じる 工夫が必要 得意
リンク選択で閉じる 苦手 得意
状態の読み上げ対応 限定的 細かく制御できる
向いている規模 小規模・学習用 本格的なサイト

表を見ると、シンプルさなら純CSS版、機能の細かさならJavaScript版という傾向がはっきりします。純CSS版はJavaScriptを1行も書かずに動くので、学習の最初の一歩や、機能が最小限でよい個人サイトに向いています。

判断のフローチャート

迷ったときは、次の順番で考えると決めやすいです。

  1. リンク選択・外側クリック・Escで閉じたいか? → はいならJavaScript版
  2. 読み上げソフト対応をしっかりやりたいか? → はいならJavaScript版
  3. どちらも不要で、とにかく軽く手早く作りたい → 純CSS版で十分

実務で納品するサイトなら、アクセシビリティと操作性を優先してJavaScript版を選ぶことがほとんどです。一方、学習中の方はまず純CSS版で仕組みを理解してから、JavaScript版へ発展させるのが遠回りに見えて一番の近道です。この記事の順番どおりに進めれば、その流れを自然にたどれます。


JavaScriptで作るハンバーガーメニューの実装

ここからはJavaScript版です。純CSS版は手軽ですが、Escキーで閉じる・外側クリックで閉じる・状態を読み上げソフトに正しく伝えるといった細かな制御はJavaScriptのほうが得意です。実務ではこちらを使う場面が多いので、しっかり押さえましょう。

HTMLはbutton要素で作る

JS版では、チェックボックスの代わりに本物のボタンである<button>要素を使います。ボタンはキーボードで押せる、読み上げソフトが「ボタン」と認識するなど、最初からアクセシビリティに優れているためです。

<button class="menu-btn" aria-label="メニューを開く"
        aria-expanded="false" aria-controls="global-menu">
  <span class="menu-btn-line"></span>
</button>

<nav class="menu" id="global-menu">
  <ul>
    <li><a href="#">ホーム</a></li>
    <li><a href="#">サービス</a></li>
    <li><a href="#">お問い合わせ</a></li>
  </ul>
</nav>

属性の意味は後半のアクセシビリティ章で詳しく説明します。ここでは aria-expanded(開いているか)と aria-controls(どのメニューを操作するか)を付けている、とだけ覚えておいてください。

CSSは「.is-open」クラスで切り替える

JS版では、:checked の代わりに.is-openというクラスの有無で見た目を切り替えます。JavaScriptがこのクラスを付けたり外したりします。

.menu {
  position: fixed;
  top: 0; right: 0;
  width: 260px;
  height: 100%;
  background: #1e293b;
  transform: translateX(100%);
  transition: transform 0.3s ease;
}
/* クラスが付いたら開く */
.menu.is-open {
  transform: translateX(0);
}
/* ボタン側の×変形も同じくクラスで */
.menu-btn.is-open .menu-btn-line { background: transparent; }
.menu-btn.is-open .menu-btn-line::before { transform: translateY(0) rotate(45deg); }
.menu-btn.is-open .menu-btn-line::after  { transform: translateY(0) rotate(-45deg); }

基本のJavaScript(classList.toggle)

いよいよJavaScriptです。中心になるのはclassList.toggle()という命令で、クラスが付いていなければ付け、付いていれば外す、という切り替えを1行で行えます。

const btn = document.querySelector(".menu-btn");
const menu = document.querySelector(".menu");

btn.addEventListener("click", () => {
  // ボタンとメニューの両方に is-open を付け外し
  const isOpen = menu.classList.toggle("is-open");
  btn.classList.toggle("is-open", isOpen);

  // 状態を支援技術に伝える
  btn.setAttribute("aria-expanded", String(isOpen));
  btn.setAttribute("aria-label", isOpen ? "メニューを閉じる" : "メニューを開く");
});

toggle()実行後にクラスが付いているかどうかをtruefalseで返してくれます。この戻り値を isOpen に受け取り、aria-expanded の値やラベルの文言を同時に更新しているのがポイントです。これで見た目と「意味」の両方が正しく切り替わります。

Escキーで閉じられるようにする

キーボードだけで操作する人のために、Escキーでメニューを閉じられるようにしましょう。開いているときだけEscを受け付けます。

document.addEventListener("keydown", (e) => {
  if (e.key === "Escape" && menu.classList.contains("is-open")) {
    closeMenu();
  }
});

function closeMenu() {
  menu.classList.remove("is-open");
  btn.classList.remove("is-open");
  btn.setAttribute("aria-expanded", "false");
  btn.setAttribute("aria-label", "メニューを開く");
  btn.focus(); // フォーカスをボタンに戻す
}

閉じる処理は複数の場所(Escキー・外側クリック・リンク選択)から呼ばれるので、closeMenu() という関数にまとめて再利用しています。閉じた後に btn.focus() でフォーカスをボタンへ戻すのは、キーボード利用者が操作の続きをしやすくするための配慮です。

外側をクリックしたら閉じる

メニューの外側をクリックしたときに閉じる挙動も、多くのサイトで採用されています。クリックされた場所がメニューでもボタンでもなければ閉じる、という判定で実現します。

document.addEventListener("click", (e) => {
  const clickedInside = menu.contains(e.target) || btn.contains(e.target);
  if (!clickedInside && menu.classList.contains("is-open")) {
    closeMenu();
  }
});

contains() は「その要素の内側に、指定した要素が含まれるか」を判定するメソッドです。e.target(クリックされた実際の要素)がメニュー内でもボタン内でもなければ、外側とみなして閉じます。ボタン自身のクリックを除外しないと開いた瞬間に即閉じてしまうので、必ずボタンも判定に含めてください。


アクセシビリティ対応で「誰でも使えるメニュー」にする

アクセシビリティとは、目が見えにくい人・マウスが使えない人・音声で操作する人など、あらゆる利用者が同じように使えるようにする配慮のことです。ハンバーガーメニューは操作の起点になる重要な部品なので、ここを丁寧に作ると一気にプロらしくなります。

必ずbutton要素を使う

クリックできる部品を <div><span> で作ってしまう人が多いのですが、これはNGです。開閉を切り替えるボタンは必ず<button>要素で作りましょうbutton は次の性質を最初から備えています。

  • キーボードのTabキーで選べ、Enter/Spaceキーで押せる
  • 読み上げソフトが「ボタン」と正しく読み上げる
  • フォーカスされたときの枠(フォーカスリング)が自動で出る

div で同じことをやろうとすると、tabindex やキー操作の処理を自分で書かなければならず、抜け漏れが起きがちです。最初から button を使えば、これらがすべて無料で手に入ります。

aria-controls と aria-expanded を付ける

読み上げソフトに「このボタンが何を操作し、いま開いているのか閉じているのか」を伝えるのがaria-controlsaria-expandedです。

属性 役割 値の例
aria-controls 操作対象のメニューのidを指す global-menu
aria-expanded いま開いているか true / false
aria-label ボタンの説明文 メニューを開く

重要なのは、aria-expanded開閉に合わせてJavaScriptで必ず更新することです。見た目だけ変えて属性を放置すると、読み上げソフトを使う人には「閉じたまま」に聞こえてしまい、状態が食い違います。前章のコードで setAttribute("aria-expanded", ...) を入れていたのはこのためです。

キーボードだけで完結できるようにする

マウスを使わない利用者のために、キーボードだけで開く・選ぶ・閉じるが完結することを確認します。チェック項目は次のとおりです。

  • Tabキーでハンバーガーボタンにたどり着けるか
  • Enter/Spaceキーでメニューを開けるか(buttonなら自動で対応)
  • 開いたあと、Tabキーでメニュー内のリンクを順にたどれるか
  • Escキーでメニューを閉じられるか
  • 閉じたあと、フォーカスがボタンへ戻るか

動きを控える設定(prefers-reduced-motion)に対応する

画面の動きで気分が悪くなる人のために、OSには「視差効果を減らす」という設定があります。この設定をしている人にはアニメーションを控えるのが親切です。CSSの prefers-reduced-motion で対応できます。

@media (prefers-reduced-motion: reduce) {
  .menu,
  .menu-btn-line,
  .menu-btn-line::before,
  .menu-btn-line::after {
    transition: none;
  }
}

この指定を入れておくと、動きを減らす設定の人にはアニメーションなしで即座に切り替わり、それ以外の人にはこれまでどおりなめらかに動きます。両方に配慮できる、覚えておきたいテクニックです。

メニューを開いたら背景のスクロールを止める

メニューを開いたまま背景がスクロールできてしまうと操作しづらいので、開いている間だけbodyのスクロールを固定します。JavaScriptで body にクラスを付けて制御します。

/* CSS:この状態のときスクロールを止める */
body.menu-open {
  overflow: hidden;
}
// JS:開閉に合わせて body のクラスも切り替える
document.body.classList.toggle("menu-open", isOpen);

overflow: hidden;body に付けると、スクロール操作を受け付けなくなります。閉じるときにクラスを外すのを忘れるとページ全体がスクロールできなくなるので、開閉の両方で確実に切り替えてください。


レスポンシブ対応:PCは通常ナビ、スマホはハンバーガー

実際のサイトでは、PCでは横並びのナビゲーション、スマホではハンバーガーメニューというように、画面幅で表示を切り替えるのが一般的です。この切り替えはCSSの「メディアクエリ」で行います。

メディアクエリで表示を切り替える

メディアクエリとは、画面幅などの条件に応じて適用するCSSを切り替える仕組みです。@media (max-width: 768px) と書くと「画面幅が768px以下のときだけ」という意味になります。

/* --- PC(初期状態):ナビは横並び、ハンバーガーは隠す --- */
.menu {
  position: static;
  transform: none;
  width: auto;
  height: auto;
  background: transparent;
}
.menu ul { display: flex; gap: 24px; }
.menu a { color: #333; }
.menu-btn { display: none; } /* PCでは3本線を消す */

/* --- スマホ(768px以下):ハンバーガー表示に切り替え --- */
@media (max-width: 768px) {
  .menu-btn { display: flex; } /* 3本線を表示 */
  .menu {
    position: fixed;
    top: 0; right: 0;
    width: 260px;
    height: 100%;
    background: #1e293b;
    transform: translateX(100%);
    transition: transform 0.3s ease;
  }
  .menu ul { display: block; }
  .menu a { color: #fff; }
  .menu.is-open { transform: translateX(0); }
}

考え方はシンプルです。PC用のスタイルを基本として書き、スマホ用の変更点だけをメディアクエリの中で上書きしています。PCではハンバーガーボタンを display: none; で隠し、メニューを普通の横並びナビとして見せます。スマホになったらボタンを表示し、メニューを画面外へ隠すハンバーガー仕様に切り替えるわけです。

ブレイクポイントは何pxにすべきか

切り替える境目の幅を「ブレイクポイント」と呼びます。768pxが定番で、タブレット縦向きの境目にあたります。ナビの項目が多くて横並びに収まらない場合は、900pxや1024pxなど大きめにするのも手です。実際にブラウザの幅を縮めながら、横並びが窮屈になり始める幅を探して決めましょう。

min-widthとmax-widthの使い分け

メディアクエリには max-width(〜以下)と min-width(〜以上)の2つがあります。「スマホを基本に、PCで上書きする」設計ならmin-width、「PCを基本にスマホで上書きする」設計ならmax-widthを使います。どちらでも作れますが、近年は軽いスマホ表示を基本にする「モバイルファースト」の考え方から min-width が好まれます。

/* モバイルファースト:スマホが基本、PCで上書き */
.menu-btn { display: flex; } /* スマホ:ボタン表示 */

@media (min-width: 769px) {
  .menu-btn { display: none; } /* PC:ボタンを隠す */
  .menu ul { display: flex; }  /* PC:横並び */
}

どちらの書き方でも結果は同じにできます。プロジェクトの方針に合わせ、チーム内で統一するのが一番大切です。


使いやすく見せるデザインのコツと、ヘッダーへの組み込み

コードが動くようになったら、次は「使いやすく、迷わせない」ための見た目の調整です。ハンバーガーメニューは便利な反面、使い方を誤ると「メニューがどこにあるか分からない」という不満を生みやすい部品でもあります。この章ではまずUXのコツを押さえ、後半で実戦的なヘッダーの完成例まで組み立てます。

アイコンに「メニュー」という文字を添える

3本線だけだと、Webに不慣れな人には「これがメニューだ」と伝わりにくいことがあります。アイコンの下や横に小さく「MENU」と文字を添えるだけで、分かりやすさが大きく上がります。研究でも、文字ラベル付きのほうがタップ率が高い傾向が知られています。

<button class="menu-btn" aria-label="メニュー" aria-expanded="false">
  <span class="menu-btn-line"></span>
  <span class="menu-btn-text">MENU</span>
</button>
.menu-btn {
  flex-direction: column;
  gap: 4px;
}
.menu-btn-text {
  font-size: 10px;
  letter-spacing: 0.05em;
  color: #333;
}

タップできる範囲を十分に広く取る

スマートフォンは指で操作するため、ボタンの当たり判定は最低44px四方を確保するのが目安です。3本線の見た目は細くても、buttonwidthheightpadding を広げて、押しやすい大きさにしましょう。当たり判定が小さいと「押したのに反応しない」というストレスの原因になります。

開いている状態を分かりやすく示す

メニューが開いたとき、3本線が×に変わる・背景が暗くなるといった変化をきちんと付けると、ユーザーは「いま開いている」「もう一度押せば閉じる」と直感的に理解できます。開閉の状態がひと目で分からないと、同じボタンを何度も押してしまう混乱を招きます。この記事で作った×への変形と暗幕は、まさにこの分かりやすさのための工夫です。

色のコントラストを確保する

3本線やメニュー内の文字は、背景との明るさの差(コントラスト)を十分に取ることが大切です。薄いグレーの線を白い背景に置くと、目が疲れている人や明るい屋外では見えにくくなります。文字と背景のコントラスト比は、一般的なアクセシビリティ指針で本文なら4.5対1以上が推奨されています。濃い色の線・はっきりした文字色を選びましょう。

アニメーションはやりすぎない

凝った動きは楽しいですが、開閉のたびに派手なアニメが走ると、かえって操作のテンポを損ねます。メニューは「素早く開いて、素早く使える」ことが最優先です。transition は0.3秒前後を基準に、控えめでキビキビした動きにまとめると、上品で使いやすい印象になります。前述の prefers-reduced-motion 対応も忘れずに入れておきましょう。

ここまでは部品を単体で作ってきました。ここからは、ロゴ・ハンバーガーボタン・メニューを1つのヘッダーとしてまとめた実戦的な完成例を組み立てます。コピーして自分のサイトの土台にできる形にしてあります。

ヘッダー全体のHTML構造

ヘッダーは「ロゴ」と「ナビゲーション」の2つで構成するのが基本です。<header>の中に、左側にロゴ、右側にハンバーガーボタンとメニューを配置します。意味のあるHTMLタグを使うことで、検索エンジンや読み上げソフトにも構造が正しく伝わります。

<header class="site-header">
  <a href="/" class="logo">WithCode</a>

  <button class="menu-btn" aria-label="メニューを開く"
          aria-expanded="false" aria-controls="global-menu">
    <span class="menu-btn-line"></span>
  </button>

  <nav class="menu" id="global-menu">
    <ul>
      <li><a href="/">ホーム</a></li>
      <li><a href="/service/">サービス</a></li>
      <li><a href="/works/">実績紹介</a></li>
      <li><a href="/about/">会社概要</a></li>
      <li><a href="/contact/">お問い合わせ</a></li>
    </ul>
  </nav>
</header>

<header> はページの見出し領域を表す要素で、<nav> はナビゲーションを表す要素です。この2つを使うだけで、「ここはヘッダーで、この中にナビゲーションがある」という意味が機械にも伝わります。class 名は自由に付けて構いませんが、役割が分かる名前にしておくと後から見返したときに理解しやすくなります。

ヘッダーのCSS(PC表示)

まずはPCでの見た目を整えます。ロゴを左、ナビを右に配置し、display: flex;justify-content: space-between;で両端に振り分けます。ハンバーガーボタンはPCでは不要なので隠しておきます。

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 32px;
  background: #fff;
  box-shadow: 0 1px 6px rgba(0,0,0,0.08);
  position: sticky;
  top: 0;
  z-index: 50;
}
.logo {
  font-size: 22px;
  font-weight: bold;
  color: #1e293b;
  text-decoration: none;
}
.menu ul {
  display: flex;
  gap: 28px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu a {
  color: #333;
  text-decoration: none;
  font-weight: 600;
}
.menu a:hover { color: #2563eb; }
.menu-btn { display: none; } /* PCでは隠す */

position: sticky; top: 0; を指定すると、スクロールしてもヘッダーが画面上部に貼り付いたままになります。ナビゲーションが常に見えるので、ユーザーがいつでもメニューにアクセスできて便利です。ヘッダーの z-index は50にして、本文より手前・開いたメニューより奥に来るよう調整しています。

ヘッダーのCSS(スマホ表示)

次に、スマホ幅になったときのスタイルをメディアクエリで上書きします。ハンバーガーボタンを表示し、ナビを画面外へ隠すハンバーガー仕様に切り替えます。

@media (max-width: 768px) {
  .site-header { padding: 12px 20px; }
  .menu-btn { display: flex; }
  .menu {
    position: fixed;
    top: 0; right: 0;
    width: 260px;
    height: 100%;
    background: #1e293b;
    padding: 80px 24px;
    box-sizing: border-box;
    transform: translateX(100%);
    transition: transform 0.3s ease;
    z-index: 90;
  }
  .menu ul { display: block; }
  .menu li { margin-bottom: 8px; }
  .menu a {
    display: block;
    padding: 12px 8px;
    color: #fff;
    border-radius: 6px;
  }
  .menu.is-open { transform: translateX(0); }
}

これで、PCでは横並びナビ、スマホではハンバーガーメニューという1つのヘッダーで両対応する完成形ができあがります。あとは前章のJavaScript(classList.togglecloseMenu)をそのまま組み合わせれば、開閉・Escキー・外側クリックまで動くようになります。パーツがそろったら、実際に自分のサイトへ貼り付けて、幅を変えながら動作を確かめてみましょう。

よく使う微調整のパターン

完成例を土台に、現場でよく行う微調整を3つ紹介します。どれも数行の追加で印象を変えられます。

  • ロゴを中央に置きたい:ヘッダーを3カラムにするか、position: absolute; left: 50%; transform: translateX(-50%); でロゴを中央固定にします。
  • メニューの背景をぼかしたいbackdrop-filter: blur(8px); と半透明背景を組み合わせると、すりガラス風の今どきな見た目になります。
  • 開いたときにリンクを順番に出したい:各 litransition-delay を少しずつずらして与えると、リンクが1つずつ現れる演出になります。

ハンバーガーメニューはSEOに悪影響がある?正しい作り方

「メニューを隠すと、検索エンジンにリンクが読まれず順位が下がるのでは?」という不安をよく聞きます。結論から言うと、正しく作られたハンバーガーメニューはSEOに悪影響を与えません。ここではその理由と、気をつけるべきポイントを整理します。

隠れていてもHTMLにあれば読まれる

検索エンジンのクローラー(ページを読み取るプログラム)は、CSSで見た目が隠れているだけのリンクも、HTMLに存在していればきちんと読み取ります。この記事で作ったハンバーガーメニューは、<nav> の中に <a> リンクが最初からHTMLに書かれている状態です。transform で画面外に置いているだけなので、クローラーはリンクを問題なくたどれます。

逆に注意が必要なのは、JavaScriptで後からリンクを生成する作りです。HTMLに最初はリンクが無く、クリックして初めてリンクが作られる場合、クローラーが読み取れないことがあります。この記事のように、リンクは最初からHTMLに書いておくのが安全です。

見出しタグとナビの構造を整える

SEOで大切なのは、ページの構造が意味的に正しいことです。ナビゲーションは <nav>、リストは <ul><li>、リンクは <a> と、役割に合ったタグを使いましょう。<div> だけで組むより、検索エンジンが構造を理解しやすくなります。この記事のコードはすべてこの原則に沿っています。

表示速度への配慮も忘れずに

検索順位には表示速度も影響します。純CSS版はJavaScriptを読み込まないぶん、わずかに速いという利点があります。JavaScript版でも、コードは軽量なので大きな負担にはなりませんが、不要なライブラリを読み込まず、この記事のような素のJavaScriptで済ませることが、速度の面でもプラスに働きます。装飾用の大きな画像アイコンより、CSSで線を描くほうが軽いのも同じ理由です。


実務でつまずくポイントとよくある失敗の直し方

最後に、実際に作っていると必ずと言っていいほど遭遇する失敗パターンと、その原因・直し方をまとめます。ここを知っておくと、うまく動かないときの原因切り分けが一気に速くなります。

失敗1:チェックボックスが画面に見えてしまう

純CSS版で、四角いチェックボックスがそのまま表示されてしまうケースです。原因はdisplay: none;を書き忘れていること。.menu-toggle { display: none; } を確認してください。アクセシビリティを重視して要素を残したい場合は、次のように画面外へ逃がす方法もあります。

/* 見た目は消すが、キーボードでは選べる状態を保つ */
.menu-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
}

失敗2:メニューが他の要素の下に隠れる

メニューを開いたのに、ヘッダーや画像の後ろに潜り込んで見えないケースです。原因はほぼz-indexの指定不足です。z-index は要素の重なり順を決める値で、大きいほど手前に来ます。メニューやボタンには z-index: 90 以上など十分大きい値を与え、暗幕はそれより少し小さくして、正しい重なり順を作りましょう。

なお z-indexpositionstatic 以外(fixedabsoluterelative など)のときだけ効きます。効かないときは position の指定を見直してください。

失敗3:メニューを開いても背景がスクロールしてしまう

メニューを開いたまま背後のページがスクロールできてしまい、操作しづらい問題です。これは「スクロール貫通」と呼ばれます。前述のとおり、開いている間だけbodyoverflow: hidden;を当てることで解決します。純CSS版なら次のように書けます。

/* 純CSS版:開いている間 html/body のスクロールを止める */
.menu-toggle:checked ~ .menu {
  overflow-y: auto; /* メニュー自体は中身をスクロール可 */
}

純CSSだけでは body のスクロールを厳密に止めるのが難しい場面もあります。スクロール制御まで確実にやりたいなら、素直にJavaScript版で body のクラスを切り替えるのが確実です。

失敗4:×にならず線がずれるだけ

3本線が×にならず、線が変な位置にずれてしまうケースです。原因の多くは回転前にtranslateY(0)で中央へ戻していないことです。上下の線を回転させる前に、いったん中央の同じ高さに集める必要があります。transform: translateY(0) rotate(45deg); のように、移動を先に書いて中央へ戻してから回転させてください。

失敗5:リンクをタップしてもメニューが開いたまま

メニュー内のリンクをタップしてページ内を移動したのに、メニューが開いたままになるケースです。JS版ならリンクをタップしたときにもメニューを閉じる処理を足せば解決します。

// メニュー内のすべてのリンクに「閉じる」を仕込む
menu.querySelectorAll("a").forEach((link) => {
  link.addEventListener("click", closeMenu);
});

純CSS版の場合は、リンク自体を label for="menu-toggle" にはできないので、この挙動は苦手です。リンク選択で閉じたいならJS版が向いています。用途に応じて選びましょう。

編集部の実装チェックリスト

WithCode編集部でハンバーガーメニューを実装するときに、公開前に必ず確認している独自のチェックリストを共有します。作り終えたら1つずつ確認してみてください。

  1. スマホ実機(できればiOSとAndroid両方)で開閉を確認したか
  2. 開いたときに背景がスクロール貫通していないか
  3. 3本線が×へきれいに変形しているか(線がずれていないか)
  4. Tabキーだけでボタンに到達し、Enterで開けるか
  5. Escキー・外側タップ・リンク選択のいずれでも閉じられるか
  6. aria-expandedが開閉に合わせてtruefalseに変わるか
  7. z-indexの重なりでメニューが隠れていないか
  8. 画面幅を変えたとき、PCナビとハンバーガーが正しく切り替わるか

この8項目を通せば、見た目だけでなく操作性・アクセシビリティまで含めて「実務で通用する」ハンバーガーメニューになります。


よくある質問

ハンバーガーメニューを作るときに、初心者からよく寄せられる質問と回答をまとめました。

Q. ハンバーガーメニューはJavaScriptとCSS、どちらで作るべきですか?

A. 学習や小規模なサイトなら純CSS版、本格的なサイトならJavaScript版がおすすめです。純CSS版は手軽で軽く、JavaScriptを読み込まないぶん表示も速いという利点があります。一方で、Escキーで閉じる・外側クリックで閉じる・リンク選択で閉じる・状態を読み上げソフトに正しく伝えるといった細かな制御はJavaScript版のほうが圧倒的にやりやすいです。仕事として納品するサイトでは、アクセシビリティを整えやすいJavaScript版を選ぶことが多いです。

Q. メニューを開いても閉じてくれません。原因は何ですか?

A. 純CSS版なら、多くの場合ラベルとチェックボックスのforidが一致していないか、要素の並び順が「チェックボックス→ラベル→メニュー」になっていないことが原因です。JS版なら、外側クリックで閉じる処理でボタン自身を判定から除外できておらず、開いた瞬間に即閉じている可能性があります。まずは forid、要素の順番、そしてクリック判定を見直してください。

Q. 3本線が×印になりません。どこを直せばいいですか?

A. 上下の線を回転させる前に、translateY(0)でいったん中央の高さへ戻せているかを確認してください。閉じた状態では上下の線が離れているので、そのまま回転させても×になりません。transform: translateY(0) rotate(45deg); のように、移動で中央に集めてから回転させるのが正解です。真ん中の線を background: transparent; で消し忘れていないかも合わせて確認しましょう。

Q. メニューを開くと後ろのページがスクロールしてしまいます。止められますか?

A. 止められます。メニューを開いている間だけbodyoverflow: hidden;を当てるのが定番の方法です。JavaScript版なら、開くときに document.body.classList.add("menu-open")、閉じるときに remove し、CSSで body.menu-open { overflow: hidden; } と書けば実現できます。閉じるときにクラスを外し忘れると、ページ全体がスクロールできなくなるので注意してください。

Q. アクセシビリティは最低限どこまでやればいいですか?

A. まずは①ボタンを<button>要素で作る、②aria-expandedを開閉に合わせて更新する、③Escキーで閉じられるようにするの3点を押さえれば、最低限のラインはクリアできます。余裕があれば、閉じたときにフォーカスをボタンへ戻す、prefers-reduced-motionで動きを控える、といった配慮も加えるとより丁寧です。完璧を目指しすぎて手が止まるより、まず3点から始めるのがおすすめです。

Q. ハンバーガーメニューはPCでも表示していいですか?

A. 表示しても問題はありませんが、PCでは横並びのナビゲーションを見せるのが一般的です。PCは画面が広く、メニューを隠す必要がないため、あえて隠すとユーザーが目的のページを探しづらくなることがあります。メディアクエリで、PCは横並びナビ、スマホはハンバーガー、と切り替えるのがおすすめです。ただしメニュー項目が非常に多いサイトでは、PCでもハンバーガーにまとめる設計もあり得ます。

Q. アイコンに画像やアイコンフォントを使ってもいいですか?

A. 使っても構いませんが、CSSで3本線を作るほうが軽くて調整しやすいです。画像だと×への変形アニメーションが作りにくく、アイコンフォントは読み込みの手間が増えます。CSSの擬似要素で作れば、色・太さ・間隔・アニメーションまで自由自在です。この記事で紹介した span+擬似要素の方法をまず試してみてください。

Q. スマホでタップしても反応が鈍いことがあります。なぜですか?

A. タップできる範囲が小さすぎることが原因のことが多いです。ボタンの当たり判定は最低でも44px四方を確保するのが目安です。3本線自体は細くても、labelbuttonwidthheightpadding を広めに取り、指で押しやすい大きさにしましょう。見た目の線は細いままでも、押せる範囲だけ広げることができます。


まとめ

ハンバーガーメニューは、一見むずかしそうに見えて、仕組みを分解すれば「開いている状態と閉じている状態を切り替えているだけ」というシンプルな部品です。この記事では、JavaScriptを使わない純CSSのチェックボックスハックから始めて、3本線が×に変わるアニメーション、スライドインやオーバーレイなどのバリエーション、JavaScript版、アクセシビリティ対応、レスポンシブ切り替え、そしてよくある失敗の直し方まで、コピペで動くコードとともに一通り解説しました。

まずは純CSS版を貼り付けて動かし、仕組みを理解したら、実務ではJavaScript版でEscキーや外側クリック、aria-expandedの更新まで整えていく——この順番で身につけるのがおすすめです。

・状態の切り替え:ハンバーガーメニューは「開・閉」2状態の切り替え。CSSなら:checked、JSなら.is-openクラスで実現する
・3本線と×span1つ+::before::afterで3本線を作り、中央へ集めてから回転させて×に変形させる
・仕上げの決め手button要素・aria-expanded・キーボード操作・レスポンシブ切り替えまで整えて初めて実務レベルになる

ここまで読んで「コードの意味は分かったけれど、自分のサイトに組み込むと途端に動かなくなる」「HTML・CSS・JavaScriptのつながりを、体系立てて基礎から学び直したい」と感じた方もいるはずです。WithCodeなら、こうしたUI部品を自分の手で作りながら、現役エンジニアのサポートを受けてつまずきを最短で解消できます。独学で止まってしまいがちなポイントを、伴走型で乗り越えていけるのが特長です。


WithCodeを体験できる初級コース公開中!

初級コース(¥49,800)が完全無料に!

  • 期間:1週間
  • 学習内容:
    ロードマップ/基礎知識/環境構築/HTML/CSS/LP・ポートフォリオ作成
    正しい学習方法で「確かな成長」を実感できるカリキュラム。

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?

未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!

この記事を書いた人

WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次