ナビゲーション/

ドロップダウンナビゲーション

Preview

ホバーで展開するドロップダウンメニュー付きのナビゲーションバー。プロダクト・ソリューション等の階層構造を持つメニューに最適

Source Code
tsx
217 lines
1"use client";
2
3import { useState } from "react";
4import Link from "next/link";
5
6const navItems = [
7 {
8 label: "プロダクト",
9 children: [
10 {
11 title: "アナリティクス",
12 description: "データを可視化し、意思決定を支援",
13 },
14 {
15 title: "オートメーション",
16 description: "ワークフローを自動化して効率化",
17 },
18 {
19 title: "インテグレーション",
20 description: "既存ツールとシームレスに連携",
21 },
22 ],
23 },
24 {
25 label: "ソリューション",
26 children: [
27 {
28 title: "スタートアップ向け",
29 description: "成長フェーズに最適化されたプラン",
30 },
31 {
32 title: "エンタープライズ向け",
33 description: "大規模組織のための拡張性",
34 },
35 ],
36 },
37 { label: "料金", children: null },
38 { label: "ブログ", children: null },
39];
40
41export function NavigationDropdown001() {
42 const [openIndex, setOpenIndex] = useState<number | null>(null);
43 const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
44
45 return (
46 <header className="w-full bg-background">
47 <nav className="mx-auto flex max-w-7xl items-center justify-between px-4 py-5 sm:px-6 lg:px-8">
48 {/* ロゴ */}
49 <Link href="#" className="flex items-center gap-2">
50 <div className="flex h-7 w-7 items-center justify-center rounded-md border border-border bg-muted">
51 <span className="text-xs font-medium text-foreground">N</span>
52 </div>
53 <span className="text-sm font-medium tracking-wide text-foreground">
54 Notion
55 </span>
56 </Link>
57
58 {/* デスクトップナビゲーション */}
59 <div className="hidden items-center gap-1 lg:flex">
60 {navItems.map((item, index) =>
61 item.children ? (
62 <div
63 key={item.label}
64 className="relative"
65 onMouseEnter={() => setOpenIndex(index)}
66 onMouseLeave={() => setOpenIndex(null)}
67 >
68 <button className="flex items-center gap-1 rounded-md px-3 py-2 text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:text-foreground">
69 {item.label}
70 <svg
71 className={`h-3.5 w-3.5 transition-transform duration-200 ${
72 openIndex === index ? "rotate-180" : ""
73 }`}
74 fill="none"
75 stroke="currentColor"
76 viewBox="0 0 24 24"
77 >
78 <path
79 strokeLinecap="round"
80 strokeLinejoin="round"
81 strokeWidth={1.5}
82 d="M19 9l-7 7-7-7"
83 />
84 </svg>
85 </button>
86
87 {openIndex === index && (
88 <div className="absolute left-0 top-full z-50 pt-2">
89 <div className="w-72 rounded-xl border border-border/60 bg-background p-2 shadow-sm">
90 {item.children.map((child) => (
91 <Link
92 key={child.title}
93 href="#"
94 className="group block rounded-lg px-3 py-3 transition-colors duration-150 hover:bg-muted"
95 >
96 <div className="text-sm font-medium tracking-wide text-foreground">
97 {child.title}
98 </div>
99 <div className="mt-1 text-xs font-light leading-relaxed text-muted-foreground">
100 {child.description}
101 </div>
102 </Link>
103 ))}
104 </div>
105 </div>
106 )}
107 </div>
108 ) : (
109 <Link
110 key={item.label}
111 href="#"
112 className="rounded-md px-3 py-2 text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:text-foreground"
113 >
114 {item.label}
115 </Link>
116 )
117 )}
118 </div>
119
120 {/* デスクトップCTA */}
121 <div className="hidden items-center gap-4 lg:flex">
122 <Link
123 href="#"
124 className="text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:text-foreground"
125 >
126 ログイン
127 </Link>
128 <Link
129 href="#"
130 className="rounded-md bg-foreground px-4 py-2 text-sm font-medium tracking-wide text-background transition-colors duration-200 hover:bg-foreground/90"
131 >
132 無料で始める
133 </Link>
134 </div>
135
136 {/* モバイルメニューボタン */}
137 <button
138 className="rounded-md p-1.5 text-muted-foreground hover:text-foreground lg:hidden"
139 onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
140 >
141 <svg
142 className="h-5 w-5"
143 fill="none"
144 stroke="currentColor"
145 viewBox="0 0 24 24"
146 >
147 {mobileMenuOpen ? (
148 <path
149 strokeLinecap="round"
150 strokeLinejoin="round"
151 strokeWidth={1.5}
152 d="M6 18L18 6M6 6l12 12"
153 />
154 ) : (
155 <path
156 strokeLinecap="round"
157 strokeLinejoin="round"
158 strokeWidth={1.5}
159 d="M4 6h16M4 12h16M4 18h16"
160 />
161 )}
162 </svg>
163 </button>
164 </nav>
165
166 {/* モバイルメニュー */}
167 {mobileMenuOpen && (
168 <div className="border-t border-border/40 bg-background px-4 pb-6 lg:hidden">
169 <div className="space-y-1 pt-4">
170 {navItems.map((item) => (
171 <div key={item.label}>
172 {item.children ? (
173 <div className="space-y-1">
174 <div className="px-3 py-2 text-[10px] uppercase tracking-[0.2em] text-muted-foreground/60">
175 {item.label}
176 </div>
177 {item.children.map((child) => (
178 <Link
179 key={child.title}
180 href="#"
181 className="block rounded-lg px-3 py-2.5 text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:bg-muted hover:text-foreground"
182 >
183 {child.title}
184 </Link>
185 ))}
186 <div className="mx-3 my-2 h-px bg-border/40" />
187 </div>
188 ) : (
189 <Link
190 href="#"
191 className="block rounded-lg px-3 py-2.5 text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:bg-muted hover:text-foreground"
192 >
193 {item.label}
194 </Link>
195 )}
196 </div>
197 ))}
198 </div>
199 <div className="mt-4 flex flex-col gap-2">
200 <Link
201 href="#"
202 className="rounded-lg px-3 py-2.5 text-center text-sm tracking-wide text-muted-foreground transition-colors duration-200 hover:bg-muted"
203 >
204 ログイン
205 </Link>
206 <Link
207 href="#"
208 className="rounded-md bg-foreground px-4 py-2.5 text-center text-sm font-medium tracking-wide text-background"
209 >
210 無料で始める
211 </Link>
212 </div>
213 </div>
214 )}
215 </header>
216 );
217}