アイキャッチの画像

Tailwind & Nextjsでレスポンシブ対応(スマホ用)ハンバーガーメニューを作成する

公開日:

更新日:

完成イメージ (スマホ画面ではヘッダーメニューがアイコンになります)

Profile Image
閉じる時の動きが滑らかではないのが若干気になりますが、かなり簡単に実装できます。

当サイトで使用しているハンバーガーメニューの実装コードを紹介します。

基本的な機能は下記になります。

・画面サイズがsm以下になるとヘッダーのメニューが隠れて、ハンバーガーボタンが表示されます。

・ハンバーガーメニューは画面右側からスライドされ表示、閉じるときもスライドで消えます。

・メニューを閉じたい時はバツボタンを押すか、メニュー外の影になっている部分を押しても閉じることができます。

コード実例

まずコードの全体像です。

Header.tsx

1"use client";
2
3/* eslint-disable */
4import Link from "next/link";
5import React, { useState } from "react";
6import HomeIcon from "@mui/icons-material/Home";
7import HeaderIcon from "./components/HeaderIcon";
8import AppBar from "@mui/material/AppBar";
9import { IconButton, Toolbar } from "@mui/material";
10import ArticleIcon from "@mui/icons-material/Article";
11import IntegrationInstructionsIcon from "@mui/icons-material/IntegrationInstructions";
12import EmailIcon from "@mui/icons-material/Email";
13import MenuIcon from "@mui/icons-material/Menu";
14import ClearIcon from "@mui/icons-material/Clear";
15const Header = () => {
16  const [openMenu, setOpenMenu] = useState(false);
17
18  const handleMenuOpen = () => {
19    setOpenMenu(!openMenu);
20  };
21
22  return (
23    <header className="border-b ">
24      <div className="flex flex-col">
25        <div
26          className={"flex items-center justify-between fixed top-0 z-10 w-full py-3 bg-[#798777]"}
27        >
28          <div
29            className={"mx-auto w-11/12 h-full flex items-center justify-center"}
30          >
31            <div className="w-1/3 flex items-center justify-center"></div>
32            <div className="w-1/3 flex items-center justify-center">
33              <Link href={"/"} className="hover:opacity-50">
34                <div className="w-[300px]">
35                  <HeaderIcon className="w-full h-full" />
36                </div>
37              </Link>
38            </div>
39            <div className="w-1/3 flex items-center justify-end"></div>
40          </div>
41        </div>
42        <div>
43          {/* ここからハンバーガーメニューの実装 */}
44          <nav
45            className={
46              openMenu
47                ? " fixed bg-black bg-opacity-40 left-0 top-0 z-50  w-6/12 h-screen flex flex-col justify-start pt-8 px-3 delay-500 fade-in-100 duration-500 "
48                : "fixed right-[-100%] delay-500 fade-out-100 duration-400"
49            }
50            onClick={handleMenuOpen}
51          ></nav>
52          <nav
53            className={
54              openMenu
55                ? "text-left fixed bg-slate-100 right-0 top-0 z-50  w-6/12 h-screen flex flex-col justify-start pt-8 px-3 delay-75 ease-linear duration-500"
56                : "fixed right-[-100%] top-0 delay-75 ease-linear duration-400 z-50"
57            }
58          >
59            <div>
60              <div className="-ml-1">
61                <IconButton onClick={handleMenuOpen}>
62                  <ClearIcon fontSize="large" sx={{ color: "black" }} />
63                </IconButton>
64              </div>
65              <ul className="mt-0">
66                <li className="p-3">
67                  <Link
68                    onClick={handleMenuOpen}
69                    href={"/"}
70                    className="hover:opacity-50 "
71                  >
72                    <div className="flex flex-row  min-w-20 items-center">
73                      <HomeIcon fontSize="small" sx={{ color: "black" }} />
74                      <p className=" ml-1 text-base font-semibold min-w-14 sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
75                        ホーム
76                      </p>
77                    </div>
78                  </Link>
79                </li>
80                <li className="p-3">
81                  <Link
82                    onClick={handleMenuOpen}
83                    href={"/mix"}
84                    className="hover:opacity-50 min-w-5"
85                  >
86                    <div className="flex flex-row   min-w-20 items-center">
87                      <ArticleIcon fontSize="small" sx={{ color: "black" }} />
88                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
89                        雑多記事
90                      </p>
91                    </div>
92                  </Link>
93                </li>
94                <li className="p-3">
95                  <Link
96                    onClick={handleMenuOpen}
97                    href={"/code"}
98                    className="hover:opacity-50 min-w-5"
99                  >
100                    <div className="flex flex-row   items-center">
101                      <IntegrationInstructionsIcon
102                        fontSize="small"
103                        sx={{ color: "black" }}
104                      />
105                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
106                        開発
107                      </p>
108                    </div>
109                  </Link>
110                </li>
111              </ul>
112            </div>
113          </nav>
114
115          <AppBar position="static" sx={{ bgcolor: "#798777" }}>
116            <Toolbar>
117              <div className="flex sm:hidden  justify-end w-full">
118                <IconButton onClick={handleMenuOpen}>
119                  <MenuIcon fontSize="large" sx={{ color: "black" }} />
120                </IconButton>
121              </div>
122              <div className=" flex-row mx-auto gap-5 sm:gap-10 hidden sm:flex">
123                <div className="">
124                  <Link href={"/"} className="hover:opacity-50 ">
125                    <div className="flex flex-row   min-w-20 items-center">
126                      <HomeIcon fontSize="small" sx={{ color: "black" }} />
127                      <p className=" ml-1 text-base font-semibold min-w-14 sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
128                        ホーム
129                      </p>
130                    </div>
131                  </Link>
132                </div>
133                <div className="">
134                  <Link href={"/mix"} className="hover:opacity-50 min-w-5">
135                    <div className="flex flex-row   min-w-20 items-center">
136                      <ArticleIcon fontSize="small" sx={{ color: "black" }} />
137                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
138                        雑多記事
139                      </p>
140                    </div>
141                  </Link>
142                </div>
143                <div className="">
144                  <Link href={"/code"} className="hover:opacity-50 min-w-5">
145                    <div className="flex flex-row   items-center">
146                      <IntegrationInstructionsIcon
147                        fontSize="small"
148                        sx={{ color: "black" }}
149                      />
150                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
151                        開発
152                      </p>
153                    </div>
154                  </Link>
155                </div>
156                <div className="">
157                  <Link href={"/contact"} className="hover:opacity-50 min-w-5">
158                    <div className="flex flex-row  minw-20 items-center">
159                      <EmailIcon fontSize="small" sx={{ color: "black" }} />
160                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
161                        お問い合わせ
162                      </p>
163                    </div>
164                  </Link>
165                </div>
166              </div>
167            </Toolbar>
168          </AppBar>
169        </div>
170      </div>
171    </header>
172  );
173};
174
175export default Header;
176
Profile Image
コピペしてそのまま使えると思います。(アイコンのライブラリなどはお好きなものを使って下さい)

詳細の説明 44〜51行目

Header.tsx

1<nav
2 className={
3     openMenu
4     ? "fixed bg-black bg-opacity-40 left-0 top-0 z-50  w-6/12 h-screflex flex-col  justify-start pt-8 px-3 delay-500 fade-in-1 duration-500 "
5     : "fixed right-[-100%] delay-500 fade-out-100 duration-400"
6 }
7 onClick={handleMenuOpen}
8></nav>

一つ目のnavはメニューが出た時の黒い影になっている部分です。

onClick={handleMenuOpen}を書くことで影の部分を押してもサイドメニュー(ハンバーガーメニュー)を閉じることができます。

詳細の説明 52〜113行目

Header.tsx

1
2<nav
3    className={
4        openMenu
5        ? "text-left fixed bg-slate-100 right-0 top-0 z-50  w-6/12 h-screeflex flex-col justify-start pt-8 px-3 delay-75 ease-linear duration-500"
6        : "fixed right-[-100%] top-0 delay-75 ease-linear duration-400 z-50"
7    }
8>
9            <div>
10              <div className="-ml-1">
11                <IconButton onClick={handleMenuOpen}>
12                  <ClearIcon fontSize="large" sx={{ color: "black" }} />
13                </IconButton>
14              </div>
15              <ul className="mt-0">
16                <li className="p-3">
17                  <Link
18                    onClick={handleMenuOpen}
19                    href={"/"}
20                    className="hover:opacity-50 "
21                  >
22                    <div className="flex flex-row  min-w-20 items-center">
23                      <HomeIcon fontSize="small" sx={{ color: "black" }} />
24                      <p className=" ml-1 text-base font-semibold min-w-14 sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
25                        ホーム
26                      </p>
27                    </div>
28                  </Link>
29                </li>
30                <li className="p-3">
31                  <Link
32                    onClick={handleMenuOpen}
33                    href={"/mix"}
34                    className="hover:opacity-50 min-w-5"
35                  >
36                    <div className="flex flex-row   min-w-20 items-center">
37                      <ArticleIcon fontSize="small" sx={{ color: "black" }} />
38                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
39                        雑多記事
40                      </p>
41                    </div>
42                  </Link>
43                </li>
44                <li className="p-3">
45                  <Link
46                    onClick={handleMenuOpen}
47                    href={"/code"}
48                    className="hover:opacity-50 min-w-5"
49                  >
50                    <div className="flex flex-row   items-center">
51                      <IntegrationInstructionsIcon
52                        fontSize="small"
53                        sx={{ color: "black" }}
54                      />
55                      <p className="ml-1 text-base font-semibold  sm:text-lg sm:font-bold text-black hover:underline underline-offset-8 decoration-2">
56                        開発
57                      </p>
58                    </div>
59                  </Link>
60                </li>
61              </ul>
62            </div>
63</nav>

二つ目のnavがサイドメニュー(ハンバーガーメニュー)です。

ease-linearは一定量の速度で変化させる、という意味です。

duration-400はどのくらいの時間をかけて変化させるか、という意味です。(数値が少ないと早く、多いと遅くなります)

delay-75はボタンを押してから少しだけ遅れてアニメーションが開始されるように設定している部分です。

まとめ

本サイトのサイドメニュー(ハンバーガーメニュー)は下記のオカログさんの記事を参考に作成させて頂きました。

※参考記事: React×Tailwindで作るハンバーガーメニュー!

説明などもとても分かりやすく参考になりました。皆さんもぜひ参照してみて下さい。

tailwindのアニメーションの説明などは下記の記事を参考にさせて頂きました。

※参考記事: Transitions|1から始めるTailwindCSS

本記事は以上になります。

ご一読頂きありがとうございました。