コンタクト/

マップ付きコンタクト

Preview

抽象的なSVGマップと拠点情報を組み合わせたコンタクトセクション。複数拠点の情報をエレガントに表示

Source Code
tsx
264 lines
1"use client";
2
3import { useState } from "react";
4
5export function ContactMap001() {
6 const [formData, setFormData] = useState({
7 name: "",
8 email: "",
9 message: "",
10 });
11
12 const handleSubmit = (e: React.FormEvent) => {
13 e.preventDefault();
14 };
15
16 const handleChange = (
17 e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
18 ) => {
19 setFormData((prev) => ({
20 ...prev,
21 [e.target.name]: e.target.value,
22 }));
23 };
24
25 const offices = [
26 { city: "東京", address: "渋谷区神南1-2-3", hours: "9:00 – 18:00" },
27 { city: "大阪", address: "北区梅田4-5-6", hours: "9:00 – 18:00" },
28 { city: "福岡", address: "中央区天神7-8-9", hours: "10:00 – 17:00" },
29 ];
30
31 return (
32 <section className="bg-background py-28 border-t border-border">
33 <div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
34 {/* ヘッダー */}
35 <div className="text-center">
36 <p className="text-[10px] uppercase tracking-[0.3em] text-muted-foreground">
37 Contact
38 </p>
39 <h2 className="mt-3 text-2xl font-medium tracking-wide text-foreground sm:text-3xl">
40 お問い合わせ
41 </h2>
42 <p className="mx-auto mt-4 max-w-md text-sm font-light leading-relaxed text-muted-foreground">
43 ご相談やご質問がございましたら、お気軽にお問い合わせください。
44 </p>
45 </div>
46
47 <div className="mt-6 h-px bg-border/40" />
48
49 <div className="mt-16 grid grid-cols-1 gap-16 lg:grid-cols-2">
50 {/* 左: マップ風ビジュアル + 拠点情報 */}
51 <div>
52 {/* SVGマップ風装飾 */}
53 <div className="relative overflow-hidden rounded-lg border border-border bg-muted/20 p-8">
54 {/* コーナードット */}
55 <div className="absolute left-2.5 top-2.5 h-1.5 w-1.5 rounded-full bg-foreground/15" />
56 <div className="absolute right-2.5 top-2.5 h-1.5 w-1.5 rounded-full bg-foreground/15" />
57 <div className="absolute bottom-2.5 left-2.5 h-1.5 w-1.5 rounded-full bg-foreground/15" />
58 <div className="absolute bottom-2.5 right-2.5 h-1.5 w-1.5 rounded-full bg-foreground/15" />
59
60 <svg
61 viewBox="0 0 400 240"
62 fill="none"
63 className="w-full text-foreground/[0.06]"
64 aria-hidden="true"
65 >
66 {/* グリッド線 */}
67 {Array.from({ length: 9 }, (_, i) => (
68 <line
69 key={`h-${i}`}
70 x1="0"
71 y1={i * 30}
72 x2="400"
73 y2={i * 30}
74 stroke="currentColor"
75 strokeWidth="0.5"
76 />
77 ))}
78 {Array.from({ length: 14 }, (_, i) => (
79 <line
80 key={`v-${i}`}
81 x1={i * 30}
82 y1="0"
83 x2={i * 30}
84 y2="240"
85 stroke="currentColor"
86 strokeWidth="0.5"
87 />
88 ))}
89 {/* 抽象的な島の輪郭 */}
90 <path
91 d="M180 40 C200 35, 240 30, 270 50 C290 65, 310 55, 320 70 C330 85, 340 100, 330 120 C320 140, 300 150, 280 145 C260 140, 250 155, 230 160 C210 165, 190 155, 175 140 C160 125, 150 105, 155 90 C160 75, 165 50, 180 40Z"
92 stroke="currentColor"
93 strokeWidth="1"
94 className="text-foreground/10"
95 fill="none"
96 />
97 {/* ロケーションピン */}
98 <circle cx="220" cy="70" r="4" className="fill-foreground/20" />
99 <circle cx="220" cy="70" r="1.5" className="fill-foreground/40" />
100 <circle cx="270" cy="110" r="4" className="fill-foreground/20" />
101 <circle cx="270" cy="110" r="1.5" className="fill-foreground/40" />
102 <circle cx="300" cy="135" r="4" className="fill-foreground/20" />
103 <circle cx="300" cy="135" r="1.5" className="fill-foreground/40" />
104 {/* 接続線 */}
105 <line
106 x1="220"
107 y1="70"
108 x2="270"
109 y2="110"
110 stroke="currentColor"
111 strokeWidth="0.5"
112 strokeDasharray="4 4"
113 className="text-foreground/10"
114 />
115 <line
116 x1="270"
117 y1="110"
118 x2="300"
119 y2="135"
120 stroke="currentColor"
121 strokeWidth="0.5"
122 strokeDasharray="4 4"
123 className="text-foreground/10"
124 />
125 </svg>
126 </div>
127
128 {/* 拠点一覧 */}
129 <div className="mt-8 space-y-4">
130 {offices.map((office, index) => (
131 <div
132 key={office.city}
133 className="flex items-start gap-4"
134 >
135 <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-border text-[10px] font-medium text-muted-foreground">
136 {String(index + 1).padStart(2, "0")}
137 </div>
138 <div className="flex-1">
139 <div className="flex items-center gap-3">
140 <p className="text-sm font-medium tracking-wide text-foreground">
141 {office.city}
142 </p>
143 <div className="h-px flex-1 bg-border/30" />
144 <p className="text-[10px] tracking-[0.15em] text-muted-foreground/60">
145 {office.hours}
146 </p>
147 </div>
148 <p className="mt-1 text-xs font-light tracking-wide text-muted-foreground">
149 {office.address}
150 </p>
151 </div>
152 </div>
153 ))}
154 </div>
155
156 {/* メールリンク */}
157 <div className="mt-8 flex items-center gap-4 border-t border-border/40 pt-6">
158 <svg
159 className="h-4 w-4 text-muted-foreground/60"
160 fill="none"
161 stroke="currentColor"
162 viewBox="0 0 24 24"
163 >
164 <path
165 strokeLinecap="round"
166 strokeLinejoin="round"
167 strokeWidth={1.5}
168 d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"
169 />
170 </svg>
171 <span className="text-xs font-light tracking-wide text-muted-foreground">
172 hello@example.com
173 </span>
174 </div>
175 </div>
176
177 {/* 右: フォーム */}
178 <div>
179 <form onSubmit={handleSubmit} className="space-y-8">
180 <div>
181 <label
182 htmlFor="contact-map-name"
183 className="block text-[10px] uppercase tracking-[0.2em] text-muted-foreground"
184 >
185 お名前
186 </label>
187 <input
188 type="text"
189 id="contact-map-name"
190 name="name"
191 value={formData.name}
192 onChange={handleChange}
193 className="mt-3 block w-full border-b border-border bg-transparent py-3 text-sm tracking-wide text-foreground placeholder-muted-foreground/40 transition-colors duration-200 focus:border-foreground focus:outline-none"
194 placeholder="山田 太郎"
195 />
196 </div>
197
198 <div>
199 <label
200 htmlFor="contact-map-email"
201 className="block text-[10px] uppercase tracking-[0.2em] text-muted-foreground"
202 >
203 メールアドレス
204 </label>
205 <input
206 type="email"
207 id="contact-map-email"
208 name="email"
209 value={formData.email}
210 onChange={handleChange}
211 className="mt-3 block w-full border-b border-border bg-transparent py-3 text-sm tracking-wide text-foreground placeholder-muted-foreground/40 transition-colors duration-200 focus:border-foreground focus:outline-none"
212 placeholder="your@email.com"
213 />
214 </div>
215
216 <div>
217 <label
218 htmlFor="contact-map-message"
219 className="block text-[10px] uppercase tracking-[0.2em] text-muted-foreground"
220 >
221 メッセージ
222 </label>
223 <textarea
224 id="contact-map-message"
225 name="message"
226 rows={5}
227 value={formData.message}
228 onChange={handleChange}
229 className="mt-3 block w-full resize-none border-b border-border bg-transparent py-3 text-sm tracking-wide text-foreground placeholder-muted-foreground/40 transition-colors duration-200 focus:border-foreground focus:outline-none"
230 placeholder="プロジェクトの概要やご要望をお聞かせください..."
231 />
232 </div>
233
234 <div className="flex items-center justify-between pt-2">
235 <p className="text-[10px] tracking-wide text-muted-foreground/50">
236 通常2営業日以内にご返信
237 </p>
238 <button
239 type="submit"
240 className="group inline-flex items-center gap-2 border-b border-foreground py-2 text-xs font-medium uppercase tracking-[0.15em] text-foreground transition-colors duration-200 hover:border-muted-foreground hover:text-muted-foreground"
241 >
242 送信する
243 <svg
244 className="h-3.5 w-3.5 transition-transform duration-200 group-hover:translate-x-0.5"
245 fill="none"
246 stroke="currentColor"
247 viewBox="0 0 24 24"
248 >
249 <path
250 strokeLinecap="round"
251 strokeLinejoin="round"
252 strokeWidth={1.5}
253 d="M17 8l4 4m0 0l-4 4m4-4H3"
254 />
255 </svg>
256 </button>
257 </div>
258 </form>
259 </div>
260 </div>
261 </div>
262 </section>
263 );
264}