{"id":"l-css-2-1","courseId":"css-mastery","lang":"ar","kind":"lesson","summary":"توقّع كيف يحدّد flexbox أحجام العناصر من flex-basis والنموّ والتقلّص، وعالج فخّ الفيضان الناتج عن min-width: auto، وابنِ صفوفًا تلتفّ دون media queries.","html":"<p>صف الجلسة في جدول Waypoint ثلاثة أشياء على خط واحد: الوقت والعنوان وزر الحفظ. يبدو جيدًا إلى أن يجدول فريق المحتوى جلسة بعنوان «Shipping Accessible Design Systems Across Twelve Product Teams Without Losing Your Mind»، فينزلق زر الحفظ خارج حافة البطاقة. لا شيء في الـ CSS يذكر أيّ عرض، فمن أين جاء الفيضان؟</p>\n<p>جاء من flexbox وهو يفعل بالضبط ما تقوله خوارزميته. وحين تعرف الخوارزمية، يستغرق إصلاح هذا الخطأ ثلاثين ثانية.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">كيف يقرّر flexbox الأحجام</h2>\n<p>يحدّد flexbox أحجام العناصر على امتداد المحور الرئيسي على مرحلتين:</p>\n<ol>\n<li><strong>الحجم المبدئي (hypothetical size).</strong> يبدأ كل عنصر من <code dir=\"ltr\">flex-basis</code> الخاصة به. ومع القيمة الافتراضية <code dir=\"ltr\">auto</code> يكون هذا هو <code dir=\"ltr\">width</code> إن كان مضبوطًا، وإلا فعرض محتواه.</li>\n<li><strong>توزيع الفرق.</strong> تقارن الحاوية مجموع الأسس (مع الفجوات) بحجمها هي. إن بقيت مساحة، تتقاسمها العناصر التي لها <code dir=\"ltr\">flex-grow</code> بنسبة معاملات نموّها. وإن لم تكفِ المساحة، تتنازل العناصر التي لها <code dir=\"ltr\">flex-shrink</code> عن جزء منها، بوزن هو معامل التقلّص مضروبًا في الأساس، فتتقلّص العناصر الكبيرة أكثر.</li>\n</ol>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-css-2-1-t1\"><defs><marker id=\"arrow-l-css-2-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-css-2-1-t1\">الصف العلوي: ثلاثة عناصر عند قيمة flex-basis الخاصة بها مع مساحة حرّة متبقية في حاوية عرضها 600 بكسل. الصف السفلي: المساحة الحرّة ذهبت إلى العنوان الذي له flex-grow بقيمة 1، بينما يحتفظ الوقت والزر بأساسهما.</title>\n  <text class=\"d-label-muted\" x=\"20\" y=\"24\">1. البداية من flex-basis</text>\n  <rect class=\"d-box\" x=\"20\" y=\"36\" width=\"600\" height=\"56\" rx=\"8\"/>\n  <rect class=\"d-box-accent\" x=\"28\" y=\"44\" width=\"80\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"44\" y=\"70\">time</text>\n  <rect class=\"d-box-primary\" x=\"116\" y=\"44\" width=\"180\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"132\" y=\"70\">title</text>\n  <rect class=\"d-box-success\" x=\"304\" y=\"44\" width=\"70\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"314\" y=\"70\">save</text>\n  <rect class=\"d-box-warn d-dashed\" x=\"382\" y=\"44\" width=\"230\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-label-muted\" x=\"412\" y=\"70\">مساحة حرّة</text>\n  <text class=\"d-label-muted\" x=\"20\" y=\"134\">2. للعنوان flex-grow: 1، فيأخذ المساحة الحرّة</text>\n  <rect class=\"d-box\" x=\"20\" y=\"146\" width=\"600\" height=\"56\" rx=\"8\"/>\n  <rect class=\"d-box-accent\" x=\"28\" y=\"154\" width=\"80\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"44\" y=\"180\">time</text>\n  <rect class=\"d-box-primary\" x=\"116\" y=\"154\" width=\"418\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"132\" y=\"180\">title</text>\n  <rect class=\"d-box-success\" x=\"542\" y=\"154\" width=\"70\" height=\"40\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"552\" y=\"180\">save</text>\n</svg><figcaption>تبدأ عناصر flex من أساسها، وتُقسَّم المساحة المتبقية بحسب معامل النموّ</figcaption></figure>\n<p>الاختصار <code dir=\"ltr\">flex</code> يضبط القيم الثلاث معًا، وكلماته المفتاحية تستحق الحفظ لأن سلوكها مختلف جدًا:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>الاختصار</th>\n<th>يتوسّع إلى</th>\n<th>استخدمه لـ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">flex: 1</code></td>\n<td><code dir=\"ltr\">1 1 0%</code></td>\n<td>أعمدة متساوية أيًّا كان المحتوى</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">flex: auto</code></td>\n<td><code dir=\"ltr\">1 1 auto</code></td>\n<td>الحفاظ على حجم المحتوى وتقاسم الفائض فقط</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">flex: none</code></td>\n<td><code dir=\"ltr\">0 0 auto</code></td>\n<td>عدم النموّ أو التقلّص أبدًا (الأيقونات والأزرار)</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">flex: 0 0 5rem</code></td>\n<td>أساس ثابت</td>\n<td>عمود بعرض محدّد بدقّة</td>\n</tr>\n</tbody>\n</table></div><h2 id=\"sec-2\" tabindex=\"-1\">فخّ <code dir=\"ltr\">min-width: auto</code></h2>\n<p>لنعد إلى العنوان الهارب. له <code dir=\"ltr\">white-space: nowrap</code>، فعرض محتواه هو الجملة كاملة، ربما 700px. والصف عرضه 400px. التقلّص يُفترض أن يحلّ المشكلة، فلماذا لا يحلّها؟</p>\n<p>لأن عناصر flex تأتي افتراضيًا بـ <code dir=\"ltr\">min-width: auto</code>، وهي تعني في عنصر flex «لا أصغر من حجم محتواي الأدنى (min-content)». والتقلّص يتوقّف عند هذا الحدّ. العنوان غير القابل للكسر لا يستطيع التقلّص تحت عرضه الكامل، فيفيض ويجرّ الزر معه.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">session-row.css</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.session-row</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  display</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  align-items</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">center</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  gap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">px</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.session-row</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .time</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 5</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.session-row</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  min-width</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;            </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">/* allow shrinking below content size */</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  overflow</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">hidden</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  text-overflow</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">ellipsis</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  white-space</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">nowrap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.session-row</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .save</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">none</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span></code></pre></div>\n<p><code dir=\"ltr\">min-width: 0</code> تزيل الحدّ الأدنى، وخصائص الفيضان الثلاث تحوّل النص المقصوص إلى علامة حذف (…) مرتّبة. والفخّ نفسه يظهر مع الروابط الطويلة وكتل الكود وحاويات flex أو grid المتداخلة، لذا هذا إصلاح ستكتبه مرات كثيرة.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>لوم عرض العنصر الأب</span></p>\n<p>حين يفيض ابنٌ في flex، يضيف الناس <code dir=\"ltr\">overflow: hidden</code> أو <code dir=\"ltr\">max-width: 100%</code> إلى <em>الحاوية</em>. الحاوية سليمة؛ المشكلة في الحدّ الأدنى التلقائي للعنصر. ضع <code dir=\"ltr\">min-width: 0</code> على العنصر الذي يرفض التقلّص (وفي حاوية باتجاه عمودي تكون <code dir=\"ltr\">min-height: 0</code>).</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">الدفع بالـ margins التلقائية</h2>\n<p>رأس صفحة Waypoint يحمل الشعار ومبدّل الأيام ورابط «Get tickets» الذي مكانه الطرف البعيد. <code dir=\"ltr\">justify-content: space-between</code> ستباعد بين الثلاثة كلها. أما الـ margin التلقائية فأدقّ:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.site-header</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">display</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">align-items</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">center</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">gap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.site-header</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .tickets</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">margin-inline-start</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">auto</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span></code></pre></div>\n<p>الـ margins التلقائية على عناصر flex تبتلع كل المساحة الحرّة في جانبها، فينتقل الرابط إلى النهاية ويبقى كل ما قبله متراصًّا. إنها في flexbox بمثابة نابض يدفع ما بعده إلى الطرف.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">الالتفاف دون media queries</h2>\n<p>شريط الفلاتر يحمل أربعة فلاتر للمسارات. على شاشة عريضة تتّسع في سطر واحد؛ وعلى الهاتف لا ينبغي أن تنضغط إلى شرائح نحيلة لا تُقرأ. الالتفاف مع أساس محدّد يعطيك ذلك دون أيّ breakpoint (نقطة تحوّل):</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.filters</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  display</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  flex-wrap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">wrap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  gap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.5</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.filters</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ></span><span style=\"color:#116329;--shiki-dark:#7EE787\"> *</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  flex</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 10</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>كل فلتر يريد 10rem. يضع المتصفّح في السطر أكبر عدد ممكن من العناصر بعرض 10rem، ويلفّ الباقي، ثم يترك عناصر كل سطر تنمو لتملأه. عند عرض 25rem تحصل على اثنين في كل سطر؛ وعند 45rem على الأربعة. نقطة التحوّل يفرضها المحتوى ضمنيًا.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>استخدم <code dir=\"ltr\">gap</code> لا الـ margins</span></p>\n<p><code dir=\"ltr\">gap</code> تضع المسافة <em>بين</em> العناصر فقط، فلا تحتاج أبدًا إلى <code dir=\"ltr\">:last-child { margin: 0 }</code> ولا إلى margins سالبة على الحاوية. وهي تعمل في flexbox في كل المتصفّحات الحالية.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">دورك الآن</h2>\n<p>يعطيك التمرين صف جلسة عرضه 400px بعنوان طويل جدًا. اجعل الوقت عمودًا ثابتًا بعرض 5rem، ودع العنوان يملأ المساحة المتبقية ويُقتطع بعلامة حذف، وأبقِ زر الحفظ في النهاية. وحين تحتاج صفوف البطاقات إلى الاصطفاف في بُعدين، ينتهي الطريق أمام flexbox؛ وهناك يتسلّم grid المهمة في الدرس التالي.</p>\n","toc":[{"id":"sec-1","text":"كيف يقرّر flexbox الأحجام","level":2},{"id":"sec-2","text":"فخّ min-width: auto","level":2},{"id":"sec-3","text":"الدفع بالـ margins التلقائية","level":2},{"id":"sec-4","text":"الالتفاف دون media queries","level":2},{"id":"sec-5","text":"دورك الآن","level":2}],"takeaways":["يحدّد flexbox أحجام العناصر على مرحلتين: يبدأ كل عنصر من <code dir=\"ltr\">flex-basis</code> الخاصة به، ثم تتقاسم العناصر المساحة المتبقية بحسب <code dir=\"ltr\">flex-grow</code>، أو تستردّ <code dir=\"ltr\">flex-shrink</code> النقص منها.","<code dir=\"ltr\">flex: 1</code> تعني <code dir=\"ltr\">1 1 0%</code>، فتحصل العناصر على حصص متساوية مهما كان محتواها؛ و<code dir=\"ltr\">flex: auto</code> تعني <code dir=\"ltr\">1 1 auto</code>، فيُحفظ حجم المحتوى ولا يُقسَّم إلا الفائض.","عناصر flex تأتي افتراضيًا بـ <code dir=\"ltr\">min-width: auto</code>، ما يمنعها من التقلّص إلى أقل من محتواها؛ اضبط <code dir=\"ltr\">min-width: 0</code> ليُقتطع النص بدل أن يفيض.","الـ margin التلقائية (auto) على عنصر flex تبتلع كل المساحة الحرّة في ذلك الجانب، وهي أنظف طريقة لدفع عنصر واحد إلى الطرف البعيد."],"further":[{"title":"Controlling ratios of flex items along the main axis (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Controlling_flex_item_ratios"},{"title":"flex (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/flex"},{"title":"Flexbox (web.dev Learn CSS)","url":"https://web.dev/learn/css/flexbox"}],"quiz":[{"q":"<p>ثلاثة عناصر بنصوص مختلفة الطول داخل حاوية flex عرضها 600px. تريد ثلاثة أعمدة متساوية عرض كل منها 200px. أيّ تصريح على العناصر يحقّق ذلك؟</p>","options":[{"text":"<code dir=\"ltr\">flex: auto</code>","why":"<code dir=\"ltr\">auto</code> تعني أساسًا قيمته <code dir=\"ltr\">auto</code> (حجم المحتوى)، فيبدأ النص الأطول أعرض ويبقى أعرض بعد تقاسم المساحة الإضافية."},{"text":"<code dir=\"ltr\">flex-grow: 1</code>","why":"هذا يُبقي القيمة الافتراضية <code dir=\"ltr\">flex-basis: auto</code>، فتظل العناصر تبدأ من عرض محتواها؛ ولا تُقسَّم بالتساوي إلا المساحة المتبقية."},{"text":"<code dir=\"ltr\">flex: 1</code>","why":"صحيح. <code dir=\"ltr\">flex: 1</code> تضبط الأساس على 0%، فتصبح الـ 600px كلها مساحة حرّة تتقاسمها ثلاثة عناصر نموّها 1 بالتساوي."}],"answer":2},{"q":"<p>عنوان جلسة في صف flex عبارة عن سلسلة طويلة متصلة مع <code dir=\"ltr\">white-space: nowrap</code>. إنه يدفع زر الحفظ خارج البطاقة رغم أن للعنوان <code dir=\"ltr\">flex: 1</code>. ما الإصلاح؟</p>","options":[{"text":"أضف <code dir=\"ltr\">min-width: 0</code> (مع <code dir=\"ltr\">overflow: hidden</code>) إلى العنوان.","why":"صحيح. عناصر flex تأتي افتراضيًا بـ <code dir=\"ltr\">min-width: auto</code>، وهي تعني «لا أصغر من محتواي أبدًا». الصفر يزيل هذا الحدّ الأدنى، فيستطيع العنوان أن يتقلّص ويُقتطع."},{"text":"أضف <code dir=\"ltr\">flex-shrink: 10</code> إلى العنوان.","why":"معاملات التقلّص لا تستطيع النزول بالعنصر تحت حجمه الأدنى، و<code dir=\"ltr\">min-width: auto</code> تضع هذا الحدّ الأدنى عند عرض المحتوى."},{"text":"أضف <code dir=\"ltr\">flex-wrap: wrap</code> إلى الصف.","why":"الالتفاف ينقل العناصر إلى أسطر جديدة، لكن العنوان وحده ما زال أعرض من الصف، فسيظل يفيض."}],"answer":0},{"q":"<p>في رأس صفحة هو صف flex، كيف تدفع رابط «Get tickets» إلى أقصى الطرف بينما يبقى الشعار والتنقّل في البداية؟</p>","options":[{"text":"<code dir=\"ltr\">justify-content: space-between</code> على رأس الصفحة.","why":"هذا يباعد بين كل العناصر، فيطفو التنقّل إلى المنتصف بدل أن يبقى بجوار الشعار."},{"text":"<code dir=\"ltr\">margin-inline-start: auto</code> على الرابط.","why":"صحيح. الـ margin التلقائية تبتلع كل المساحة الحرّة في ذلك الجانب، فينتقل الرابط إلى النهاية وتبقى العناصر الأخرى متراصّة في البداية."},{"text":"<code dir=\"ltr\">float: right</code> على الرابط.","why":"الـ floats لا أثر لها على عناصر flex؛ حاوية flex تتجاهل <code dir=\"ltr\">float</code>."},{"text":"<code dir=\"ltr\">justify-self: end</code> على الرابط.","why":"<code dir=\"ltr\">justify-self</code> لا تفعل شيئًا في flexbox، لأن المحور الرئيسي مشترك بين كل العناصر ولا ينقسم إلى خلايا."}],"answer":1},{"q":"<p><code dir=\"ltr\">.filters &gt; * { flex: 1 1 10rem; }</code> في حاوية flex تلتفّ عرضها 25rem وفيها أربعة فلاتر. ماذا يحدث؟</p>","options":[{"text":"تنضغط الأربعة في سطر واحد بعرض 6.25rem لكل منها.","why":"الالتفاف يحدث قبل التقلّص. أربعة أسس بعرض 10rem لا تتّسع في 25rem، فينكسر السطر بدلًا من ذلك."},{"text":"كل فلتر عرضه 10rem بالضبط، مع فراغ في نهاية كل سطر.","why":"مع نموّ قيمته 1 تتقاسم العناصر المساحة المتبقية في سطرها، فتتمدّد لتملأه."},{"text":"اثنان في كل سطر، يكبر كلٌّ منهما ليملأ نصف سطره.","why":"صحيح. أساسان بعرض 10rem يتّسعان وثلاثة لا تتّسع، فيحمل كل سطر عنصرين ينموان بعدها لتقاسم المساحة المتبقية."}],"answer":2}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<div class=\"session-row\">\n  <span class=\"time\">09:30</span>\n  <span class=\"title\">Shipping Accessible Design Systems Across Twelve Product Teams Without Losing Your Mind</span>\n  <button class=\"save\" type=\"button\">Save</button>\n</div>","css":"/* The row is fixed at 400px for this exercise */\n.session-row {\n  width: 400px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n}\n.time {\n  width: 5rem;\n  font-variant-numeric: tabular-nums;\n  color: #6b7280;\n}\n.title {\n  white-space: nowrap;\n  font-weight: 600;\n}\n.save {\n  padding: 0.3em 0.8em;\n}\n"},"solution":{"html":"<div class=\"session-row\">\n  <span class=\"time\">09:30</span>\n  <span class=\"title\">Shipping Accessible Design Systems Across Twelve Product Teams Without Losing Your Mind</span>\n  <button class=\"save\" type=\"button\">Save</button>\n</div>","css":"/* The row is fixed at 400px for this exercise */\n.session-row {\n  width: 400px;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  border: 1px solid #e5e7eb;\n  border-radius: 10px;\n}\n.time {\n  flex: 0 0 5rem;\n  font-variant-numeric: tabular-nums;\n  color: #6b7280;\n}\n.title {\n  flex: 1;\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  font-weight: 600;\n}\n.save {\n  flex: none;\n  padding: 0.3em 0.8em;\n}\n"},"checks":[{"id":"time-fixed","test":"const box = (s) => $(s).getBoundingClientRect(); return Math.abs(box('.time').width - 80) < 1;"},{"id":"no-overflow","test":"const row = $('.session-row'); return row.scrollWidth <= row.clientWidth + 1;"},{"id":"save-at-end","test":"const box = (s) => $(s).getBoundingClientRect(); const row = $('.session-row'); const inner = box('.session-row').right - parseFloat(style(row, 'border-right-width')) - parseFloat(style(row, 'padding-right')); return Math.abs(box('.save').right - inner) < 1.5;"},{"id":"title-fills","test":"const box = (s) => $(s).getBoundingClientRect(); const gap = parseFloat(style('.session-row', 'column-gap')) || 0; return Math.abs(box('.title').right + gap - box('.save').left) < 1.5;"},{"id":"ellipsis","test":"return style('.title', 'text-overflow') === 'ellipsis' && style('.title', 'overflow-x') === 'hidden';"},{"id":"short-title-still-fills","test":"const box = (s) => $(s).getBoundingClientRect(); const t = $('.title'); const old = t.textContent; t.textContent = 'Keynote'; await tick(16); const row = $('.session-row'); const inner = box('.session-row').right - parseFloat(style(row, 'border-right-width')) - parseFloat(style(row, 'padding-right')); const ok = Math.abs(box('.save').right - inner) < 1.5 && Math.abs(box('.time').width - 80) < 1; t.textContent = old; return ok;"}],"title":"روّض عنوان الجلسة الهارب","prompt":"<p>عرض الصف 400px، والعنوان الطويل يدفع زر <strong>Save</strong> خارجه. باستخدام خصائص flexbox على العناصر:</p>\n<ul>\n<li>اجعل <code dir=\"ltr\">.time</code> عمودًا ثابتًا بعرض <strong>5rem</strong> لا ينمو ولا يتقلّص،</li>\n<li>دع <code dir=\"ltr\">.title</code> يأخذ كل المساحة المتبقية ويُقتطع بـ <strong>علامة حذف (ellipsis)</strong> بدل أن يفيض،</li>\n<li>أبقِ <code dir=\"ltr\">.save</code> بحجمه الطبيعي في نهاية الصف.</li>\n</ul>\n<p>يجب ألّا يفيض الصف، وأن يصمد التخطيط حين يكون العنوان قصيرًا.</p>\n","hints":["شيءٌ ما يمنع العنوان من التقلّص تحت عرض نصّه. أيّ خاصية تضع هذا الحدّ الأدنى على عناصر flex؟","<code dir=\"ltr\">flex: 0 0 5rem</code> تعطي عمودًا ثابتًا. و<code dir=\"ltr\">flex: 1</code> مع <code dir=\"ltr\">min-width: 0</code> تتيح للعنوان أن يملأ ويتقلّص.","على <code dir=\"ltr\">.title</code>: <code dir=\"ltr\">flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;</code>. وعلى <code dir=\"ltr\">.save</code>: <code dir=\"ltr\">flex: none;</code>."],"explanation":"<p>عناصر flex لها <code dir=\"ltr\">min-width: auto</code>، ما يُبقيها بعرض محتواها على الأقل؛ ومع <code dir=\"ltr\">white-space: nowrap</code> يكون ذلك الجملة كلها. <code dir=\"ltr\">min-width: 0</code> تزيل الحدّ الأدنى فيستطيع العنوان أن يتقلّص، و<code dir=\"ltr\">overflow: hidden</code> مع <code dir=\"ltr\">text-overflow: ellipsis</code> تحوّلان النص المقصوص إلى <code dir=\"ltr\">…</code>. و<code dir=\"ltr\">flex: 0 0 5rem</code> تثبّت عمود الوقت (أما <code dir=\"ltr\">width: 5rem</code> وحدها فستتقلّص رغم ذلك)، و<code dir=\"ltr\">flex: 1</code> تجعل العنوان يمتصّ المساحة المتبقية حتى حين يكون قصيرًا، و<code dir=\"ltr\">flex: none</code> تمنع الزر من الانضغاط.</p>\n","checkLabels":{"time-fixed":"عرض عمود الوقت 5rem بالضبط","no-overflow":"لا شيء يفيض خارج الصف","save-at-end":"زر Save يقع في نهاية الصف","title-fills":"العنوان يملأ المساحة حتى الزر","ellipsis":"العنوان يُقتطع بعلامة حذف","short-title-still-fills":"مع عنوان قصير يصمد التخطيط"}},"words":601,"readMinutes":4}