كيفية إنشاء موقع معرض أعمال احترافي باستخدام Next.js وTailwindCSS

دقائق القراءة: 11

يمثل موقع معرض الأعمال الشخصي نقطة انطلاق مهمة لأي مطور ويب أو محترف تقني يبحث عن فرصة عمل أو يريد بناء حضور رقمي قوي. فبدلاً من الاكتفاء بإرسال السيرة الذاتية، يمنحك الموقع الشخصي مساحة عملية لعرض مهاراتك، ومشاريعك، وخبراتك، وطريقتك في التفكير، وكل ذلك ضمن تجربة احترافية تعكس هويتك التقنية.

في هذا الدليل، سنشرح كيفية بناء موقع Portfolio حديث باستخدام Next.js وTailwindCSS، مع توضيح البنية العامة للمشروع، وأهم الصفحات، وآلية دعم الوضع الليلي، وجلب أحدث المستودعات من GitHub، ثم نشر المشروع على Vercel. الهدف هنا ليس مجرد ترجمة الفكرة، بل تقديم شرح عربي احترافي ومفيد يساعدك على تنفيذ موقع فعلي قابل للتخصيص والنشر.

تصميم واجهة موقع معرض أعمال مبني باستخدام Next.js وTailwindCSS

لماذا تحتاج إلى موقع معرض أعمال شخصي؟

امتلاك موقع شخصي لم يعد خياراً إضافياً، بل أصبح أداة فعالة لرفع فرصك في الظهور أمام مسؤولي التوظيف والعملاء المحتملين. فعندما يرى الزائر نماذج عملك بشكل مباشر، يصبح من الأسهل تقييم مستواك العملي بعيداً عن العبارات العامة الموجودة في السيرة الذاتية.

  • عرض المشاريع بطريقة منظمة وجذابة.
  • إبراز المهارات التقنية والأدوات التي تتقنها.
  • تقديم نبذة احترافية عنك وعن مسارك المهني.
  • نشر مقالات تقنية أو شروحات أو مقتطفات كود قابلة لإعادة الاستخدام.
  • تعزيز العلامة الشخصية وبناء حضور موثوق على الإنترنت.

والميزة الأهم أن موقعك الشخصي يمنحك حرية كاملة في تقديم نفسك بالطريقة التي تناسبك، بدلاً من الاعتماد فقط على منصات خارجية محدودة القالب.

التقنيات المستخدمة في بناء الموقع

إطار Next.js

يعد Next.js واحداً من أفضل الأطر المبنية فوق React لإنشاء مواقع سريعة وعملية وصديقة لمحركات البحث. ومن أبرز مزاياه دعمه لـServer-Side Rendering، وهي ميزة تساعد على تحسين الأرشفة وظهور المحتوى بشكل أوضح لمحركات البحث.

كما يوفر مزايا جاهزة مثل:

  • تحسين الصور.
  • إنشاء الصفحات والمسارات بسهولة.
  • أداء مرتفع في التحميل والعرض.
  • مرونة كبيرة بين العرض الثابت والديناميكي.

إطار التنسيق TailwindCSS

أما TailwindCSS فهو إطار CSS عملي يعتمد على أصناف جاهزة منخفضة المستوى يمكن تضمينها مباشرة داخل عناصر الواجهة. هذا الأسلوب يقلل الوقت المستغرق في كتابة التنسيقات التقليدية، ويسرّع بناء الواجهات بشكل ملحوظ.

من أهم مزاياه:

  • السرعة في بناء النماذج الأولية والواجهات الإنتاجية.
  • سهولة دعم التصميم المتجاوب.
  • إمكانية تخصيص الألوان والمسافات والخطوط.
  • تقليل الحاجة إلى ملفات CSS كبيرة ومعقدة.

على سبيل المثال، الكود <div className="text-sm md:text-xl"></div> يعني أن حجم النص سيكون صغيراً على الشاشات الصغيرة، وأكبر على الشاشات المتوسطة وما فوق.

مكتبة Rough Notation

استُخدمت مكتبة react-rough-notation لإبراز بعض النصوص في القسم التعريفي الأول Hero Section. هذا النوع من التأثيرات البصرية يلفت انتباه الزائر بسرعة إلى أهم الكلمات أو الأوصاف التي تريد ترسيخها.

النشر عبر Vercel

تم اختيار Vercel لكونه من أفضل الحلول لنشر تطبيقات Next.js. إذ يوفر تجربة نشر سهلة، ويدعم التكامل المستمر CI/CD، بحيث يتم تحديث الموقع تلقائياً عند كل عملية push إلى المستودع.

أهم مزايا موقع معرض الأعمال

عرض متحرك لموقع معرض أعمال احترافي متجاوب مع الوضع الليلي

هذا النوع من المواقع لا يحتاج إلى تعقيد مبالغ فيه، لكنه يحتاج إلى أساس قوي وخصائص عملية. ومن أبرز الميزات التي ينبغي أن تتوفر فيه:

  • الوضع الليلي: دعم التبديل بين النمط الفاتح والداكن.
  • بنية مبنية على Next.js: للاستفادة من السرعة وتحسين السيو.
  • تنسيق قابل للتخصيص: بفضل TailwindCSS يمكن تعديل الألوان والهوية البصرية بسهولة.
  • مكوّن مخصص للبيانات الوصفية: لتخصيص وسوم meta لكل صفحة.
  • تصميم متجاوب: ليظهر الموقع بشكل ممتاز على الحاسوب والجهاز اللوحي والهاتف.

الصفحات الأساسية التي يجب تضمينها

أي موقع معرض أعمال احترافي يجب أن يتضمن مجموعة صفحات أساسية توصل الرسالة بسرعة ووضوح:

  • الصفحة الرئيسية: نقطة الدخول الأولى للزائر.
  • من أنا: نبذة تعريفية ومهارات وروابط اجتماعية.
  • الخبرات: سجل الخبرة المهنية والمشاريع ذات الصلة.
  • المشاريع: عرض مرئي لأبرز الأعمال المنجزة.
  • التواصل: نموذج أو وسائل اتصال مباشرة.

هيكلة المشروع والمجلدات

من نقاط قوة Next.js سهولة تنظيم الملفات والمكوّنات. ويمكن اعتماد بنية بسيطة وواضحة كما يلي:

  • components: يحتوي على المكوّنات القابلة لإعادة الاستخدام مثل شريط التنقل والقسم التعريفي والتذييل.
  • public: للملفات الثابتة مثل الصور والخطوط والملفات العامة.
  • styles: للتنسيقات العامة ودمج مكتبة TailwindCSS.
  • pages: يضم جميع صفحات الموقع، وكل ملف بداخله يمثل مساراً تلقائياً.

هذه البنية تسهّل التطوير والصيانة والتوسع لاحقاً.

مكوّن الحاوية الرئيسي ContainerBlock

يُعد المكوّن <ContainerBlock /> الأب الذي يغلّف بقية أجزاء الصفحات. ووظيفته لا تقتصر على تنسيق التخطيط العام، بل يوفّر أيضاً إدارة مركزية للبيانات الوصفية مثل العنوان والوصف والصورة ووسوم المشاركة الاجتماعية.

هذا الأسلوب مفيد جداً من ناحية السيو، لأنه يسمح بتخصيص عنوان ووصف مختلفين لكل صفحة بدلاً من الاكتفاء بإعدادات عامة للموقع بأكمله.

import React from "react";
import Head from "next/head";
import { useRouter } from "next/router";
import Navbar from "./Navbar";
import Footer from "./Footer";

export default function ContainerBlock({ children, ...customMeta }) {
  const router = useRouter();
  const meta = {
    title: "Manu Arora - Developer, Writer, Creator and YouTuber",
    description: `I've been developing websites for 5 years straight. Get in touch with me to know more.`,
    image: "/avatar.png",
    type: "website",
    ...customMeta,
  };

  return (
    <div>
      <Head>
        <title>{meta.title}</title>
        <meta name="robots" content="follow, index" />
        <meta content={meta.description} name="description" />
        <meta property="og:url" content={`https://yourwebsite.com${router.asPath}`} />
        <link rel="canonical" href={`https://yourwebsite.com${router.asPath}`} />
        <meta property="og:type" content={meta.type} />
        <meta property="og:site_name" content="Manu Arora" />
        <meta property="og:description" content={meta.description} />
        <meta property="og:title" content={meta.title} />
        <meta property="og:image" content={meta.image} />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:site" content="@mannupaaji" />
        <meta name="twitter:title" content={meta.title} />
        <meta name="twitter:description" content={meta.description} />
        <meta name="twitter:image" content={meta.image} />
        {meta.date && (
          <meta property="article:published_time" content={meta.date} />
        )}
      </Head>

      <main className="dark:bg-gray-800 w-full">
        <Navbar />
        <div>{children}</div>
        <Footer />
      </main>
    </div>
  );
}

وبعد إنشاء ملف ContainerBlock.js يمكنك استخدامه لتغليف أي صفحة وتمرير بياناتها الوصفية بسهولة:

import Head from "next/head";
import styles from "../styles/Home.module.css";
import ContainerBlock from "../components/ContainerBlock";
import FavouriteProjects from "../components/FavouriteProjects";
import LatestCode from "../components/LatestCode";
import Hero from "../components/Hero";

export default function Home() {
  return (
    <ContainerBlock
      title="Manu Arora - Developer, Writer, Creator"
      description="Building a template with Next.js and Tailwindcss - for FreeCodeCamp users."
    >
      <Hero />
      <FavouriteProjects />
      <LatestCode />
    </ContainerBlock>
  );
}

كيفية تفعيل الوضع الليلي Dark Mode

واجهة موقع تدعم الوضع الليلي باستخدام next-themes

دعم الوضع الليلي أصبح من الخصائص المتوقعة في أغلب المواقع الحديثة. ولتنفيذه هنا تم الاعتماد على الحزمة next-themes، والتي توفر آلية بسيطة لإدارة السمة الحالية وتمريرها إلى جميع الأبناء عبر ThemeProvider.

import "../styles/globals.css";
import { ThemeProvider } from "next-themes";

function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider defaultTheme="light" attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

export default MyApp;

زر التبديل بين الوضع الفاتح والداكن داخل شريط التنقل

ثم نضيف زر التبديل داخل مكوّن Navbar حتى يبقى متاحاً في جميع الصفحات:

import React, { useEffect, useState } from "react";
import Link from "next/link";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";

export default function Navbar() {
  const router = useRouter();
  console.log(router.asPath);
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  return (
    <div className="max-w-6xl mx-auto px-4 py-10 md:py-20">
      <div className="flex md:flex-row justify-between items-center">
        <button
          aria-label="Toggle Dark Mode"
          type="button"
          className="w-10 h-10 p-3 rounded focus:outline-none"
          onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
        >
          {mounted && (
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              fill="currentColor"
              stroke="currentColor"
              className="w-4 h-4 text-yellow-500 dark:text-yellow-500"
            >
              {theme === "dark" ? (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
                />
              ) : (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
                />
              )}
            </svg>
          )}
        </button>
      </div>
    </div>
  );
}

هذه الإضافة صغيرة من ناحية التنفيذ، لكنها تعزز تجربة الاستخدام بشكل واضح، خاصة في الأجهزة المحمولة أو أثناء التصفح الليلي.

بناء قسم البداية Hero Section

القسم التعريفي الأول في موقع معرض الأعمال مع إبراز النصوص

القسم الافتتاحي هو أول ما يراه الزائر، ولذلك يجب أن يجيب بسرعة عن سؤالين أساسيين: من أنت؟ وماذا تقدم؟

تم استخدام مكتبة react-rough-notation لإضافة إبراز حركي لبعض الكلمات المفتاحية. ويمكن إنشاء مكوّن مخصص باسم RainbowHighlight ليكون قابلاً لإعادة الاستخدام في أي مكان داخل المشروع.

import React from "react";
import { RoughNotation } from "react-rough-notation";

export const RainbowHighlight = ({ color, children }) => {
  const animationDuration = Math.floor(30 * children.length);

  return (
    <RoughNotation
      type="highlight"
      multiline={true}
      padding={[0, 2]}
      iterations={1}
      animationDuration={animationDuration}
      color={color}
    >
      {children}
    </RoughNotation>
  );
};
import React from "react";
import { RoughNotation, RoughNotationGroup } from "react-rough-notation";
import { RainbowHighlight } from "./RainbowHighlight";

export default function Hero() {
  const colors = ["#F59E0B", "#84CC16", "#10B981", "#3B82F6"];

  return (
    <div className="flex flex-row justify-center items-start overflow-hidden">
      <div className="w-full md:w-1/2 mx-auto text-center md:text-left lg:p-20">
        <RoughNotationGroup show={true}>
          <RainbowHighlight color={colors[0]}>
            <h1 className="text-4xl md:text-8xl font-bold text-gray-700 dark:text-gray-200 my-2">
              Developer.
            </h1>
          </RainbowHighlight>
        </RoughNotationGroup>
      </div>
    </div>
  );
}

يفضل أن يحتوي هذا القسم على:

  • اسمك أو صفتك المهنية بوضوح.
  • وصف مختصر لما تقدمه.
  • زر للانتقال إلى المشاريع أو صفحة التواصل.
  • صورة شخصية أو رسم تعبيري يعكس هويتك المهنية.

جلب أحدث المستودعات من GitHub

عرض أحدث مستودعات GitHub داخل موقع معرض الأعمال

إضافة آخر المشاريع البرمجية من GitHub تمنح الموقع حداثة مستمرة وتربطه مباشرة بنشاطك التقني الحقيقي. ويمكن تنفيذ ذلك عبر GitHub API بسهولة.

الطلب التالي يجلب المستودعات مع الاعتماد على الترتيب بحسب تاريخ التحديث:

const res = await axios.get(
  `https://api.github.com/search/repositories?q=user:${username}+sort:author-date-asc`
);

وبعد وصول البيانات، يمكن الاكتفاء بآخر 6 مستودعات فقط باستخدام splice():

let repos = res.data.items;
let latestSixRepos = repos.splice(0, 6);
return latestSixRepos;

وهذا هو شكل الدالة الكامل:

import axios from "axios";

const getLatestRepos = async (data) => {
  console.log("data", data);
  try {
    const username = data.githubUsername;
    const res = await axios.get(
      `https://api.github.com/search/repositories?q=user:${username}+sort:author-date-asc`
    );
    let repos = res.data.items;
    let latestSixRepos = repos.splice(0, 6);
    return latestSixRepos;
  } catch (err) {
    console.log(err);
  }
};

export default getLatestRepos;

ثم يمكن استخدام النتائج داخل مكوّن لعرض البطاقات:

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto px-10 lg:-mt-10 gap-y-20">
  {repos && repos.map((latestRepo, idx) => (
    <GithubRepoCard latestRepo={latestRepo} key={idx} />
  ))}
</div>
const GithubRepoCard = ({ latestRepo }) => {
  return (
    <div className="github-repo">
      <h1 className="font-semibold text-xl dark:text-gray-200 text-gray-700">
        {latestRepo.name}
      </h1>
      <p className="text-base font-normal my-4 text-gray-500">
        {latestRepo.description}
      </p>
      <a
        href={latestRepo.clone_url}
        className="font-semibold group flex flex-row space-x-2 w-full items-center"
      >
        <p>View Repository</p>
        <div className="transform group-hover:translate-x-2 transition duration-300">
          →
        </div>
      </a>
    </div>
  );
};

لكن هناك نقطة مهمة: يفرض GitHub API حداً معيناً لعدد الطلبات لكل عنوان IP. وإذا كنت تتوقع عدداً أكبر من الطلبات، فمن الأفضل إنشاء تطبيق على GitHub واستخدام Auth Tokens لرفع هذا الحد وتقليل احتمالات فشل الاستدعاءات.

إضافة صفحة المشاريع بشكل احترافي

تصميم شبكة مشاريع داخل موقع بورتفوليو باستخدام TailwindCSS

قسم المشاريع هو غالباً أكثر جزء يهتم به مسؤول التوظيف، لأنه يجيب عملياً عن سؤال: ماذا أنجزت؟ لذلك يفضل أن يكون بصرياً، بسيطاً، وسهل التصفح.

في هذا المثال، تم تقسيم المشاريع ضمن شبكة grid من عمودين على الشاشات الكبيرة، وتتحول تلقائياً إلى عمود واحد على الهواتف. كما أن تأثير hover على الصور يضيف حيوية خفيفة دون إزعاج المستخدم.

import React from "react";

export default function Projects() {
  return (
    <section className="bg-white dark:bg-gray-800">
      <div className="max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800">
        <h1 className="text-5xl md:text-9xl font-bold py-20 text-center md:text-left">
          Projects
        </h1>
      </div>

      <div className="bg-[#F1F1F1] dark:bg-gray-900">
        <div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 py-20 pb-40">
          <a href="https://tailwindmasterkit.com" className="w-full block shadow-2xl">
            <div className="relative overflow-hidden">
              <img
                src="/tmk.jpg"
                alt="portfolio"
                className="transform hover:scale-125 transition duration-2000 ease-out"
              />
              <h1 className="absolute top-10 left-10 text-gray-50 font-bold text-xl bg-red-500 rounded-md px-2">
                Tailwind Master Kit
              </h1>
              <h1 className="absolute bottom-10 left-10 text-gray-50 font-bold text-xl">
                01
              </h1>
            </div>
          </a>
        </div>
      </div>
    </section>
  );
}

لتحسين هذا القسم فعلياً، احرص على أن يتضمن كل مشروع:

  • صورة واضحة أو لقطة شاشة حقيقية.
  • اسم المشروع.
  • وصف قصير يوضح المشكلة والحل.
  • رابط إلى النسخة الحية أو المستودع.
  • ذكر التقنيات المستخدمة مثل Next.js أو React أو Node.js.

بناء صفحة التواصل

نموذج تواصل احترافي داخل موقع معرض أعمال

صفحة التواصل يجب أن تكون مباشرة وواضحة، لأن الغاية منها تحويل الزائر المهتم إلى فرصة حقيقية. ويُستحسن أن تحتوي على أكثر من وسيلة تواصل، مثل البريد الإلكتروني، وروابط الحسابات المهنية، ونموذج بسيط وسريع.

في المثال التالي تم استخدام مكوّن جاهز مبني على TailwindCSS لتسريع العمل:

import React from "react";

export default function Contact() {
  return (
    <section>
      <div className="max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800 antialiased">
        <h1 className="text-5xl md:text-9xl font-bold py-20 text-center md:text-left">
          Contact
        </h1>
      </div>

      <div className="relative z-10 rounded-md shadow-md bg-[#02044A] p-4 md:p-10 lg:p-20 max-w-6xl mx-auto mb-20 -mt-4">
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <div className="md:ml-4">
            <header>
              <h1 className="text-gray-50 font-semibold text-2xl">
                Get in touch, let's talk.
              </h1>
              <p className="font-light text-base text-gray-200 mt-2">
                Fill in the details and I'll get back to you as soon as I can.
              </p>
            </header>

            <form className="form rounded-lg bg-white p-4 flex flex-col">
              <label htmlFor="name" className="text-sm text-gray-600 mx-4">
                Your Name
              </label>
              <input
                type="text"
                className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
                name="name"
              />

              <label htmlFor="email" className="text-sm text-gray-600 mx-4 mt-4">
                Email
              </label>
              <input
                type="text"
                className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
                name="email"
              />

              <button
                type="submit"
                className="bg-blue-500 rounded-md w-1/2 mx-4 mt-8 py-2 text-gray-50 text-xs font-bold"
              >
                Send Message
              </button>
            </form>
          </div>
        </div>
      </div>
    </section>
  );
}

من الناحية العملية، يفضل ربط النموذج لاحقاً بخدمة إرسال رسائل مثل EmailJS أو واجهة API مخصصة، مع التحقق من المدخلات وإظهار رسائل نجاح أو فشل واضحة للمستخدم.

خطوات نشر الموقع على Vercel

نشر المشروع من أسهل المراحل عند استخدام Next.js مع Vercel. ويمكن اختصار الخطوات كالتالي:

  1. استنساخ المستودع:
git clone https://github.com/manuarora700/simple-developer-portfolio-website
  1. تثبيت الاعتماديات:
npm install
  1. تشغيل بيئة التطوير المحلية:
npm run dev
  1. تخصيص المحتوى بإضافة مشاريعك، وروابطك، ومعلوماتك التعليمية، والبيانات الوصفية الخاصة بك.
  2. رفع التعديلات إلى مستودعك البعيد:
git add *
git commit -m "add changes to the cloned repo"
git push
  1. إنشاء حساب على Vercel أو تسجيل الدخول.
  2. ربط مستودع GitHub الجديد بلوحة Vercel.
  3. سيتم نشر المشروع تلقائياً ومنحك رابطاً جاهزاً للمعاينة والمشاركة.

لوحة نشر مشروع Next.js على منصة Vercel

بعد ذلك يمكنك مشاركة الرابط مع الأصدقاء أو إضافته إلى سيرتك الذاتية أو ربطه بنطاق مخصص ليظهر بصورة أكثر مهنية.

نصائح مهمة لتحسين السيو والقبول في AdSense

إذا كان هدفك إنشاء موقع معرض أعمال احترافي وقابل لتحقيق الدخل أو القبول في Google AdSense، فهناك مجموعة ممارسات مهمة يجب الالتزام بها:

  • اكتب محتوى أصلياً يشرح مشاريعك وخبراتك بلغة بشرية واضحة.
  • أنشئ صفحات حقيقية مثل من نحن، سياسة الخصوصية، تواصل معنا، والمشاريع.
  • استخدم عناوين واضحة ومنظمة عبر <h2> و<h3>.
  • أضف أوصافاً دقيقة للصور عبر خاصية alt.
  • اجعل الموقع سريعاً ومتوافقاً مع الهواتف.
  • لا تملأ الصفحات بعناصر فارغة أو محتوى ضعيف القيمة.
  • خصص لكل صفحة عنواناً ووصفاً فريدين باستخدام وسوم meta.

كلما كان الموقع واضح الغرض، متماسك البنية، وغنياً بالمعلومات الفعلية، زادت فرص قبوله وثقة محركات البحث به.

الخلاصة التقنية

بناء موقع معرض أعمال باستخدام Next.js وTailwindCSS هو خيار ذكي لمن يريد موقعاً سريعاً، مرناً، وسهل التطوير. يجمع هذا النهج بين الأداء العالي، وسهولة تحسين السيو، والقدرة على إنشاء واجهات جذابة دون تعقيد كبير. والأهم من ذلك أن الموقع لا ينبغي أن يكون مجرد واجهة جميلة، بل منصة عملية تعرض خبرتك بوضوح، وتدعم صورتك المهنية، وتمنح الزائر سبباً حقيقياً للتواصل معك أو ترشيحك.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *