ハンバーガーボタン 何で作ってる?僕なりの作り方を解説してみる。

ハンバーガーボタン

もう年末ですね。

去年の今頃は風邪で鼻水だらだらの記憶がありますが、皆さんは如何お過ごしですか?

どうも、しばおです。

さて、いきなりですが、皆さん ハンバーガーボタン ってどうやって作ってます?

ハンバーガーボタン の作り方をググったりすると、空っぽの span タグ 3個で作ってたり、div タグ や チェックボックスで作ってたりするのをよく見かけます。

でもね。僕的には、button 要素がベストだと思っているんです。

そこで今回は、なぜハンバーガーボタンを button 要素で作った方がいいのかや、僕なりの作り方をできる限り詳しく解説をしてみたいと思います。

ちなみにこの記事は、ある程度、HTMLやCSS、JavaScriptを使った基本的なコーディングを理解している人向けになりますので、CSSって何?って方は先に、基本的なコーディングを学習しておいて下さい。

ハンバーガーボタン ってなに?

そもそもハンバーガーボタンって何?って話なのですが、最近のスマホサイトでは当たり前になってきた、三本線のボタンのことです。

つまり、下記の様なボタンです。

See the Pen Hamburger use jQuery by Shiba Hiro (@hilosiva) on CodePen.36915

ちなみにこれが今回解説する完成品のコードです。

「解説なんていらないから、ハンバーガーボタンを作るコードだけ欲しい!」って方はこのコードを見てもらえば完結ですね。

お疲れさまでした。

はい。ということで、これがハンバーガーボタンです。

真ん中の線がお肉、上下の線がバンズに見えることから、そう名付けられたとか・・・?

なんせ、このボタンをクリックすると、ドロワーメニューと言って、サイトのメニューがスパーンって横から表示されるのが一般的ですよね。

昔は、糞UIと騒がれてましたが、今となってはメニューを開くボタンとして一般的に認知されています。

じゃあ、まず、このハンバーガーボタンを実装するための HTML を考えていきましょう。

僕流の ハンバーガーボタン のHTML

結論から言うと、僕は以下のような HTML でマークアップしています。

sample.html
<button type="button" id="js-buttonHamburger" class="c-button p-hamburger" aria-controls="global-nav" aria-expanded="false">
  <span class="p-hamburger__line">
    <span class="u-visuallyHidden">
      メニューを開閉する
    </span>
  </span>
</button>

まぁ、id名やclass名などはお好きに変えて下さい。

今回僕は、とりあえず深く考えずに、FLOCSSというCSS設計案的に書いたので、「c-」や「p-」って何?って方はFLOCSSというCSS設計案を確認して頂ければと思います。

なぜ button 要素なのか?
span 要素だけや、チェックボックスじゃだめなの?

よく、ハンバーガーボタンに関してググると、spanタグ3つ並べて作ったりdivタグだけで作ってたりするのを見かけますよね。

あ、あと、チェックボックスを使って CSS だけでドロワーメニューを実装するパターンもありますね。

が、結論僕は、 button 要素がいいと思ってるんです。

理由はいくつかあるのですが、まずそもそも、span 要素や div 要素って、フォーカスが当たらないんです。

どういう事かって言うと、Webサイトをキーボードだけで操作する時は、Tab キーを使って、リンクや、フォームの入力項目にフォーカスを当てて、Enter キーでリンク先に移動したり、フォームに入力したりするんです。

このフォーカス が当たる要素というのは、基本的には、a 要素 と フォームのコントロール部品の 要素だけです。

つまり、span 要素や div 要素で作ったハンバーガーボタンには、フォーカスが当たらないので、キーボードだけで操作している人は、ハンバーガーボタン を押すことができないのです。

以下は、button 要素を使わずに、div 要素と、span 要素だけで、ハンバーガーボタン を作ってみました。

See the Pen Hamburger use span by Shiba Hiro (@hilosiva) on CodePen.36915

この「Result」パネルのプレビュー画面を1度クリックしてから、tab キーを押してみて下さい。

ほら、フォーカスが当たらないでしょ?

つまり、キーボードだけでは ハンバーガーボタン をクリック出来ないのです。

じゃあさ、
チェックボックスを使ったらいいじゃん!

って思ったかもしれませんが、その場合はきっと、ハンバーガーボタン にする際、input 要素を display: none にして、label 要素 で実装しませんか?

ってなるとですよ。

label 要素にはそもそもフォーカスが当たらないので、 input 要素 を display: none したら、結局、フォーカスが当たる要素がないんですよ。

以下のサンプルは、input要素で、チェックボックスを作って、label 要素でハンバーガーボタンを実装してみました。

See the Pen Hamburger use input by Shiba Hiro (@hilosiva) on CodePen.36915

また、この「Result」パネルのプレビュー画面を一度クリックしてから、tab キーを押してみて下さい。

ほら、これもフォーカスが当たらないでしょ?

label要素にはそもそも、フォーカスが当たらないので、input 要素 を display: none しちゃうと、結局フォーカスが当たる要素がないんです。

じゃあさ、じゃあさ、
label要素に tabindex 属性つけたら
フォーカス当てれるじゃん

なるほど。
確かに、フォーカスは当てれます。

でも、label 要素にフォーカスがあたっても、キーボードのみの操作で、そのラベルと関連する チェックボックスのチェックをオンにすることができないのです。

以下は、さっきのサンプルに、tabindex 属性でlabel 要素にフォーカスが当たるようにしてみました。

See the Pen Hamburger use input tab-index by Shiba Hiro (@hilosiva) on CodePen.36915

どうですか?

フォーカスは当たるけど、Space キーを押そうが、Enter キーを押そうが、選択ができないですよね?

つまり、span 要素 や div 要素、チェックボックスでハンバーガーボタンを作ったとしても、見た目は問題ないが、キーボードのみで操作している人は、メニューを展開することができないのです。

マウス使えばいいじゃん!

確かに、マウスを使えばいいかもしれません。

ですが、身体的理由でマウスの操作ができない方もいるかも知れませんし、もしあなたがデスクトップパソコンを使ってたとして、急にマウスが壊れたらどうです?

ほら、キーボードのみで操作しなければいけないでしょ?

こんな時に、アクセシビリティが低いサイトだと、メニューが展開できず、「他のページに移動できない!」なんてことになるのです。

ということで、ハンバーガーボタンは button 要素が良いと思っています。

aria-controls 属性とか、aria-expanded 属性って何?

よく見かけるハンバーガーボタンの解説コードでは、こんな属性付いてなかったりしますが、これは一体なんなのかって話です。

これも、アクセシビリティを向上させるための属性です。

aria-controls 属性
この属性が指定されている要素が、コントロールする対象となる要素を指定する属性。対象の要素にid属性で固有名を決めて、そのID名で指定する
aria-expanded 属性
この要素が操作する対象の要素が、現在展開されているか、展開されていないかという状態を表す属性。属性値に true を指定すると展開中を表し、false を指定すると現在展開していないことを表す

という属性たちです。

つまり、このaria-controls 属性を使えば、このボタンがどの要素を操作するボタンなのかを視覚的に見てるユーザー以外にも伝えることが出来ます。

そして、aria-expanded 属性は、ドロワーメニューが表示されるタイミングで true にしたり、非表示になるタイミングで false にすることで、視覚的に見てるユーザー以外にも、今メニューがどういう状態になっているかを伝える事ができます。

これらの属性のおかげで、Webサイトを視覚的に見ているユーザー以外でも、ちゃんとメニューの表示状態を把握することができるのです。

じゃあさ、「メニューを開閉する」ってテキストいるの?

多くのハンバーガーボタンのサンプルコードをみると、確かにこんなテキスト書いてないですよね。

でも、この様なテキストがないと、音声ソフトなどのスクリーンリーダーを使ってWebサイトを見ているユーザーに、これがメニューを表示するためのボタンであることが伝えられません。

これは、別に、aria-label 属性という属性を使って、指定する方法でもいいと思います。

いずれにしても、この様なテキストや、aria-label 属性を使うことで、音声ソフトなどのスクリーンリーダーでWebサイトの内容を理解しているユーザーにも、このボタンを押せば何が起こるのかが、明確にわかるようになります。

でも、このテキストって、視覚的にはいらないですよね?

という事で、CSSでそのテキストを非表示にしちゃいます。

ちなみに、こんな感じでテキストを非表示にする場合は、aria-label 属性を使うより、直接HTMLにテキストを配置した方が良いんじゃないかなと思います。

aria-label 属性はあくまで支援ソフト向けの属性であって、視覚的なユーザー向けのものではないため、もし何らかの影響(ネットワークの不調など)で、CSSがうまく読み込まれなかったとしたら、何のテキストも書いてない空っぽのボタンとなり、視覚的に見ているユーザーにはどんな役割のボタンなのかが伝わらないからです。

ということで、この記事ではHTMLに直接「メニューを開閉する」というテキストを配置して、CSSでそのテキストを非表示にします。

あ、ちょっとまって!display: none はやめてね。

なぜなら、display: noneを使うと、音声読み上げソフトも読み上げないです!

え!!

ってことで、それ以外の方法で非表示にします。

具体的にどんなコードで非表示してるかは、後で言うとして、いずれにしても、僕はこんな感じで、ハンバーガーボタンのHTMLを書いています。

See the Pen Make Hamburger Button Step 0 by Shiba Hiro (@hilosiva) on CodePen.36915

ちなみに、button 要素に、type 属性 で button を指定することは忘れないでくださいね。

button 要素の デフォルトの type 属性は、submit なので・・・。

僕流の ハンバーガーボタンの CSS

じゃあCSSはどうしてるのかっていうところも、簡単に説明しておきたいと思います。

まずは、さっきのテキストを非表示する方法から紹介しましょうか。

スクリーンリーダー用のテキストを非表示にするスタイル

結論から言うと、以下のスタイルを使って非表示にしています。

sample.css
.u-visuallyHidden {
  position: absolute;
  white-space: nowrap;
  width: 1px;
  height: 1px;
  overflow: hidden;
  border: 0;
  padding: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%); 
}

って感じですかね。

position: absolute で 飛ばして、ボックスを 1px にしたり、clip で切り取ったりして、ボックスを越えたものを overflow: hidden で隠す!

という、わりと無理矢理テキストを隠すという呪文です。

でも、この方法だと、display: none などとは違い、ちゃんと音声ソフトでも読み上げられます。

ということで、この u-visuallyHidden というclassを「メニューを開閉する」というテキストを包んでる span 要素に指定してます。

See the Pen Make Hamburger Button Step 1 by Shiba Hiro (@hilosiva) on CodePen.36915

これで、無事テキストが非表示になりました。

button 要素 のスタイルはどんな感じ?

そしたら、実際にハンバーガーボタンに関するスタイルを見ていきましょう。

まずは、button 要素のスタイルですね。

button 要素には、「c-button」というclassと、「p-hamburger」というclassの2つのクラスを当ててます。

まぁ、ここはFLOCSS的に書いたのでこんな感じですが、別に変えもらって問題ありません。

いずれにしても、button 要素には以下のようなスタイルを当てています。

sample.css
.c-button {
  position: relative;
  display: inline-block;
  text-decoration: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border: none;
  background-color: transparent;
  cursor: pointer;
}

.p-hamburger {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 48px;
  height: 48px;
  margin: auto;
  border-radius: 50%;
  border: 1px solid #f9d8ae;
  box-shadow: 0 0 2rem transparent;
  outline: none;
  -webkit-transition: all .3s ease-in-out;
          transition: all .3s ease-in-out;
}

.p-hamburger:hover,
.p-hamburger:focus {
  box-shadow: 0 0 .5rem rgba(255, 255, 255, .5 );
}

c-buttonには、どのボタンにも使いそうな、汎用的なボタンのスタイルを当てて、p-hamburger には、このハンバーガーボタンの枠のスタイルを当てています。

まぁ、ハンバーガーでいうと、ハンバーガーを包んでる紙の部分のスタイルって事ですね。

特別なことはとくに何もしてないのですが、ボタンの位置やら、サイズやら、角丸やら、色はお好きに変更して下さい。

button要素の注意点

まぁ、ちょっと注意しておいてほしいポイントがひとつあるので、そこの説明だけしておきますね。

それが、フォーカス時のスタイルですかね。

今回僕は、p-hamburgeroutline: none を指定してます。

sample.css
.p-hamburger {
  /* ... 省略 ... */
  outline: none;
  /* ... 省略 ... */
}

これめちゃめちゃよく見かけるんですが、これ個人的には結構危険だと思ってます。

確かに付けたくなる意味はわかります。

だって、デフォルトのフォーカス時に表示される、あの青いアウトラインって、デザインとマッチしないなどの理由で、使いたくなかったりするんですよね。

でも、あのアウトラインが無くなると、キーボードだけで操作してるユーザーは大変です。

なんでかって言うと、ハンバーガーボタンにフォーカスが当たっているかどうかが視覚的にわからなくなっちゃうんですよね。

以下は、単純に outline: none を使った、ハンバーガーボタンです。

See the Pen Hamburger use outline-none by Shiba Hiro (@hilosiva) on CodePen.36915

Tab キーを使って、ハンバーガーボタンにフォーカスを当てて、Enter キー で選択してくみて下さい。

どうですか?ちゃんと選択できるけど、フォーカスが当たっているかわからないでしょ?

これが、個人的に危険だなぁ。って思ってることです。

ま、めちゃめちゃ見かけますけどね。

だからら、基本的に、outline: none使わないようにしましょう!

でも、あの青いアウトラインが嫌!ってのもわかります。

そこで、僕がよく使うのテクニックが、フォーカス時には、box-shadow を使ってフォーカスが当たっていることがわかるようなスタイルを当てています。

あと、これに、ちょっとアニメーションを加えることで、オシャンティーにしています。

sample.css
.p-hamburger {
  /* ... 省略 ... */
  box-shadow: 0 0 2rem transparent;
  outline: none;
  -webkit-transition: all .3s ease-in-out;
          transition: all .3s ease-in-out;
}
.p-hamburger:hover,
.p-hamburger:focus {
  box-shadow: 0 0 .5rem rgba(255, 255, 255, .5 );
}

これで、ハンバーガーを包む紙、つまりボタンの枠の部分ができました。

See the Pen Hamburger use outline-none by Shiba Hiro (@hilosiva) on CodePen.36915

p-hamburger__line はどんなスタイル?

次は、その中の「 p-hamburger__line 」というclassです。

この、「p-hamburger__line 」というclassで、ハンバーガーのお肉とバンズを作っています。

まぁ、なのでこの子がハンバーガーですね。

お肉の作り方

お肉を作るには、まず、p-hamburger__line に、お肉の幅と高さを設定し、背景色をつけることで、お肉が完成します。

そして、そのお肉を、先程作ったp-hamburger のど真ん中に持っていきます。

具体的には以下の様なCSSになります。

sample.css
.p-hamburger__line {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto; /* 親要素に対してど真ん中に配置 */
  width: 18px; /* お肉の幅 */
  height: 2px; /* お肉の高さ */
  background-color: #f9d8ae;
  -webkit-transition: inherit;
          transition: inherit;
}

まぁ、これもそんなに変わったことはしてませんが、width と、height を使って お肉のボックスを作ってます。

でそれを、position: absoluteを使って、親要素となる button 要素のド真ん中に持ってきてます。

leftrighttopbottomが全て、0だから、親要素の領域を把握できてるし、幅と高さもちゃんと指定しているので、margin: autoが効きます。

つまり、ど真ん中に来ます。

See the Pen Make Hamburger Button Step 3 by Shiba Hiro (@hilosiva) on CodePen.36915

ここまで書けば、この時点でこんな感じになると思います。

バンズの作り方

バンズは、::before::after で作っています。

まず、::before::after に、position: absolute を指定して、親要素である、お肉と、同じ位置に同じ大きさになるようにスタイルを指定します。

で、::beforetop をマイナスで指定して上に配置し、::after の方には、プラスの値を指定することでお肉の下にバンズの位置を調整しています。

sample.css
.p-hamburger__line::before,
.p-hamburger__line::after {
  content: '';
  position: absolute;
  display: block;
  width: 100%;
  height: 100%;
  background-color: inherit;
  -webkit-transition: inherit;
          transition: inherit;
}

.p-hamburger__line::before {
  top: -5px;
}

.p-hamburger__line::after {
  top: 5px;
}

これによって、span 要素ひとつで、ハンバーガーを作っています。

See the Pen Make Hamburger Button Step 4 by Shiba Hiro (@hilosiva) on CodePen.36915

JavaScript はどうしてるの?

もちろん、JavaScript に関してもいろんな方法があると思いますが、個人的なポイントは、2つです。

  • メニューが表示中の時に、html 要素 にclass を付加する
  • aria-expanded属性の true / falseを切り替える

まぁ、ドロワーメニューを作るだけなら、これが出来れば問題ないのかなぁと思っています。

なので、ネイティブなJavaScriptだと以下のようなコードでそれが実装できます。

sample.js
const hamburgerButton = document.querySelector('#js-buttonHamburger');

hamburgerButton.addEventListener('click', (e) => {
  const isExpanded = e.currentTarget.getAttribute("aria-expanded") !== "false";
  e.currentTarget.setAttribute("aria-expanded", !isExpanded);
  
  document.documentElement.classList.toggle("is-drawerActive");
})

これで、上に挙げたポイントがクリアできるわけです。

メニューが表示中の時に、html 要素 にclass を付加する

さて、これはなぜやってるかって話も入れておきますね。

まぁ、ぼくはドロワーメニューもそうですが、この様な何かをクリックして、要素が表示されるようなものを作る時は、だいたい、html要素に、classをつけるようにしています。

なぜかっていうと、単純に、html要素って全ての要素を包んでる、母なる要素なので、ここにクリックされた時だけ classを付けちゃえば、子孫セレクタを組み合わせたりすれば、html 要素内のどの要素にでも スタイルを指定できるからです。

なので、ドロワーメニューの場合は、ハンバーガーボタンをクリックした時に、html要素 に 「is-drawerActive」的なclass を付けたら、 以下のようなスタイルで、ドロワーメニューを操作できます。

sample.css
.p-drawer {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
  transform: translateX(100%);
  -webkit-transition: transform .3s ease-in-out;
          transition: transform .3s ease-in-out;
}

.is-drawerActive .p-drawer {
  transform: translateX(0);
}}

ちなみに、このclassを活用したら、ハンバーガーボタンも、メニュー表示用のスタイルに切り替えることができますね。

aria-expanded属性の true / falseを切り替える

そして、2つめのポイントが、aria-expanded 属性を切り替えるって処理ですね。

まぁ、HTMLのマークアップの時にも書きましたが、この属性は、操作対象となる要素が、現在表示されているのか非表示なのかをスクリーンリーダーなどに知らせることができる属性です。

一般的に ハンバーガーボタンをクリックしたら、ドロワーメニューが、横からとかピューンと登場するかと思います。

なので、基本的にはハンバーガーボタンが操作する対象は、そのドロワーメニューというわけです。

で、そのドロワーメニューって、サイトを表示した時点では、非表示になっていることが多いと思います。

つまり、そのような非表示時の時は、aria-expanded 属性が false である必要があります。

で、ハンバーガーボタンをクリックして、ドロワーメニューが表示されたら、その時は、この、aria-expanded 属性を true にする必要があります。

これを JavaScript を使って切り替えているのです。

これで、スクリーンリーダーなどを使っているユーザーに、メニューが表示されたことを知らせるサポートができます。

See the Pen Make Hamburger Button Step 5 by Shiba Hiro (@hilosiva) on CodePen.36915

ドロワーメニュー展開中のハンバーガーボタンのスタイル

じゃあ、最後にハンバーガーボタンをクリックして、ドロワーメニューが表示されている時の、ハンバーガーボタンのスタイルについて解説したいと思います。

結論からいうと、以下の様なスタイルを僕はよく書きます。

sample.css
.p-hamburger[aria-expanded="true"] .p-hamburger__line {
  background-color: transparent;
}

.p-hamburger[aria-expanded="true"] .p-hamburger__line::before,
.p-hamburger[aria-expanded="true"] .p-hamburger__line::after {
  top: 0;
  background-color: #f9d8ae;
}

.p-hamburger[aria-expanded="true"] .p-hamburger__line::before {
  -webkit-transform: rotate(45deg);
      -ms-transform: rotate(45deg);
          transform: rotate(45deg);
}

.p-hamburger[aria-expanded="true"] .p-hamburger__line::after {
  -webkit-transform: rotate(-45deg);
      -ms-transform: rotate(-45deg);
          transform: rotate(-45deg);
}

こんな感じです。

さっき JavaScriptで、body要素に「 is-drawerActive 」というclassをつけるって言っていいました。

なのでそれを活用しても良かったんですが、今回は、属性セレクタも活用しちゃいました。

つまり、aria-expanded 属性が true の時という属性セレクタでスタイルを当てています。

まぁ、やっていることは、シンプルで、真ん中のお肉の背景色を透明にして、バンズは真ん中に戻して、45度回転させて、「☓」にしています。

アニメーションの感じとかは、自由に変えちゃってくださいね。

これで、ハンバーガーボタンの完成です。

See the Pen Hamburger use jQuery by Shiba Hiro (@hilosiva) on CodePen.36915

まとめ

如何だったでしょうか?

Shibajuku の授業でも、僕なりのハンバーガーボタンの作り方をこんな感じでガッツリ作り込む授業してるんですが、今回はそれを記事にして解説してみました。

まぁ、僕のこだわりポイントとしては、アクセシビリティを意識したハンバーガーボタンを作るってことですかね。

もっとアクセシビリティにこだわることもできる気もしますが・・・w

なにわともあれ、このハンバーガーボタンの作り方がみなさんの参考になれば幸いです。

ちなみに、Shibajukuでは、こういうアクセシビリティを意識したコーディングを結構授業でやってるので、興味がある方は一度覗いてみてください。

では。

おまけ

CDNで読み込むタイプのやつですが、vue.js 版も作ってみました。

See the Pen Hamburger use Vue.js by Shiba Hiro (@hilosiva) on CodePen.36915