مرحباً بكم في درس جديد! اليوم، سنتعلم كيفية تصميم هيكل ثابت (شريط تنقل وتذييل) لجميع صفحات تطبيق Next.js باستخدام ميزة التخطيطات (Layouts) الجديدة في App Router، مما يضمن تجربة مستخدم موحدة.
الخطوة 1: فهم مكون التخطيط (layout.tsx)
في Next.js App Router، يُعد ملف layout.tsx حجر الزاوية لتحديد واجهة المستخدم المشتركة عبر مجموعة من الصفحات. إنه يلتف حول مكونات الصفحة الفرعية (children) ويسمح لنا بتطبيق عناصر تصميم متسقة مثل شريط التنقل والتذييل.
ملاحظة تقنية: ملف
layout.tsxيجب أن يُصدر مكون React افتراضي يقبل خاصيةchildren، والتي ستحتوي على محتوى الصفحة الحالية أو التخطيطات المتداخلة.
لنبدأ بإنشاء ملف layout.tsx أساسي في مجلد app/:
// app/layout.tsx
import './globals.css'; // استيراد ملف الأنماط العامة
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar"> {/* تحديد لغة المستند */}
<body> {/* جسم الصفحة الرئيسي */}
{children} {/* هنا سيتم عرض محتوى الصفحة أو التخطيطات المتداخلة */}
</body>
</html>
);
}
في هذا الكود، قمنا بإنشاء تخطيط جذري بسيط. الخاصية children هي المكان الذي سيتم فيه عرض محتوى أي صفحة (مثل page.tsx) موجودة داخل نفس المسار أو مسار فرعي.
الخطوة 2: إنشاء مكونات شريط التنقل والتذييل
للحفاظ على الكود نظيفاً وقابلاً لإعادة الاستخدام، سنقوم بإنشاء مكونات منفصلة لشريط التنقل (Navbar) والتذييل (Footer). هذه المكونات ستكون بسيطة في البداية.
أنشئ مجلداً جديداً باسم components داخل app/، ثم أنشئ الملفين التاليين:
app/components/Navbar.tsx:
// app/components/Navbar.tsx
import Link from 'next/link'; // استخدام Link لتحسين التنقل
export default function Navbar() {
return (
<nav className="navbar"> {/* شريط التنقل */}
<h1>تطبيقي الخاص</h1> {/* عنوان التطبيق */}
<ul>
<li><Link href="/">الرئيسية</Link></li> {/* رابط الصفحة الرئيسية */}
<li><Link href="/about">عنا</Link></li> {/* رابط صفحة "عنا" (مثال) */}
</ul>
</nav>
);
}
app/components/Footer.tsx:
// app/components/Footer.tsx
export default function Footer() {
return (
<footer className="footer"> {/* قسم التذييل */}
<p>© 2023 تطبيقي الخاص. جميع الحقوق محفوظة.</p> {/* حقوق النشر */}
</footer>
);
}
لقد أنشأنا الآن مكونين أساسيين سيشكلان الأجزاء الثابتة من هيكل صفحتنا. لاحظ استخدام className، والتي سنستخدمها لاحقاً لتطبيق الأنماط.
الخطوة 3: دمج شريط التنقل والتذييل في layout.tsx
الآن بعد أن أصبح لدينا مكوناتنا، حان الوقت لدمجها في layout.tsx لضمان ظهورها في كل صفحة تستخدم هذا التخطيط.
قم بتعديل ملف app/layout.tsx ليكون كالتالي:
// app/layout.tsx
import './globals.css';
import Navbar from './components/Navbar'; // استيراد مكون شريط التنقل
import Footer from './components/Footer'; // استيراد مكون التذييل
export const metadata = { // بيانات تعريفية للموقع
title: 'تطبيق تخطيط ثابت',
description: 'مثال على تخطيط ثابت باستخدام Next.js App Router',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar">
<body>
<Navbar /> {/* عرض شريط التنقل في أعلى الصفحة */}
<main className="content-wrapper"> {/* غلاف لمحتوى الصفحة لسهولة التنسيق */}
{children} {/* هنا سيتم عرض محتوى الصفحة المتغير */}
</main>
<Footer /> {/* عرض التذييل في أسفل الصفحة */}
</body>
</html>
);
}
في هذا التحديث، قمنا باستيراد Navbar و Footer ووضعناهما خارج children ولكن داخل . هذا يضمن أن شريط التنقل والتذييل سيظهران في جميع الصفحات التي تستخدم هذا التخطيط، مع بقاء محتوى الصفحة (children) بينهما.
الخطوة 4: تنسيق التخطيط لهيكل ثابت
لجعل شريط التنقل والتذييل ثابتين (fixed) في مكانهما، سنحتاج إلى إضافة بعض الأنماط CSS. سنستخدم ملف app/globals.css الذي قمنا باستيراده مسبقاً.
قم بتعديل ملف app/globals.css ليتضمن الأنماط التالية:
/* app/globals.css */
html, body {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f4f7f6;
color: #333;
}
.navbar {
background-color: #282c34;
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
width: 100%;
z-index: 1000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.navbar h1 {
margin: 0;
font-size: 1.5rem;
}
.navbar ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
}
.navbar li {
margin-left: 1.5rem;
}
.navbar a {
color: white;
text-decoration: none;
font-weight: bold;
transition: color 0.3s ease;
}
.navbar a:hover {
color: #61dafb;
}
.content-wrapper {
flex-grow: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
background-color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
border-radius: 8px;
margin-top: 20px;
margin-bottom: 20px;
}
.footer {
background-color: #282c34;
color: #a0a0a0;
padding: 1rem 2rem;
text-align: center;
position: sticky;
bottom: 0;
width: 100%;
z-index: 1000;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
}
لقد استخدمنا position: sticky لشريط التنقل والتذييل لتحقيق تأثير الثبات عند التمرير. الـ flex-grow: 1 على .content-wrapper يضمن أن المحتوى سيشغل كل المساحة المتاحة بين شريط التنقل والتذييل، مما يدفع التذييل إلى أسفل الصفحة حتى لو كان المحتوى قصيراً.
الكود النهائي الكامل
هذا هو الكود الكامل لجميع الملفات التي قمنا بإنشائها أو تعديلها:
app/layout.tsx:
import './globals.css';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
export const metadata = { // بيانات تعريفية للموقع
title: 'تطبيق تخطيط ثابت',
description: 'مثال على تخطيط ثابت باستخدام Next.js App Router',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar">
<body>
<Navbar />
<main className="content-wrapper">
{children}
</main>
<Footer />
</body>
</html>
);
}
app/page.tsx (مثال على صفحة المحتوى):
// app/page.tsx
export default function HomePage() {
return (
<div>
<h2>مرحباً بك في الصفحة الرئيسية!</h2>
<p>هذا هو محتوى الصفحة الرئيسية. يمكنك إضافة المزيد من المحتوى هنا.</p>
<p>
للتأكد من أن التذييل يلتصق بالأسفل، دعنا نضيف بعض الفقرات الطويلة هنا.
هذا النص هو مجرد مثال لتعبئة المساحة واختبار وظيفة التذييل الثابت.
يمكنك استبدال هذا بأي محتوى تريده لصفحتك.
تذكر أن التخطيطات تساعد في الحفاظ على تجربة مستخدم متسقة.
<br/><br/>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<br/><br/>
Proin eget tortor risus. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Nulla porttitor accumsan tincidunt. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Pellentesque in ipsum id orci porta dapibus.
<br/><br/>
Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Donec rutrum congue leo eget malesuada. Cras ultricies ligula sed magna dictum porta. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Nulla porttitor accumsan tincidunt.
</p>
</div>
);
}
app/components/Navbar.tsx:
import Link from 'next/link'; // استخدام Link لتحسين التنقل
export default function Navbar() {
return (
<nav className="navbar">
<h1>تطبيقي الخاص</h1>
<ul>
<li><Link href="/">الرئيسية</Link></li>
<li><Link href="/about">عنا</Link></li>
</ul>
</nav>
);
}
app/components/Footer.tsx:
export default function Footer() {
return (
<footer className="footer">
<p>© 2023 تطبيقي الخاص. جميع الحقوق محفوظة.</p>
</footer>
);
}
app/globals.css:
/* app/globals.css */
html, body {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f4f7f6;
color: #333;
}
.navbar {
background-color: #282c34;
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
width: 100%;
z-index: 1000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.navbar h1 {
margin: 0;
font-size: 1.5rem;
}
.navbar ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
}
.navbar li {
margin-left: 1.5rem;
}
.navbar a {
color: white;
text-decoration: none;
font-weight: bold;
transition: color 0.3s ease;
}
.navbar a:hover {
color: #61dafb;
}
.content-wrapper {
flex-grow: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
background-color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
border-radius: 8px;
margin-top: 20px;
margin-bottom: 20px;
}
.footer {
background-color: #282c34;
color: #a0a0a0;
padding: 1rem 2rem;
text-align: center;
position: sticky;
bottom: 0;
width: 100%;
z-index: 1000;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
}
النتيجة المتوقعة
بعد تشغيل التطبيق، سترى صفحة ويب تحتوي على شريط تنقل ثابت في الأعلى (بلون داكن)، وتذييل ثابت في الأسفل (بلون داكن أيضاً)، ومحتوى الصفحة الرئيسية (HomePage) بينهما. عند تمرير الصفحة، سيبقى شريط التنقل والتذييل مرئيين في مكانهما، مما يوفر تجربة تصفح متسقة واحترافية عبر جميع صفحات تطبيقك.
هذا الهيكل الثابت هو أساس معظم تطبيقات الويب الحديثة، ويوفر نقطة انطلاق ممتازة لتطوير واجهة مستخدم غنية ومتجاوبة.