【CSS】クリックすると動くハンバーガーメニューを自作する方法(ヘッダーメニュー、グローバルナビゲーション)

css3-prograshi(プロぐらし)-kv CSS・SCSS
記事内に広告が含まれていることがあります。

WEBサイトのヘッダーメニュー(グローバルナビゲーション)でよく見かけるものに、クリック前は2本線(または3本線)のハンバーガーメニューの形をしていて、クリックすると✕に変形するものがあります。

難しいかなと思いきや、実はHTMLとCSSのみで実装することができます。

ここではクリックすると✕に変形するオシャレなヘッダーメニューの作成方法を実例で解説しています。


完成形

See the Pen Untitled by pro gurashi (@pro-gurashi) on CodePen.


主な仕様
  1. 右端に3本線のハンバーガーメニューを表示
  2. クリックすると✕に変形する
  3. クリックすると画面全体を覆う形でリンク一覧を表示


コードの解説

上記はHTMLとCSSのみで構成され、コードは以下のようになっています。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>ハンバーガーメニュー</title>
</head>
<body>
  
  <header class="header">
    <div class="navtext-container">
      <div class="navtext">This is Our Website</div>
    </div>
    <input type="checkbox" class="menu-btn" id="menu-btn">
    <label for="menu-btn" class="menu-icon"><span class="navicon"></span></label>
    <ul class="menu">
      <li class="top"><a href="#top">TOP</a></li>
      <li><a href="#company">COMPANY</a></li>
      <li><a href="#solutions">SOLUTIONS</a></li>
      <li><a href="#contact">CONTACT</a></li>
    </ul>
  </header>
  
</body>

</html>



/*********ハンバーガーメニューのアニメーション設定**********/
/* 変数 */
:root {
  --background-navbar: rgba(55, 55, 55, 0.98);
}


/* ヘッダーの背景色と固定、高さ */
.header {
  /* background: var(--background-navbar); */
  position: fixed;
  width: 100%;
  height: 52px;
}

/* メニュークリック時に表示するリンク一覧の背景 */
.menu {
  list-style: none;
  position: absolute;
  width: 100%;
  height: 100vh;
  top: 0;
  margin-top: 52px;
  padding: 0 0 10px 0;
  clear: both;
  background: var(--background-navbar);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
  transform: scale(1, 0);
  transform-origin: top;
}

/* ボタンクリックでメニュー一覧を表示する */
.menu-btn:checked ~ .menu {
  transform: scale(1);
  transform-origin: top;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


/* ハンバーガーメニュー内のテキスト */
.menu a {
  text-decoration: none;
  font-weight: 500;
  letter-spacing: 2px;
  font-size: 16px;
  text-transform: capitalize;
  color: #ddd;
  opacity: 0;
  transition: 0.5s;
}

/* ハンバーガーメニューの中の隙間 */
.menu li {
  border-top: 1px solid rgb(75, 75, 75);
  padding: 15px 0;
  margin: 0 54px;
  opacity: 0;
  transition: 0.5s;
}


/* クリック時のみメニュー内のliとaタグを表示する */
.menu-btn:checked ~ .menu a,
.menu-btn:checked ~ .menu li {
  opacity: 1;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.2s;
}


/* メニューがクリックされてるかどうかを判断するボタン */
.menu-btn {
  display: none;
}


/* ハンバーガーメニューの位置。カーソルを乗せたときの挙動 */
.menu-icon {
  display: inline-block;
  position: relative;
  left: 88%;
  top: 8px;
  cursor: pointer;
  padding: 14px 14px;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}


/* メニューの中央の横線 */
.navicon {
  background: gray;
  display: block;
  height: 3px;
  width: 26px;
  position: relative;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニューの上下の横線 */
.navicon:before,
.navicon:after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  position: absolute;
  background: gray;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニュー上の線の位置 */
.navicon:before {
  top: 9px;
}


/* メニュー下の線の位置 */
.navicon:after {
  bottom: 9px;
}


/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}

/* 下側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:after {
  transform: rotate(45deg);
}


/* メニュークリック時の上下の線の位置 */
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:before {
  top: 0;
}
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:after {
  bottom: 0;
}

/* メニュークリック時の真ん中の線(透明にする) */
.menu-btn:checked ~ .menu-icon .navicon {
  background: rgba(0, 0, 0, 0);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/*********ここまで**********/



/* ハンバーガーメニュー横のボックスの設定 */
.navtext-container {
  width: 100%;
  height: 52px;
  position: absolute;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* ハンバーガーメニュー横のテキスト */
.navtext {
  position: absolute;
  text-transform: uppercase;
  color: #lightgray;
  letter-spacing: 4px;
  font-size: 20px;
}


カスタムプロパティの設定

:root {
  --background-navbar: rgba(55, 55, 55, 0.98);
}


まず一番最初に :rootの疑似クラスの中でカスタムプロパティ「–background-navbar」を定義しています。

これで、var関数を使ってグローバルに–background-navbarを呼び出すことができます。


ハンバーガーメニューのクリック判定

見えないチェックボックス

このメニューの一番のポイントは見えないチェックボックスを作成して、ハンバーガーメニューがクリックされるたびに、チェックのありなしが切り替わることです。

HTMLの中のinputタグとlabelタグが該当します。

<input type="checkbox" class="menu-btn" id="menu-btn">
<label for="menu-btn" class="menu-icon"><span class="navicon"></span></label>


上記コードに対して、.menu-btnにdisplay:noneを設定することで見えなくしています。

/* メニューがクリックされてるかどうかを判断するボタン */
.menu-btn {
  display: none;
}


ちなみに、display: none;を削除すると、チェックボックスが表示されます。


ハンバーガーをクリックするたびにチェックがついたり外れたりします。




labelタグによるinputタグの紐づけ

labelタグはinputタグと紐づけを行うためのものです。

紐づけをすることで、labelタグを選択すると関連付けられているinputタグが選択された状態になります

紐付け方法はとても簡単で、inputタグのid属性の値を、labelタグのfor属性の値に設定するだけです。

ちなみに、labelタグはinputタグの側に記述しなくても問題ありません(紐づけさえできていればいい。ただし可読性を考えたら隣接して記述するのがベター)。また、1つのinputタグに対して複数のlabelタグを紐づけることができます。

<input type="checkbox" class="menu-btn" id="menu-btn">
<label for="menu-btn" class="menu-icon"><span class="navicon"></span></label>

 ↓ inputタグとlabelタグの紐づけだけ表示

<input id="menu-btn">
<label for="menu-btn"></label>


(参考)MDN:label


:checked|疑似クラス

重要なポイントの2つ目は、inputタグのチェックボックスと合わせて使う疑似クラス :checkedです。

これが超便利で、チェックボックスにチェックがついているときのみCSSを適用することができます。

例えば次のように .menu-btn:checkedとして、3本線の一番上の横線を、-45度回転させる指示をしています。

/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}


:checkedを使ったCSSの指示は以下になります。

/* ボタンクリックでメニュー一覧を表示する */
.menu-btn:checked ~ .menu {
  transform: scale(1, 1);
  transform-origin: top;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


/* クリック時のみメニュー内のliとaタグを表示する */
.menu-btn:checked ~ .menu a,
.menu-btn:checked ~ .menu li {
  opacity: 1;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.2s;
}

/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}

/* 下側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:after {
  transform: rotate(45deg);
}


/* メニュークリック時の上の線の開始位置 */
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:before {
  top: 0;
}
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:after {
  bottom: 0;
}

/* メニュークリック時の真ん中の線(透明にする) */
.menu-btn:checked ~ .menu-icon .navicon {
  background: rgba(0, 0, 0, 0);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


間接セレクタ(チルダ ~ 波線)

セレクタの中で疑似要素:checkedの後ろに「~(チルダ)」という表記を使っています。

/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}


これは間接セレクタというもので、要素1 ~ 要素2{}のように記述します。

これは、要素1の親要素と同じ親を持つ、すべての要素2に指定したプロパティを適用する記述です。

:checkedと一緒に使われることが多く、とても便利な記述です。


参考

間接セレクタ「~」の具体的な使い方については下記をご参考ください。

【CSS】セレクタの:checkedと~は何をしているか?


例えば以下のコードの場合、class=”menu-btn”を持つ要素が選択状態のとき、同じ親要素の中でclass="menu-btn"よりも後ろにあるclass="menu-icon"の中のclass="navicon"がついている要素の前方(:before)の要素にCSSを適用するという意味になります。

/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}


3本線の作り方

ハンバーガーメニューの3本線はlabelタグの中のspanタグで作成しています。

<label for="menu-btn" class="menu-icon"><span class="navicon"></span></label>

1つのspanタグに対して長さ26px、高さ3pxの線を作成し、更に:beforeと:afterで上下の線を作成しています。

/* メニューの中央の横線 */
.navicon {
  background: gray;
  display: block;
  height: 3px;
  width: 26px;
  position: relative;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニューの上下の横線 */
.navicon:before,
.navicon:after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  position: absolute;
  background: gray;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニュー上の線の位置 */
.navicon:before {
  top: 9px;
}


/* メニュー下の線の位置 */
.navicon:after {
  bottom: 9px;
}


ここで超需要な枠割を果たしているのが、transitionプロパティやその中のcubic-bezierです。

cubic-bezierとは3字ベジェ曲線のことで、通過点と方向点という2つの点で曲線を描く方法です。Illustratorなどのデザインツールで使われることが多いです。


CSSでtransitionプロパティの値として「cubic-bezier」を指定したときは、元の状態から最終的な状態に変化するまでにどのような時間軸で変化させるかを指定することができます。

例えば、最初はゆっくりで後半は早く変化するといった形です。


なお、transitionプロパティが以下のような場合、

  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;


それぞれの値は以下を意味しています。

  • 変化開始から終了までの時間(transition-duration): 0.3秒
  • 変化方法(transition-timing-function): cubic-bezier(0.04, 0.04, 0.12, 0.96)
  • 遅延(transition-delay): 0.1s


transitionの詳細については下記をご参考ください。

なお、それぞれの中でtransition-propertyも合わせて使う方法を紹介しています。

transition-behaviorはdispalyプロパティのように、本来transitionを設定できない要素に対してtransitionを適用させるためのプロパティです。


クリック時に✕になる動き

ハンバーガーメニューをクリックしたときに✕になる動きは以下で指定しています。

/* メニューの中央の横線 */
.navicon {
  background: gray;
  display: block;
  height: 3px;
  width: 26px;
  position: relative;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニューの上下の横線 */
.navicon:before,
.navicon:after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  position: absolute;
  background: gray;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニュー上の線の位置 */
.navicon:before {
  top: 9px;
}


/* メニュー下の線の位置 */
.navicon:after {
  bottom: 9px;
}


/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:before {
  transform: rotate(-45deg);
}

/* 下側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:after {
  transform: rotate(45deg);
}


/* メニュークリック時の上下の線の位置 */
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:before {
  top: 0;
}
.menu-btn:checked ~ .menu-icon:not(.steps) .navicon:after {
  bottom: 0;
}

/* メニュークリック時の真ん中の線(透明にする) */
.menu-btn:checked ~ .menu-icon .navicon {
  background: rgba(0, 0, 0, 0);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


.menu-btn:checkedでチェックボックスにチェックが入ったときに、一番上と一番下の線はtransformで45度回転するという変形を指示しています。

transform: rotate(45deg);
transform

transformの使い方や動きについては下記をご参考ください。

【CSS】transformとは何か?使い方を実例で解説|translate, translateX, translateY, scale, skew


真ん中の線は透過度を100% background: rgba(0, 0, 0, 0); にして見えなくしています。


なお、スムーズに変形するように変形前の要素にtransitionを設定しています。

transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;

0.3秒かけてcubic-bezier(0.04, 0.04, 0.12, 0.96)のタイミングで変形する動きを0.1秒後に発動するという処理です。

※変形前の要素にtransitionをつけることで、開始⇔終了のどちらもに同じ動きを適用することができます。


クリック時に表示するリンク一覧

クリック時に表示するリンク一覧は以下のCSSが該当します。

/* メニュークリック時に表示するリンク一覧の背景 */
.menu {
  list-style: none;
  position: absolute;
  width: 100%;
  height: 100vh;
  top: 0;
  margin-top: 52px;
  padding: 0 0 10px 0;
  clear: both;
  background: var(--background-navbar);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
  transform: scale(1, 0);
  transform-origin: top;
}

/* ボタンクリックでメニュー一覧を表示する */
.menu-btn:checked ~ .menu {
  transform: scale(1);
  transform-origin: top;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


/* ハンバーガーメニュー内のテキスト */
.menu a {
  text-decoration: none;
  font-weight: 500;
  letter-spacing: 2px;
  font-size: 16px;
  text-transform: capitalize;
  color: #ddd;
  opacity: 0;
  transition: 0.5s;
}

/* ハンバーガーメニューの中の隙間 */
.menu li {
  border-top: 1px solid rgb(75, 75, 75);
  padding: 15px 0;
  margin: 0 54px;
  opacity: 0;
  transition: 0.5s;
}


/* クリック時のみメニュー内のliとaタグを表示する */
.menu-btn:checked ~ .menu a,
.menu-btn:checked ~ .menu li {
  opacity: 1;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.2s;
}



一番上で指定しているのが背景を覆うモーダルの部分です。

.menu {
  list-style: none;
  position: absolute;
  width: 100%;
  height: 100vh;
  top: 0;
  margin-top: 52px;
  padding: 0 0 10px 0;
  clear: both;
  background: var(--background-navbar);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
  transform: scale(1, 0);
  transform-origin: top;
}

width: 100%; height 100vh;とすることで全体を覆っています。

またデフォルト状態の形状をtransform: scale(1, 0);とすることで、x座標側のみ表示しています。つまり見えないけど、x座標の原点側に存在しているということです。

これに対して、チェック時にtransform: scale(1);で、X、Y座標方向ともに1倍になる、つまり、通常どおり表示するように指定しています。

.menu-btn:checked ~ .menu {
  transform: scale(1);
  transform-origin: top;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


また、メニューの中のaタグのテキストリンクもデフォルトではopacity: 0;で見えない状態にしてあるので、クリック時にopacity: 1;にして表示しています。

.menu-btn:checked ~ .menu a,
.menu-btn:checked ~ .menu li {
  opacity: 1;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.2s;
}


クリックで動いて✕になるハンバーガーメニューの解説は以上です。


メニューの棒線を2本にする

メニューの棒線を2本線にすることもできます。

See the Pen Untitled by pro gurashi (@pro-gurashi) on CodePen.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>ハンバーガーメニュー</title>
</head>
<body>
  
  <header class="header">
    <div class="navtext-container">
      <div class="navtext">This is Our Website</div>
    </div>
    <input type="checkbox" class="menu-btn" id="menu-btn">
    <label for="menu-btn" class="menu-icon"><span class="navicon"></span></label>
    <ul class="menu">
      <li class="top"><a href="#top">TOP</a></li>
      <li><a href="#company">COMPANY</a></li>
      <li><a href="#solutions">SOLUTIONS</a></li>
      <li><a href="#contact">CONTACT</a></li>
    </ul>
  </header>
  
</body>

</html>

/*********ハンバーガーメニューのアニメーション設定**********/
/* 変数 */
:root {
  --background-navbar: rgba(55, 55, 55, 0.98);
}


/* ヘッダーの背景色と固定、高さ */
.header {
  /* background: var(--background-navbar); */
  position: fixed;
  width: 100%;
  height: 52px;
}

/* メニュークリック時に表示するリンク一覧の背景 */
.menu {
  list-style: none;
  position: absolute;
  width: 100%;
  height: 100vh;
  top: 0;
  margin-top: 52px;
  padding: 0 0 10px 0;
  clear: both;
  background: var(--background-navbar);
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
  transform: scale(1, 0);
  transform-origin: top;
}

/* ボタンクリックでメニュー一覧を表示する */
.menu-btn:checked ~ .menu {
  transform: scale(1);
  transform-origin: top;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


/* ハンバーガーメニュー内のテキスト */
.menu a {
  text-decoration: none;
  font-weight: 500;
  letter-spacing: 2px;
  font-size: 16px;
  text-transform: capitalize;
  color: #ddd;
  opacity: 0;
  transition: 0.5s;
}

/* ハンバーガーメニューの中の隙間 */
.menu li {
  border-top: 1px solid rgb(75, 75, 75);
  padding: 15px 0;
  margin: 0 54px;
  opacity: 0;
  transition: 0.5s;
}


/* クリック時のみメニュー内のliとaタグを表示する */
.menu-btn:checked ~ .menu a,
.menu-btn:checked ~ .menu li {
  opacity: 1;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.2s;
}


/* メニューがクリックされてるかどうかを判断するボタン */
.menu-btn {
  display: none;
}


/* ハンバーガーメニューの位置。カーソルを乗せたときの挙動 */
.menu-icon {
  display: inline-block;
  position: relative;
  left: 88%;
  top: 8px;
  cursor: pointer;
  padding: 12px 14px;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}


/* メニューの中央の横線 */
.navicon {
  background: gray;
  display: block;
  height: 3px;
  width: 26px;
  position: relative;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}

/* メニューの上の横線 */
.navicon:after{
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  position: absolute;
  top: 9px;
  background: gray;
  transition: 0.3s cubic-bezier(0.04, 0.04, 0.12, 0.96) 0.1s;
}


/* 上側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon {
  transform: rotate(-45deg);
}

/* 下側の線のアニメーション */
.menu-btn:checked ~ .menu-icon .navicon:after {
  transform: rotate(90deg);
  top: 0;
}

/*********ここまで**********/



/* ハンバーガーメニュー横のボックスの設定 */
.navtext-container {
  width: 100%;
  height: 52px;
  position: absolute;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* ハンバーガーメニュー横のテキスト */
.navtext {
  position: absolute;
  text-transform: uppercase;
  color: #lightgray;
  letter-spacing: 4px;
  font-size: 20px;
}
タイトルとURLをコピーしました