قسمت هفتم - ساخت صفحه درباره من و تماس در Next.js
صفحه About به بازدیدکننده میگوید شما چه کسی هستید و روی چه چیزی تمرکز دارید صفحه Contact مسیر ارتباط و شروع همکاری را برای کارفرما، مشتری یا همتیمی باز میکند
ساخت صفحه درباره من و تماس در Next.js
تا اینجای کار، ما برای پروژهمان چند بخش مهم را ساختهایم:
- معماری کلی پروژه
- Routing و Layoutها در App Router
- استایلدهی با Tailwind CSS
- صفحه اصلی پرتفولیو
- Header و Footer
حالا وقت آن است که دو صفحه مهم دیگر را اضافه کنیم: About و Contact.
این دو صفحه در اکثر پرتفولیوها نقش کلیدی دارند، چون:
- صفحه About به بازدیدکننده میگوید شما چه کسی هستی و روی چه چیزی تمرکز داری
- صفحه Contact مسیر ارتباط و شروع همکاری را برای کارفرما، مشتری یا همتیمی باز میکند
صفحه درباره من چه محتوایی دارد؟
صفحه About قرار نیست فقط یک متن طولانی و بیساختار باشد.
بهتر است این صفحه چند هدف مشخص را پوشش دهد:
- معرفی کوتاه شخصی
- بیان حوزه تخصص
- اشاره به تجربه کاری یا مسیر یادگیری
- معرفی تکنولوژیهایی که با آنها کار میکنیم
- نشان دادن نوع پروژههایی که روی آنها تمرکز داریم
- ایجاد اعتماد برای همکاری
برای پروژهای که تا اینجا ساختهایم، یک ساختار مناسب برای صفحه About میتواند این باشد:
- Hero کوتاه صفحه
- معرفی تجربه و تخصص
- فهرست مهارتها یا حوزههای کاری
- یک CTA برای همکاری یا تماس
ساخت صفحه About
اول route مربوط به این صفحه را در app/about/page.tsx میسازیم.
// app/about/page.tsx
import { AboutIntroSection } from '@/components/sections/about/about-intro-section'
import { ExperienceSection } from '@/components/sections/about/experience-section'
import { ExpertiseSection } from '@/components/sections/about/expertise-section'
import { AboutCtaSection } from '@/components/sections/about/about-cta-section'
export default function AboutPage() {
return (
<div className="space-y-16">
<AboutIntroSection />
<ExperienceSection />
<ExpertiseSection />
<AboutCtaSection />
</div>
)
}
در اینجا مثل صفحه اصلی، صفحه را به چند section مستقل تقسیم کردیم.
این الگو باعث میشود:
- کد خواناتر باشد
- توسعه سادهتر شود
- در آینده اگر بخواهیم محتوای صفحه را از CMS می گیریم، راحتتر sectionها را جداگانه مدیریت می کنیم
Hero کوتاه برای About
اولین بخش صفحه About معمولاً معرفی سریع است.
// components/sections/about/about-intro-section.tsx
export function AboutIntroSection() {
return (
<section className="rounded-2xl border border-slate-800 bg-slate-900/60 p-8 sm:p-10">
<span className="text-sm font-medium text-cyan-400">About Me</span>
<h1 className="mt-4 text-3xl font-bold tracking-tight text-slate-50 sm:text-4xl">
Building modern web experiences with focus on clarity, performance, and
maintainability.
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-300 sm:text-base">
I am a frontend-focused web developer building portfolio, dashboard,
blog, and content-driven platforms with Next.js, React, Tailwind CSS,
Prisma, and PostgreSQL. I care about clean architecture, reusable
components, and a professional user experience.
</p>
</section>
)
}
این بخش باید خیلی سریع به کاربر نشان دهد:
- چه کسی هستی
- روی چه نوع پروژههایی کار میکنی
- رویکردت در توسعه چیست
معرفی تجربه و تخصص
صفحه About یکی از بهترین جاها برای معرفی تجربه و نوع کار حرفهای ما است.
لازم نیست حتماً رزومه کامل بنویسیم؛ کافی است روشن و حرفهای توضیح بدهیم که چه میسازیم و در چه چیزهایی تخصص داریم.
// components/sections/about/experience-section.tsx
export function ExperienceSection() {
return (
<section className="grid gap-6 lg:grid-cols-2">
<div className="rounded-2xl border border-slate-800 bg-slate-900/50 p-6">
<h2 className="text-xl font-semibold text-slate-50">Experience</h2>
<p className="mt-4 text-sm leading-7 text-slate-300">
My work is centered around building structured and scalable web
applications. I enjoy turning product ideas into well-organized
interfaces with clear routing, reusable UI sections, and maintainable
code.
</p>
<p className="mt-4 text-sm leading-7 text-slate-300">
I focus on developer portfolios, project showcases, blog systems,
admin dashboards, and custom CMS-style workflows where content
management and usability both matter.
</p>
</div>
<div className="rounded-2xl border border-slate-800 bg-slate-900/50 p-6">
<h2 className="text-xl font-semibold text-slate-50">Approach</h2>
<p className="mt-4 text-sm leading-7 text-slate-300">
I prefer a clean project structure from the beginning: thoughtful
folder organization, shared utilities, validation with Zod, database
modeling with Prisma, and component separation that supports future
growth.
</p>
<p className="mt-4 text-sm leading-7 text-slate-300">
My goal is not only to make pages look good, but also to make them
easier to extend, test, and maintain over time.
</p>
</div>
</section>
)
}
نمایش تخصصها و حوزههای کاری
بعد از معرفی متنی، بهتر است تخصصها را بهشکل خلاصه و ساختاریافته نمایش دهیم.
این بخش هم برای خوانایی بهتر است، هم برای اینکه کاربر سریعتر متوجه تواناییها شود.
// components/sections/about/expertise-section.tsx
const expertiseItems = [
{
title: 'Frontend Development',
description:
'Building responsive and polished interfaces with Next.js, React, and Tailwind CSS.',
},
{
title: 'Dashboard & CMS UI',
description:
'Designing content management flows, admin layouts, and structured data views.',
},
{
title: 'Database Integration',
description:
'Working with Prisma and PostgreSQL for scalable application data models.',
},
{
title: 'Validation & Auth Foundations',
description:
'Preparing reliable forms and secure flows with Zod, bcrypt, and server-side logic.',
},
]
export function ExpertiseSection() {
return (
<section>
<div className="max-w-2xl">
<span className="text-sm font-medium text-cyan-400">Expertise</span>
<h2 className="mt-3 text-2xl font-bold tracking-tight text-slate-50 sm:text-3xl">
What I focus on
</h2>
<p className="mt-4 text-sm leading-7 text-slate-300 sm:text-base">
I build web applications that are not only visually clean, but also
practical to manage, scale, and extend.
</p>
</div>
<div className="mt-8 grid gap-4 sm:grid-cols-2">
{expertiseItems.map((item) => (
<div
key={item.title}
className="rounded-2xl border border-slate-800 bg-slate-900/50 p-6"
>
<h3 className="text-base font-semibold text-slate-50">
{item.title}
</h3>
<p className="mt-3 text-sm leading-6 text-slate-300">
{item.description}
</p>
</div>
))}
</div>
</section>
)
}
این الگو از نظر UI هم با بخش Skillها و Cardهایی که قبلاً در صفحه اصلی ساختیم هماهنگ است.
CTA همکاری در صفحه About
بعد از اینکه بازدیدکننده تو را شناخت، باید یک قدم بعدی مشخص هم ببیند.
برای همین در انتهای صفحه About، یک CTA میگذاریم که او را به صفحه تماس هدایت کند.
// components/sections/about/about-cta-section.tsx
import Link from 'next/link'
export function AboutCtaSection() {
return (
<section className="rounded-2xl border border-cyan-500/20 bg-cyan-500/5 p-8 sm:p-10">
<h2 className="text-2xl font-bold tracking-tight text-slate-50">
Interested in working together?
</h2>
<p className="mt-4 max-w-2xl text-sm leading-7 text-slate-300 sm:text-base">
If you need a modern portfolio, a structured dashboard, or a clean
content-driven website, feel free to reach out and start a conversation.
</p>
<div className="mt-6">
<Link
href="/contact"
className="inline-flex items-center justify-center rounded-md bg-cyan-500 px-5 py-3 text-sm font-semibold text-slate-950 transition hover:bg-cyan-400"
>
Contact Me
</Link>
</div>
</section>
)
}
ساخت صفحه تماس
صفحه Contact یکی از مهمترین صفحات هر پرتفولیو است.
هدف این صفحه فقط نمایش ایمیل نیست؛ بلکه باید کاربر را برای ارتباط راحتتر هدایت کند.
در این مرحله، چون هنوز فرم کامل تماس با validation و ارسال داده نساختهایم، یک نسخه حرفهای و ساده میسازیم که شامل این موارد باشد:
- معرفی کوتاه
- نمایش راههای ارتباطی
- CTA همکاری
بعداً اگر خواستیم، در پست جداگانه میتوانیم فرم تماس واقعی را با Server Actions و Zod پیادهسازی کنیم.
ساخت route صفحه Contact
// app/contact/page.tsx
import { ContactIntroSection } from '@/components/sections/contact/contact-intro-section'
import { ContactMethodsSection } from '@/components/sections/contact/contact-methods-section'
import { ContactCtaSection } from '@/components/sections/contact/contact-cta-section'
export default function ContactPage() {
return (
<div className="space-y-16">
<ContactIntroSection />
<ContactMethodsSection />
<ContactCtaSection />
</div>
)
}
بخش معرفی صفحه تماس
// components/sections/contact/contact-intro-section.tsx
export function ContactIntroSection() {
return (
<section className="rounded-2xl border border-slate-800 bg-slate-900/60 p-8 sm:p-10">
<span className="text-sm font-medium text-cyan-400">Contact</span>
<h1 className="mt-4 text-3xl font-bold tracking-tight text-slate-50 sm:text-4xl">
Let’s talk about your project
</h1>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-300 sm:text-base">
Whether you want a portfolio, a dashboard, a blog system, or a custom
content workflow, I am open to discussing ideas, scope, and the right
implementation approach.
</p>
</section>
)
}
نمایش راههای ارتباطی
در این بخش، اطلاعات تماس را بهصورت کارت نمایش میدهیم.
این روش خیلی بهتر از نوشتن چند خط ساده زیر هم است، چون:
- خواناتر است
- ظاهر حرفهایتری دارد
- توسعه آن در آینده راحتتر است
// components/sections/contact/contact-methods-section.tsx
const contactMethods = [
{
title: 'Email',
value: 'hello@example.com',
href: 'mailto:hello@example.com',
},
{
title: 'GitHub',
value: 'github.com/your-username',
href: 'https://github.com/your-username',
},
{
title: 'LinkedIn',
value: 'linkedin.com/in/your-username',
href: 'https://linkedin.com/in/your-username',
},
]
export function ContactMethodsSection() {
return (
<section>
<div className="max-w-2xl">
<span className="text-sm font-medium text-cyan-400">Reach Out</span>
<h2 className="mt-3 text-2xl font-bold tracking-tight text-slate-50 sm:text-3xl">
Ways to connect
</h2>
<p className="mt-4 text-sm leading-7 text-slate-300 sm:text-base">
Choose the channel that works best for you. For project inquiries,
email is usually the fastest option.
</p>
</div>
<div className="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{contactMethods.map((item) => (
<a
key={item.title}
href={item.href}
target="_blank"
rel="noreferrer"
className="rounded-2xl border border-slate-800 bg-slate-900/50 p-6 transition hover:border-cyan-500/40 hover:bg-slate-900"
>
<h3 className="text-base font-semibold text-slate-50">
{item.title}
</h3>
<p className="mt-3 break-words text-sm leading-6 text-slate-300">
{item.value}
</p>
</a>
))}
</div>
</section>
)
}
یک نکته مهم
در اینجا چون لینکها خارجی هستند یا mailto: هستند، از a استفاده کردیم، نه Link.
mailto:یک navigation داخلی اپ نیست- GitHub و LinkedIn هم external link هستند
پس همچنان همان قاعده قبلی پابرجاست:
- لینک داخلی $\rightarrow$
Link - لینک خارجی $\rightarrow$
a
CTA همکاری در صفحه تماس
در انتهای صفحه Contact بهتر است یک متن واضح داشته باشیم که نوع همکاری را روشن کند.
// components/sections/contact/contact-cta-section.tsx
export function ContactCtaSection() {
return (
<section className="rounded-2xl border border-cyan-500/20 bg-cyan-500/5 p-8 sm:p-10">
<h2 className="text-2xl font-bold tracking-tight text-slate-50">
Open to meaningful projects
</h2>
<p className="mt-4 max-w-2xl text-sm leading-7 text-slate-300 sm:text-base">
I am interested in building clean and maintainable web experiences,
especially portfolios, blogs, dashboards, and content-focused platforms
with modern frontend architecture.
</p>
</section>
)
}
این بخش کمک میکند صفحه Contact فقط یک لیست راه ارتباطی نباشد، بلکه لحن حرفهای پروژه را هم حفظ کند.
اگر بخواهی نسخه کاملتر بسازی
در این مرحله ما صفحه تماس را بدون فرم ساختیم، چون هنوز وارد بخش فرمها نشدهایم.
اما ساختار فعلی کاملاً آماده است که بعداً این موارد به آن اضافه شوند:
- فرم تماس با
name,email,message - اعتبارسنجی با
Zod - ارسال با
Server Actions - نمایش stateهایی مثل loading, success, error
- ذخیره پیامها در دیتابیس با Prisma
- یا ارسال ایمیل از طریق سرویسهای خارجی
پس این صفحه هم از نظر طراحی و هم از نظر معماری، پایه خوبی برای ادامه کار است.
ساختار نهایی فایلها
بعد از اضافه شدن این دو صفحه، ساختار پروژه میتواند به این شکل برسد:
app/
├── about/
│ └── page.tsx
├── contact/
│ └── page.tsx
├── blog/
│ └── page.tsx
├── projects/
│ └── page.tsx
├── layout.tsx
└── page.tsx
components/
├── layout/
│ ├── container.tsx
│ ├── footer.tsx
│ ├── header.tsx
│ ├── nav-link.tsx
│ └── site-nav-links.ts
├── sections/
│ ├── about/
│ │ ├── about-cta-section.tsx
│ │ ├── about-intro-section.tsx
│ │ ├── experience-section.tsx
│ │ └── expertise-section.tsx
│ ├── contact/
│ │ ├── contact-cta-section.tsx
│ │ ├── contact-intro-section.tsx
│ │ └── contact-methods-section.tsx
│ ├── about-snippet.tsx
│ ├── cta-section.tsx
│ ├── hero-section.tsx
│ └── skills-section.tsx
└──
اگر بخواهی حتی مرتبترش کنیم، میتوانیم برای sectionهای صفحه اصلی هم پوشه جدا بسازیم، ولی در همین مرحله اجباری نیست.
چند نکته طراحی برای هماهنگی با پروژه
برای اینکه این دو صفحه با بقیه پروژه هماهنگ بمانند، بهتر است این اصول را نگه داریم:
-
استفاده از همان palette:
bg-slate-950bg-slate-900text-slate-50text-slate-300border-slate-800cyan-500
-
استفاده از همان radius و spacing:
rounded-2xlp-6,p-8,p-10space-y-16
-
حفظ ساختار section-based
-
استفاده از متن کوتاه، حرفهای و واضح
-
اجتناب از شلوغی بیش از حد در About و Contact
جمعبندی
در این پست، دو صفحه مهم دیگر از سایت پرتفولیوی خودمان را ساختیم:
صفحه About
که شامل این بخشها بود:
- معرفی کوتاه
- توضیح تجربه و رویکرد کاری
- نمایش تخصصها
- CTA برای هدایت به صفحه تماس
صفحه Contact
که شامل این بخشها بود:
- معرفی کوتاه
- نمایش راههای ارتباطی
- CTA همکاری