Build portfolio site with real CV content
Single-page scrolling portfolio using Next.js 16, Tailwind CSS v4, and Framer Motion. Sections: Hero, About, Skills, Projects, Contact. Dark theme with orange/amber accents, scroll-reveal animations, and a sticky blurring navbar. All content populated from Khaalid's CV — real bio, skills, work context, and personal projects (server hosting, game dev, AI). Contact form wired to khaalidmnbaccar@gmail.com via mailto. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
155
src/components/Contact.tsx
Normal file
155
src/components/Contact.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import SectionWrapper from "./SectionWrapper";
|
||||
|
||||
function GitHubIcon() {
|
||||
return (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkedInIcon() {
|
||||
return (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function EmailIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" />
|
||||
<path d="M22 7L13.03 12.7a2 2 0 01-2.06 0L2 7" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const socials = [
|
||||
{ label: "GitHub", href: "https://github.com", Icon: GitHubIcon },
|
||||
{ label: "LinkedIn", href: "https://linkedin.com", Icon: LinkedInIcon },
|
||||
{ label: "Email", href: "mailto:khaalidmnbaccar@gmail.com", Icon: EmailIcon },
|
||||
];
|
||||
|
||||
const CONTACT_EMAIL = "khaalidmnbaccar@gmail.com";
|
||||
|
||||
const inputClass =
|
||||
"w-full bg-zinc-900/60 border border-zinc-800 rounded-lg px-4 py-3 text-white text-sm placeholder-zinc-600 focus:outline-none focus:border-orange-500/60 transition-colors";
|
||||
|
||||
export default function Contact() {
|
||||
const [form, setForm] = useState({ name: "", email: "", message: "" });
|
||||
const [status, setStatus] = useState<"idle" | "opening">("idle");
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setStatus("opening");
|
||||
const subject = `Portfolio contact from ${form.name}`;
|
||||
const body = `${form.message}\n\nReply to: ${form.email}`;
|
||||
window.location.href = `mailto:${CONTACT_EMAIL}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
||||
setTimeout(() => setStatus("idle"), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<SectionWrapper id="contact" className="py-28 px-6">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<div className="flex items-center gap-4 mb-16">
|
||||
<span className="font-mono text-orange-400 text-sm">04.</span>
|
||||
<h2 className="text-3xl font-bold text-white">Get In Touch</h2>
|
||||
<div className="flex-1 h-px bg-zinc-800" />
|
||||
</div>
|
||||
|
||||
<p className="text-zinc-400 text-base leading-relaxed text-center mb-12">
|
||||
I'm open to new opportunities and collaborations. Whether you have a project in
|
||||
mind or just want to connect — feel free to reach out.
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-5 mb-12">
|
||||
<div className="grid sm:grid-cols-2 gap-5">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm text-zinc-400 mb-2 font-medium">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
required
|
||||
value={form.name}
|
||||
onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}
|
||||
placeholder="Your name"
|
||||
className={inputClass}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm text-zinc-400 mb-2 font-medium">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
value={form.email}
|
||||
onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}
|
||||
placeholder="your@email.com"
|
||||
className={inputClass}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-sm text-zinc-400 mb-2 font-medium">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
required
|
||||
rows={5}
|
||||
value={form.message}
|
||||
onChange={(e) => setForm((f) => ({ ...f, message: e.target.value }))}
|
||||
placeholder="What's on your mind?"
|
||||
className={`${inputClass} resize-none`}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status !== "idle"}
|
||||
className="w-full py-3.5 bg-orange-500 hover:bg-orange-400 disabled:opacity-60 text-white font-semibold rounded-lg transition-all duration-200 hover:shadow-[0_0_25px_rgba(249,115,22,0.3)] active:scale-[0.99]"
|
||||
>
|
||||
{status === "opening" ? "Opening mail client…" : "Send Message"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="flex justify-center gap-8">
|
||||
{socials.map(({ label, href, Icon }) => (
|
||||
<a
|
||||
key={label}
|
||||
href={href}
|
||||
target={href.startsWith("mailto") ? undefined : "_blank"}
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 text-zinc-500 hover:text-orange-400 transition-colors text-sm"
|
||||
>
|
||||
<Icon />
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-20 pt-8 border-t border-zinc-900 text-center">
|
||||
<p className="font-mono text-zinc-600 text-xs">
|
||||
Built with Next.js & Tailwind CSS — Designed & Developed by Khaalid.M.N Baccar
|
||||
</p>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user