أساسيات الواجهة الأمامية
Flexbox: كل ما تحتاجه لترتيب العناصر في بُعد واحد
دليل بصري كامل لـ CSS Flexbox — المحوران، flex-direction والالتفاف وjustify-content وalign-items وgap وgrow/shrink/basis واختصار flex والهوامش التلقائية وفخ min-width:0 وسلوك RTL، مع تمارين عملية وحلولها.
بعد أن تتعلم كيف يُقاس الصندوق، يأتي السؤال التالي: كيف ترتّب صفا منها؟ الـ Flexbox أداة مصممة لهذا بالضبط: خذ مجموعة عناصر، صُفّها على محور واحد، ودعها تتقاسم المساحة المتبقية بدلا من أن تحسب العروض بيدك. التوسيط، أشرطة التنقل، أشرطة الأدوات، صفوف البطاقات، الأشرطة الجانبية — تقريبا كل مشكلة من نوع "ضع هذه بجانب بعضها واجعلها تتصرف بشكل جيد" هي مشكلة flexbox.
الـ Flexbox يجيب على سؤال واحد: عند وجود صف من العناصر ومساحة فارغة، كيف تُوزَّع هذه المساحة على طول الصف، وكيف تُحاذى العناصر عبره؟
بُعد واحد، محوران
الـ Flexbox يرتّب العناصر على بُعد واحد في كل مرة — صف أو عمود. هذا البُعد هو المحور الرئيسي (main axis)؛ والعمودي عليه هو المحور المتقاطع (cross axis). تقريبا كل خاصية في flex هي إما "افعل شيئا على المحور الرئيسي" أو "افعل شيئا على المحور المتقاطع"، فإتقان هذين المحورين هو معظم المعركة.
flex-direction: row، يمتد المحور الرئيسي أفقيا (مع اتجاه الكتابة) ويمتد المحور المتقاطع عموديا. justify-content يعمل على المحور الرئيسي، وalign-items يعمل على المحور المتقاطع.أهم نتيجة: المحوران يتبادلان عند تغيير الاتجاه. في العمود يمتد المحور الرئيسي من الأعلى للأسفل، فيتحكم justify-content الآن بالموضع العمودي ويتحكم align-items بالأفقي. احفظ الأدوار (justify = الرئيسي، align = المتقاطع)، لا "أفقي/عمودي".
التفعيل: display: flex
تفعّل الخاصية على الحاوية، لا على العناصر:
.container {
display: flex; /* حاوية flex على مستوى block */
/* display: inline-flex; تتدفق inline مثل inline-block */
}
في اللحظة التي يصبح فيها العنصر حاوية flex، تحدث ثلاثة أمور لـ أبنائه المباشرين (عناصر flex الآن):
- يصطفّون على المحور الرئيسي بدل أن يتكدّسوا.
- يجلسون على سطر واحد افتراضيا (بلا التفاف).
- يتمددون ليملؤوا المحور المتقاطع (أعمدة متساوية الارتفاع، مجانا).
الأبناء المباشرون فقط يصبحون عناصر flex — التأثير لا يصل إلى الأحفاد. كما تتوقف float وclear وvertical-align عن التأثير على عناصر flex.
flex-direction: اختيار المحور الرئيسي
.container {
flex-direction: row; /* الافتراضي — المحور الرئيسي باتجاه الكتابة */
flex-direction: row-reverse; /* نفس السطر، العناصر معكوسة */
flex-direction: column; /* المحور الرئيسي للأسفل */
flex-direction: column-reverse; /* المحور الرئيسي للأعلى */
}
row إلى column يدوّر المحور الرئيسي — ومعه أي خاصية (justify أم align) تتحكم بكل اتجاه.فضّل row/column ودع اتجاه كتابة الصفحة يتولى الانعكاس. لا تلجأ إلى -reverse إلا عندما تريد فعلا انعكاسا بصريا يجب ألا ينقلب في RTL — فهو يغيّر ترتيب الرسم فقط لا ترتيب DOM ولا ترتيب التنقل بـ Tab، وهو ما قد يضر بإمكانية الوصول عند الإفراط فيه.
الالتفاف: flex-wrap وflex-flow
افتراضيا لا ينكسر سطر flex أبدا — تتقلص العناصر لتتسع، وإن عجزت عن التقلص أكثر فإنها تفيض. تتيح flex-wrap أن تنتقل إلى أسطر جديدة بدلا من ذلك:
.container {
flex-wrap: nowrap; /* الافتراضي — سطر واحد، قد يفيض */
flex-wrap: wrap; /* الانكسار إلى أسطر جديدة حسب الحاجة */
flex-wrap: wrap-reverse; /* الالتفاف لكن بترتيب أسطر معكوس */
/* اختصار للاتجاه + الالتفاف: */
flex-flow: row wrap;
}
flex-wrap: wrap هو أساس شبكات البطاقات المتجاوبة: أعطِ كل بطاقة flex-basis (أو min-width) ودع الصف ينكسر طبيعيا كلما ضاقت الشاشة — بلا media queries لإعادة التدفق الأساسية.
justify-content: التوزيع على المحور الرئيسي
هكذا تُقتسم المساحة المتبقية على المحور الرئيسي:
space-between يثبّت العنصر الأول والأخير على الحافتين؛ space-around يعطي كل عنصر مساحة متساوية على جانبيه (فتحصل الحواف على نصف فجوة)؛ space-evenly يجعل كل الفجوات — بما فيها الحواف — متطابقة..container {
justify-content: flex-start; /* الافتراضي */
justify-content: center;
justify-content: flex-end;
justify-content: space-between;
justify-content: space-around;
justify-content: space-evenly;
}
align-items: المحاذاة على المحور المتقاطع
بينما يوزّع justify-content العناصر على طول السطر، يضع align-items العناصر عبره — أداة المحور المتقاطع لسطر واحد:
stretch هي الافتراضية — العناصر التي بلا قياس ثابت على المحور المتقاطع تتمدد لتملأ الحاوية، ولهذا تعطي صفوف flex أعمدة متساوية الارتفاع تلقائيا. أما البقية فتثبّت العناصر عند بداية المحور المتقاطع أو وسطه أو نهايته..container {
align-items: stretch; /* الافتراضي */
align-items: flex-start;
align-items: center;
align-items: flex-end;
align-items: baseline; /* محاذاة خطوط أساس النص */
}
baseline هي البطل الخفي لأشرطة الأدوات وصفوف النماذج: تحاذي النص على خط أساسه حتى لو اختلفت أحجام الخطوط أو الـ padding بين العناصر.
التوسيط، أخيرا
الجواب من سطرين عن "كيف أوسّط شيئا في الاتجاهين" هو ببساطة محاذاة على المحور الرئيسي + المتقاطع:
.center {
display: flex;
justify-content: center; /* المحور الرئيسي */
align-items: center; /* المحور المتقاطع */
}
align-content: محاذاة الأسطر المتعددة
هناك خاصية محاذاة ثالثة لا تفعل شيئا إلا عندما يلتف المحتوى على أسطر متعددة. align-items تحاذي العناصر داخل سطرها؛ أما align-content فتحاذي الأسطر نفسها داخل المحور المتقاطع للحاوية (flex-start، center، space-between، stretch، …). على حاوية بسطر واحد لا تأثير لها — وهذا مصدر شائع لسؤال "لماذا لا تفعل هذه الخاصية شيئا؟".
gap: مسافات بلا متاعب الـ margin
.container {
gap: 16px; /* فجوات الصفوف والأعمدة معا */
gap: 12px 24px; /* row-gap | column-gap */
column-gap: 24px;
row-gap: 12px;
}
تضيف gap مسافة بين العناصر فقط — لا على الحواف الخارجية أبدا — وعلى عكس الـ margin لا تنهار أبدا ولا تحتاج حيل :not(:last-child). لتوزيع المسافات بين أبناء flex (وgrid)، صارت gap الخيار الافتراضي الحديث؛ ولا تلجأ إلى الـ margin إلا عند الحاجة لمسافة على جهة واحدة محددة.
تحجيم العناصر: flex-grow وflex-shrink وflex-basis
هذا هو قلب الـ flexbox — كيف تتغير أحجام العناصر لتستهلك المساحة أو تتنازل عنها. ثلاث خصائص تعيش على العناصر:
flex-basis— القياس الابتدائي للعنصر على المحور الرئيسي قبل توزيع المساحة الفارغة. القيمةauto(الافتراضية) تعني "استخدمwidth/heightأو حجم المحتوى"؛ وقيمة كـ200pxأو0تضبطه صراحة.flex-grow— مدى نهم العنصر في امتصاص المساحة المتبقية. القيمة0(الافتراضية) = لا تنمُ. والأرقام الأكبر تأخذ حصصا أكبر تناسبيا.flex-shrink— مدى استعداد العنصر للتنازل عن المساحة عند فيض السطر. القيمة1(الافتراضية) = تقلّص حسب الحاجة. والقيمة0= ارفض التقلص.
flex: 1 والبقية بـ flex: 0 0 auto، يمتص العنصر المرن كل ما تبقى بعد وضع العناصر الثابتة.اختصار flex (استخدم هذا، لا الخصائص المنفصلة)
ستضبط الثلاث معا غالبا عبر اختصار flex. وهذه القيم التي تستخدمها فعليا كل يوم:
| الاختصار | يتوسع إلى | المعنى |
|---|---|---|
flex: 1 | 1 1 0% | ينمو ويتقلص من أساس صفري → أعمدة مرنة متساوية |
flex: auto | 1 1 auto | ينمو ويتقلص من حجم المحتوى → مرن لكنه يراعي المحتوى |
flex: none | 0 0 auto | حجم ثابت، لا ينمو ولا يتقلص |
flex: 0 1 auto | (الافتراضي) | لا ينمو، يتقلص عند الحاجة — السلوك الابتدائي |
flex: 0 0 240px | 0 0 240px | عمود صارم بعرض 240px (ممتاز للأشرطة الجانبية) |
الفخ الكبير هنا: flex: 1 يستخدم أساسا صفريا، فثلاثة عناصر بـ flex: 1 تصبح متساوية تماما بغض النظر عن المحتوى. أما flex: auto فيستخدم المحتوى كأساس، فيبدأ العنصر الأطول أعرض ويبقى أعرض قليلا. اختر flex: 1 حين تريد أعمدة متساوية تماما، وflex: auto حين يجب أن يؤثر المحتوى في التقسيم.
تجاوزات لكل عنصر: align-self وorder
.item {
align-self: center; /* تجاوز align-items للحاوية لهذا العنصر فقط */
order: -1; /* انقل هذا العنصر مبكرا؛ الافتراضي 0، الأقل يُرسم أولا */
}
align-self مثالية لتعديل عنصر واحد (مثلا صورة رمزية يجب أن تتوسط بينما يتمدد إخوتها). وorder يعيد ترتيب التسلسل البصري دون لمس HTML — مفيد، لكن تذكّر أنه لا يغيّر ترتيب القراءة ولا التنقل بـ Tab، فلا تستخدمه لإصلاح مشاكل ترتيب المصدر المهمة لإمكانية الوصول.
الهوامش التلقائية تلتهم المساحة الفارغة
حيلة خاصة بـ flex تستحق الحفظ: الهامش auto على عنصر flex يمتص كل المساحة الفارغة على تلك الجهة. فـ margin-inline-start: auto على العنصر الأخير يدفعه (وكل ما بعده) إلى أقصى النهاية — أنظف طريقة لبناء شريط "الشعار في جهة والأزرار في الأخرى":
.navbar { display: flex; gap: 16px; align-items: center; }
.navbar .spacer-target { margin-inline-start: auto; } /* يُدفع إلى النهاية */
والهوامش التلقائية تُوسّط أيضا: margin: auto على عنصر flex يوسّطه في كلا المحورين (نعم، عموديا أيضا — ما لم يستطع تخطيط الـ block العادي فعله بنظافة).
فخ min-width: 0
افتراضيا لن يتقلص عنصر flex تحت الحجم الأدنى لمحتواه (min-width: auto). غالبا غير مرئي — حتى يحمل العنصر سلسلة نصية طويلة بلا فواصل، أو <pre>، أو ابنا فيه overflow، فيرفض التقلص ويفجّر التخطيط أو يفرض شريط تمرير. الحل أن تسمح له بالتقلص تحت حجم المحتوى:
.flex-child {
min-width: 0; /* اسمح بالتقلص تحت حجم المحتوى (صف) */
/* min-height: 0; المكافئ لاتجاه العمود */
}
/* الكلاسيكي: اقتطاع النص داخل ابن flex */
.label {
flex: 1;
min-width: 0; /* بدونها لن تعمل النقاط الثلاث أبدا */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
إذا كانت النقاط الثلاث (ellipsis) "لا تعمل" داخل flexbox، فهذا هو السبب دائما تقريبا.
الـ Flexbox والاتجاه (RTL)
الـ Flexbox مدرك للاتجاه بحكم تصميمه، وهذه ميزة. مع flex-direction: row في صفحة RTL، يمتد المحور الرئيسي من اليمين لليسار تلقائيا: فـ justify-content: flex-start تعني الحافة اليمنى، وتتدفق العناصر يمينا، وينعكس شريط التنقل بشكل صحيح بلا أي كود إضافي. ولهذا بالضبط يجب أن تفضّل الأدوات المنطقية المرتبطة بالاتجاه:
- أبقِ
flex-direction: row/columnودعdir="rtl"يقلب المحور الرئيسي لك — وتجنّبrow-reverseالمثبّت لـ "الوضع العربي". - استخدم
margin-inline-start/margin-inline-end(لاleft/right) لحيلة دفع الهامش التلقائي، لتنقلب مع الاتجاه. gapمحايدة تجاه الاتجاه ولا تحتاج شيئا خاصا.
لموقع يُطلق في LTR وRTL معا، يحذف الاعتماد على إدراك flex الأصيل للاتجاه فئة كاملة من أخطاء الانعكاس.
متى flexbox ومتى grid؟
قاعدة سريعة: الـ Flexbox أحادي البُعد (المحتوى يتدفق على سطر واحد، والالتفاف أثر جانبي)، والـ Grid ثنائي البُعد (تضع العناصر في صفوف وأعمدة معا). الجأ إلى flexbox لأشرطة الأدوات وصفوف الأزرار وأشرطة التنقل والتوسيط و"دع هذه تتقاسم سطرا"؛ والجأ إلى grid حين تعرّف بنية صفوف وأعمدة فعلية. وهما يتكاملان بسعادة — يمكن أن تكون خلية grid حاوية flex والعكس.
أخطاء شائعة
- ضبط خصائص flex على العناصر بينما مكانها الحاوية (
justify-contentوalign-itemsوflex-wrapخصائص حاوية). - الخلط بين المحاور بعد التحويل إلى
column— صارjustify-contentعموديا وalign-itemsأفقيا. - توقّع أن تفعل
align-contentشيئا على حاوية بسطر واحد (بلا التفاف). - نسيان
min-width: 0، ثم التساؤل لماذا تكسر سلسلة طويلة أو النقاط الثلاث التخطيط. - استخدام
flex: autoوالاندهاش لأن الأعمدة ليست متساوية تماما — استخدمflex: 1للأعمدة المتساوية. - اللجوء إلى
marginبين العناصر بينماgapأنظف وبلا انهيار. - استخدام
row-reverse/الهوامش الفيزيائية لـ RTL بدل تركdirوالخصائص المنطقية تقلب الأمور.
تمارين
طبّق هذه في ملف HTML تجريبي (أو أي بيئة لعب). جرّب كل تمرين قبل فتح الحل. كل نقطة بداية تستخدم هذا الترميز البسيط ما لم يُذكر غير ذلك:
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
</div>
تمرين 1 — التوسيط المثالي
وسّط ابنا واحدا أفقيا وعموديا داخل صندوق 300×200.
<div class="frame">
<div class="badge">Centered</div>
</div>
اعرض الحل
.frame {
width: 300px;
height: 200px;
display: flex;
justify-content: center; /* المحور الرئيسي: أفقي */
align-items: center; /* المحور المتقاطع: عمودي */
}
سطرا محاذاة — بلا transforms ولا أرقام سحرية.
تمرين 2 — شريط تنقل بأزرار مدفوعة للنهاية
ابنِ شريطا بشعار في جهة البداية ورابطين مدفوعين إلى النهاية، متوسطين عموديا. ويجب أن ينعكس صحيحا في RTL.
<nav class="navbar">
<span class="logo">Logo</span>
<a href="#">Docs</a>
<a href="#">Login</a>
</nav>
اعرض الحل
.navbar {
display: flex;
align-items: center;
gap: 16px;
}
.navbar a:first-of-type {
margin-inline-start: auto; /* يمتص المساحة الفارغة، يدفع الروابط للنهاية */
}
الهامش auto على الرابط الأول يمتص كل المساحة المتبقية فيدفعه وما بعده إلى أقصى النهاية. ولأنه inline-start (منطقي)، ينقلب تلقائيا في RTL.
تمرين 3 — صف بطاقات متجاوب يلتف
رتّب بطاقات في صف يلتف إلى أسطر جديدة كلما ضاقت الشاشة، كل بطاقة بعرض 220px على الأقل وتنمو لتتقاسم المساحة المتبقية. بلا media queries.
<div class="cards">
<article>Card A</article>
<article>Card B</article>
<article>Card C</article>
<article>Card D</article>
</div>
اعرض الحل
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.cards article {
flex: 1 1 220px; /* ينمو ويتقلص، أساس مثالي 220px */
}
تريد كل بطاقة 220px؛ والمساحة الزائدة تُقتسم عبر flex-grow: 1؛ وعندما يعجز الصف عن استيعاب بطاقة 220px أخرى، يُنزلها flex-wrap إلى السطر التالي. سائل تماما بلا أي نقاط توقف.
تمرين 4 — كائن وسائط بنص يُقتطع
صورة رمزية ثابتة 48px في جهة البداية، ثم عنوان يملأ الباقي ويُقتطع بنقاط ثلاث حين يطول.
<div class="media">
<img class="avatar" src="..." alt="" />
<p class="title">A very long title that should be cut off with an ellipsis…</p>
</div>
اعرض الحل
.media {
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
flex: 0 0 48px; /* صارم: لا ينمو ولا يتقلص */
width: 48px;
height: 48px;
}
.title {
flex: 1;
min-width: 0; /* الحل — اسمح للنص بالتقلص تحت حجم محتواه */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
بدون min-width: 0 يرفض العنوان التقلص تحت عرض نصه فلا تظهر النقاط الثلاث أبدا — أكثر مفاجآت flexbox شيوعا.
تمرين 5 — تذييل ملتصق بالأسفل
اجعل التذييل يجلس أسفل نافذة العرض حين يكون المحتوى قصيرا، لكنه يُدفع للأسفل طبيعيا حين يطول المحتوى.
<body class="layout">
<header>Header</header>
<main>Content</main>
<footer>Footer</footer>
</body>
اعرض الحل
.layout {
min-height: 100vh; /* أو 100dvh */
display: flex;
flex-direction: column; /* المحور الرئيسي للأسفل */
}
.layout main {
flex: 1; /* ينمو ليمتص كل المساحة العمودية المتبقية */
}
في حاوية flex عمودية، يجعل flex: 1 على main يلتهم الارتفاع المتبقي، فيثبّت التذييل أسفل الشاشة حين يكون المحتوى قصيرا ويتركه يتدفق للأسفل حين يطول.
تمرين 6 — شريط جانبي + محتوى سائل
شريط جانبي ثابت 240px بجانب منطقة محتوى تملأ الباقي. ويتكدّسان على الشاشات الضيقة.
<div class="shell">
<aside>Sidebar</aside>
<section>Main content</section>
</div>
اعرض الحل
.shell {
display: flex;
flex-wrap: wrap;
gap: 24px;
}
.shell aside {
flex: 0 0 240px; /* عمود صارم 240px */
}
.shell section {
flex: 1 1 320px; /* يملأ المساحة المتبقية؛ أساسه 320px يفرض الالتفاف-والتكدّس عند الضيق */
}
على الشاشات العريضة يتقاسم الشريط الصارم والمحتوى المرن الصف؛ وحين لا يعود أساس المحتوى 320px يتسع بجانب الشريط 240px، يكدّسهما flex-wrap. تخطيط شبيه بـ grid بإعلان واحد لكل عنصر وبلا نقاط توقف.
النموذج الذهني الذي تحتفظ به
حاوية flex تصفّ عناصرها على محور رئيسي، والمحور المتقاطع عمودي عليه. justify-content يوزّع على المحور الرئيسي؛ وalign-items يحاذي عبر المحور المتقاطع؛ والتحويل إلى column يبدّل أيهما أيّ. تتغير أحجام العناصر عبر flex: grow shrink basis — الجأ إلى flex: 1 (متساوٍ) أو flex: auto (يراعي المحتوى) أو flex: none (صارم). استخدم gap للمسافات، وmin-width: 0 لترويض الأبناء العنيدين، والهوامش التلقائية لإبعاد العناصر، واعتمد على إدراك الاتجاه الأصيل ليعمل RTL وحده. أتقن هذه، وسيتوقف التخطيط أحادي البُعد عن كونه معركة.