ハンバーガーボタンの作り方と実装ポイント【コピペOK】

当サイトはアフィリエイト広告を使用しています。

スマホサイトやレスポンシブデザインで定番になっているのが「ハンバーガーボタン」です。
三本線のアイコンを押すとメニューが展開する仕組みで、ナビゲーションをコンパクトにまとめるときによく使われます。

一見シンプルですが、実際に案件で使うには「作り方」や「実装のポイント」を押さえておくことが大切です。
この記事では、普段自分が実務で使っているコードをもとに、ハンバーガーボタンの基本と気をつける点をまとめます。

ハンバーガーボタンとは

ハンバーガーボタンとは、三本線「≡」のアイコンで表現されるナビゲーションボタンのことです。
クリックやタップで隠れていたメニュー(ドロワーメニュー)を表示できる仕組みになっています。

名前の由来は、その形がパンで具材を挟んだハンバーガーに似ていることから。
主にスマートフォンやタブレットなど画面の小さいデバイスで、限られたスペースを有効活用する目的で広く使われています。

メリット

  • ナビゲーションを隠せるので画面を広く使える
  • デザインをシンプルに保てる

デメリット

  • アイコンの存在に気づかれないことがある
  • メニューを開かないと中身が見えない

現在ではモバイルだけでなく、PCサイトでも使われるケースが増えています。

基本の作り方(HTML / CSS)

まずは完成デモを CodePen で確認してください。

See the Pen Drawer Templete by Suzuki Kazuma (@build_suzuki) on CodePen.

HTMLはシンプルで、button 要素の中に三本の span を配置します。

<button type="button" class="drawer-button" aria-label="メニューを開閉">
  <span class="drawer-button__bar"></span>
  <span class="drawer-button__bar"></span>
  <span class="drawer-button__bar"></span>
</button>

CSSは、三本線を描くための最低限のスタイルだけ抜粋します。

:root {
  --drawer-bar-gap: 8px;
  --duration: 300ms;
  --easing: ease;
}

.drawer-button {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  justify-content: center;
  padding: 8px;
  background: transparent;
  border: none;
  cursor: pointer;
}

.drawer-button__bar {
  display: block;
  width: 32px;
  height: 2px;
  background-color: #000;
  border-radius: 9999px;
  transition: transform var(--duration) var(--easing),
              opacity var(--duration) var(--easing);
}

.drawer-button__bar:nth-child(1) {
  transform: translateY(calc(-1 * var(--drawer-bar-gap)));
}
.drawer-button__bar:nth-child(2) {
  transform: translateY(0);
}
.drawer-button__bar:nth-child(3) {
  transform: translateY(var(--drawer-bar-gap));
}

これだけで 「三本線のハンバーガーボタン」 を描くことができます。
開閉時の回転やクロスマークへの変化は .is-open クラスを使って切り替えます。

このコードを使う理由

このハンバーガーボタンのコードは、実案件でそのまま流用できるように設計しています。
とりあえず動けばいいコードではなく、運用や拡張を考えたときに扱いやすい形です。

汎用性が高い

サイズや色、配置はCSS変数やクラスで簡単に調整できます。
新しい案件でも基本部分をそのまま使い回し、デザインに応じて見た目だけ変更可能です。

カスタマイズがしやすい

  • ボタンの色は開閉時に変えることができる
  • ラベルテキスト(例:「メニュー」)は任意。削除してもエラーは出ない
  • 線の太さや間隔はCSSの数値を変えるだけで調整できる

安心して使える設計

  • button 要素を使っているので、クリックやキーボード操作にも対応
  • 開閉状態は .is-open クラスで一元管理できる
  • 必要な要素が欠けていてもエラーになりにくいよう配慮

案件ごとに一からコードを書くよりも、ベースを決めておくことで効率的に開発できます。

気をつけたいポイント

どのタグを使うべきか

ハンバーガーボタンは必ず button 要素で実装してください。理由は次のとおり。

既定のインタラクションが揃っている

button は標準で「Tabでフォーカス」「Enter/Spaceで押下」「無効化(disabled)」等が何も書かずに動きます。

アクセシビリティツリーで“ボタン”として伝わる

支援技術(音声読み上げ等)に“ボタン”として正しく認識されます。余計なrole指定が不要。

フォームとの衝突を防げる

type="button" を必ず指定(未指定は submit になり得ます)。予期せぬフォーム送信を防止。

推奨の最小マークアップ

<button
  type="button"
  class="drawer-button"
  aria-controls="drawer-nav"
  aria-expanded="false"
  aria-label="メニューを開閉">
  <span class="drawer-button__icon" aria-hidden="true">
    <span class="drawer-button__bar"></span>
    <span class="drawer-button__bar"></span>
    <span class="drawer-button__bar"></span>
  </span>
</button>
  • ラベルをテキストで出す場合は aria-label は省略可。アイコンのみなら aria-label 必須

なぜ div / span はダメなのか

divspan は非インタラクティブ要素です。クリックは付けられても、キーボード操作や読み上げの期待値が満たせません。
補強しようとすると実装量と落とし穴が増えます。

典型的NG例(クリックだけ)

<div class="drawer-button">☰</div>
<script>
  document.querySelector('.drawer-button').addEventListener('click', toggleMenu);
</script>
  • Tabでフォーカスできない
  • Enter/Spaceで動かない
  • 読み上げで何の要素か分からない

改善を試みたが“未完成”な例

<div class="drawer-button" role="button" tabindex="0" aria-label="メニューを開閉"></div>
<script>
  const el = document.querySelector('.drawer-button');
  el.addEventListener('click', toggleMenu);
  el.addEventListener('keydown', (e) => {
    // Enter対応はしてるが…
    if (e.key === 'Enter') toggleMenu();
    // Space対応を忘れがち(Spaceは押下中スクロールも起きやすい)
  });
</script>
  • Spaceキーでの動作が未実装になりがち
  • role="button" でも本物の button と同等にはならない
  • disabled 相当の制御・フォーカス順管理・状態の発表(aria-pressed等トグル表現)まで自前で必要

結局、buttonに戻した方が速くて安全です

どうしても非ボタンで実装するケースでも、roletabindex・Enter/Spaceの両対応・押下時の既定動作抑止など多くの追加対応が要ります。基本は避けましょう。

tabindex の正しい使い方

  • tabindex="0" … フォーカス可能にし、自然な順序に参加(原則:本当に必要な時だけ)。
  • tabindex="-1" … プログラムでのみフォーカスさせたい時に使用(Tab巡回には入れない)。
  • 正の値(例:tabindex="1")はNG … フォーカス順序が壊れます。原則使わない。
  • 基本はネイティブ要素(button・aなど)を正しく使うことで tabindex すら不要にするのがベスト。

フォーカスが見えるようにする

リセットCSSでアウトラインが消えている場合があります。キーボード操作でフォーカスが見えるのは必須。

/* リセット等で消されている場合に復活 */
.drawer-button:focus { outline: none; } /* マウス時は非表示でもOK */
.drawer-button:focus-visible {
  outline: 2px solid #000;    /* 目に入る太さ */
  outline-offset: 3px;
  /* 必要なら box-shadow で代替可 */
}
  • :focus-visible はキーボード操作時にだけアウトラインを出す意図に合致。
  • 背景が暗い/明るいで見え方が変わる場合は、テーマに合わせて色を調整。

視覚的ラベルを非表示にしてアイコンだけにする場合は、スクリーンリーダー向けのテキストを用意すると親切です。

<button type="button" class="drawer-button" aria-controls="drawer-nav" aria-expanded="false">
  <span class="sr-only">メニューを開閉</span>
  <!-- アイコン -->
</button>
/* 視覚的に隠しつつ読み上げ可能にするユーティリティ */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

キーボード操作は実装後に必ず確認

実装後、実機チェックは必須です。自分は以下のような項目を確認しています。

  • Tabでボタンにフォーカスできるか
  • Enter / Space で開閉できるか
  • 開いた後、Tabでメニュー内のリンクに移動できるか
  • Escキーで閉じられるか
  • 画面リーダーで、「開いています/閉じています」が伝わるか(aria-expanded を更新)

メニューの実装方法については別記事で解説します。
少々お待ちください。

タップ領域とコントラスト

タップ領域は40px四方以上が目安
参考:Material Designのガイド(タッチサイズ)

コントラスト比 4.5:1 以上(本文小サイズ相当の推奨基準)
参考:WCAG 2.1 最低コントラスト(日本語訳 / W3C公式)


参考:Color Contrast Checker

まとめ

ハンバーガーボタンは「三本線を描くだけ」なら簡単ですが、
実際に案件で使うには 押しやすさ・わかりやすさ・操作できること を意識する必要があります。

  • button type="button" を使う(divspanはNG)
  • aria-labelaria-expanded で意味と状態を伝える
  • フォーカスが見えるようにする(:focus-visible を活用)
  • タブキーやEscキーでの操作も確認する
  • タップ領域と色のコントラストにも配慮する

これらを押さえておけば、どの案件でも安心して使えるハンバーガーボタンになります。

今回の内容を押さえておけば、ほとんどの案件で「背景が動いてしまう」という問題は解決できます。
もっと作り込んだハンバーガーメニューの完成版テンプレートも用意しているので、良かったらあわせてご覧ください。

使用サーバー

使用テーマ