EduFlow
إتقان CSS الحديثة
⁦0%⁩

الوحدة 2 · تخطيط يصمد

Flexbox بالتفصيل

18 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

توقّع كيف يحدّد flexbox أحجام العناصر من flex-basis والنموّ والتقلّص، وعالج فخّ الفيضان الناتج عن min-width: auto، وابنِ صفوفًا تلتفّ دون media queries.

  1. 1كيف يقرّر flexbox الأحجام
  2. 2فخّ min-width: auto
  3. 3الدفع بالـ margins التلقائية
  4. 4الالتفاف دون media queries
  5. 5دورك الآن

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

صف الجلسة في جدول Waypoint ثلاثة أشياء على خط واحد: الوقت والعنوان وزر الحفظ. يبدو جيدًا إلى أن يجدول فريق المحتوى جلسة بعنوان «Shipping Accessible Design Systems Across Twelve Product Teams Without Losing Your Mind»، فينزلق زر الحفظ خارج حافة البطاقة. لا شيء في الـ CSS يذكر أيّ عرض، فمن أين جاء الفيضان؟

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

كيف يقرّر flexbox الأحجام

يحدّد flexbox أحجام العناصر على امتداد المحور الرئيسي على مرحلتين:

  1. الحجم المبدئي (hypothetical size). يبدأ كل عنصر من flex-basis الخاصة به. ومع القيمة الافتراضية auto يكون هذا هو width إن كان مضبوطًا، وإلا فعرض محتواه.
  2. توزيع الفرق. تقارن الحاوية مجموع الأسس (مع الفجوات) بحجمها هي. إن بقيت مساحة، تتقاسمها العناصر التي لها flex-grow بنسبة معاملات نموّها. وإن لم تكفِ المساحة، تتنازل العناصر التي لها flex-shrink عن جزء منها، بوزن هو معامل التقلّص مضروبًا في الأساس، فتتقلّص العناصر الكبيرة أكثر.
الصف العلوي: ثلاثة عناصر عند قيمة flex-basis الخاصة بها مع مساحة حرّة متبقية في حاوية عرضها 600 بكسل. الصف السفلي: المساحة الحرّة ذهبت إلى العنوان الذي له flex-grow بقيمة 1، بينما يحتفظ الوقت والزر بأساسهما. 1. البداية من flex-basis time title save مساحة حرّة 2. للعنوان flex-grow: 1، فيأخذ المساحة الحرّة time title save
تبدأ عناصر flex من أساسها، وتُقسَّم المساحة المتبقية بحسب معامل النموّ

الاختصار flex يضبط القيم الثلاث معًا، وكلماته المفتاحية تستحق الحفظ لأن سلوكها مختلف جدًا:

الاختصار يتوسّع إلى استخدمه لـ
flex: 1 1 1 0% أعمدة متساوية أيًّا كان المحتوى
flex: auto 1 1 auto الحفاظ على حجم المحتوى وتقاسم الفائض فقط
flex: none 0 0 auto عدم النموّ أو التقلّص أبدًا (الأيقونات والأزرار)
flex: 0 0 5rem أساس ثابت عمود بعرض محدّد بدقّة

فخّ min-width: auto

لنعد إلى العنوان الهارب. له white-space: nowrap، فعرض محتواه هو الجملة كاملة، ربما 700px. والصف عرضه 400px. التقلّص يُفترض أن يحلّ المشكلة، فلماذا لا يحلّها؟

لأن عناصر flex تأتي افتراضيًا بـ min-width: auto، وهي تعني في عنصر flex «لا أصغر من حجم محتواي الأدنى (min-content)». والتقلّص يتوقّف عند هذا الحدّ. العنوان غير القابل للكسر لا يستطيع التقلّص تحت عرضه الكامل، فيفيض ويجرّ الزر معه.

session-row.css
.session-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.session-row .time  { flex: 0 0 5rem; }
.session-row .title {
  flex: 1;
  min-width: 0;            /* allow shrinking below content size */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-row .save  { flex: none; }

min-width: 0 تزيل الحدّ الأدنى، وخصائص الفيضان الثلاث تحوّل النص المقصوص إلى علامة حذف (…) مرتّبة. والفخّ نفسه يظهر مع الروابط الطويلة وكتل الكود وحاويات flex أو grid المتداخلة، لذا هذا إصلاح ستكتبه مرات كثيرة.

الدفع بالـ margins التلقائية

رأس صفحة Waypoint يحمل الشعار ومبدّل الأيام ورابط «Get tickets» الذي مكانه الطرف البعيد. justify-content: space-between ستباعد بين الثلاثة كلها. أما الـ margin التلقائية فأدقّ:

CSS
.site-header { display: flex; align-items: center; gap: 1rem; }
.site-header .tickets { margin-inline-start: auto; }

الـ margins التلقائية على عناصر flex تبتلع كل المساحة الحرّة في جانبها، فينتقل الرابط إلى النهاية ويبقى كل ما قبله متراصًّا. إنها في flexbox بمثابة نابض يدفع ما بعده إلى الطرف.

الالتفاف دون media queries

شريط الفلاتر يحمل أربعة فلاتر للمسارات. على شاشة عريضة تتّسع في سطر واحد؛ وعلى الهاتف لا ينبغي أن تنضغط إلى شرائح نحيلة لا تُقرأ. الالتفاف مع أساس محدّد يعطيك ذلك دون أيّ breakpoint (نقطة تحوّل):

CSS
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.filters > * {
  flex: 1 1 10rem;
}

كل فلتر يريد 10rem. يضع المتصفّح في السطر أكبر عدد ممكن من العناصر بعرض 10rem، ويلفّ الباقي، ثم يترك عناصر كل سطر تنمو لتملأه. عند عرض 25rem تحصل على اثنين في كل سطر؛ وعند 45rem على الأربعة. نقطة التحوّل يفرضها المحتوى ضمنيًا.

دورك الآن

يعطيك التمرين صف جلسة عرضه 400px بعنوان طويل جدًا. اجعل الوقت عمودًا ثابتًا بعرض 5rem، ودع العنوان يملأ المساحة المتبقية ويُقتطع بعلامة حذف، وأبقِ زر الحفظ في النهاية. وحين تحتاج صفوف البطاقات إلى الاصطفاف في بُعدين، ينتهي الطريق أمام flexbox؛ وهناك يتسلّم grid المهمة في الدرس التالي.

أهم النقاط

  • يحدّد flexbox أحجام العناصر على مرحلتين: يبدأ كل عنصر من flex-basis الخاصة به، ثم تتقاسم العناصر المساحة المتبقية بحسب flex-grow، أو تستردّ flex-shrink النقص منها.
  • flex: 1 تعني 1 1 0%، فتحصل العناصر على حصص متساوية مهما كان محتواها؛ وflex: auto تعني 1 1 auto، فيُحفظ حجم المحتوى ولا يُقسَّم إلا الفائض.
  • عناصر flex تأتي افتراضيًا بـ min-width: auto، ما يمنعها من التقلّص إلى أقل من محتواها؛ اضبط min-width: 0 ليُقتطع النص بدل أن يفيض.
  • الـ margin التلقائية (auto) على عنصر flex تبتلع كل المساحة الحرّة في ذلك الجانب، وهي أنظف طريقة لدفع عنصر واحد إلى الطرف البعيد.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ثلاثة عناصر بنصوص مختلفة الطول داخل حاوية flex عرضها 600px. تريد ثلاثة أعمدة متساوية عرض كل منها 200px. أيّ تصريح على العناصر يحقّق ذلك؟

السؤال 2 من 4

عنوان جلسة في صف flex عبارة عن سلسلة طويلة متصلة مع white-space: nowrap. إنه يدفع زر الحفظ خارج البطاقة رغم أن للعنوان flex: 1. ما الإصلاح؟

السؤال 3 من 4

في رأس صفحة هو صف flex، كيف تدفع رابط «Get tickets» إلى أقصى الطرف بينما يبقى الشعار والتنقّل في البداية؟

السؤال 4 من 4

.filters > * { flex: 1 1 10rem; } في حاوية flex تلتفّ عرضها 25rem وفيها أربعة فلاتر. ماذا يحدث؟

تدريب

بيئة تجريب الكود

روّض عنوان الجلسة الهارب

عرض الصف 400px، والعنوان الطويل يدفع زر Save خارجه. باستخدام خصائص flexbox على العناصر:

  • اجعل .time عمودًا ثابتًا بعرض 5rem لا ينمو ولا يتقلّص،
  • دع .title يأخذ كل المساحة المتبقية ويُقتطع بـ علامة حذف (ellipsis) بدل أن يفيض،
  • أبقِ .save بحجمه الطبيعي في نهاية الصف.

يجب ألّا يفيض الصف، وأن يصمد التخطيط حين يكون العنوان قصيرًا.

<div class="session-row">
  <span class="time">09:30</span>
  <span class="title">Shipping Accessible Design Systems Across Twelve Product Teams Without Losing Your Mind</span>
  <button class="save" type="button">Save</button>
</div>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • عرض عمود الوقت 5rem بالضبط
  • لا شيء يفيض خارج الصف
  • زر Save يقع في نهاية الصف
  • العنوان يملأ المساحة حتى الزر
  • العنوان يُقتطع بعلامة حذف
  • مع عنوان قصير يصمد التخطيط