CSS3 والتصميم المتجاوب

نموذج الصندوق في CSS بالتفصيل

30 دقيقة الدرس 8 من 60

فهم نموذج الصندوق في CSS

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

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

  1. المحتوى (Content) -- المنطقة الداخلية الأعمق حيث يعيش محتواك الفعلي: النص والصور والعناصر الأبناء والفيديوهات أو أي محتوى آخر. عندما تحدد width و height على عنصر، في نموذج الصندوق الافتراضي، أنت تحدد حجم منطقة المحتوى هذه فقط.
  2. الحشوة (Padding) -- المساحة الشفافة المريحة بين المحتوى والحد. الحشوة تدفع الحد للخارج بعيداً عن المحتوى. تفصيل مهم: لون خلفية العنصر وصورة الخلفية يمتدان إلى منطقة الحشوة. هذا يعني أن الحشوة بصرياً "داخل" العنصر.
  3. الحد (Border) -- خط (مرئي أو غير مرئي) يلتف حول الحشوة. يمكن أن يكون للحد عرض ونمط ولون خاص به. يقع بين الحشوة والهامش.
  4. الهامش (Margin) -- المساحة الشفافة خارج الحد التي تنشئ مسافة بين هذا العنصر وجيرانه. الخلفية لا تمتد إلى الهامش. الهامش دائماً شفاف.
ملاحظة: يمكنك تصور نموذج الصندوق في أدوات المطور في متصفحك. في Chrome أو Firefox، انقر بزر الفأرة الأيمن على أي عنصر واختر "فحص" وابحث عن مخطط نموذج الصندوق (في Chrome يظهر في تبويب "Computed" أو في أسفل لوحة "Styles"). يعرض المخطط المحتوى والحشوة والحد والهامش كمستطيلات ملونة متداخلة بقيمها بالبكسل. تمرير المؤشر فوق عنصر في لوحة العناصر يبرز أيضاً هذه الطبقات مباشرة على الصفحة بألوان مختلفة. هذه واحدة من أكثر أدوات تتبع الأخطاء فائدة في تطوير الويب -- استخدمها باستمرار.

خاصية box-sizing: content-box مقابل border-box

تتحكم خاصية box-sizing في سؤال جوهري: عندما تحدد width و height على عنصر، ما الذي تحدد أبعاده بالضبط؟ الإجابة تعتمد على نموذج box-sizing النشط. هناك قيمتان ممكنتان، والفرق بينهما هو مصدر ارتباك وأخطاء تخطيط أكثر من أي شيء آخر تقريباً في CSS.

content-box: الافتراضي (والمشكلة)

افتراضياً، كل عنصر يستخدم box-sizing: content-box. في هذا النموذج، خاصيتا width و height تحددان حجم منطقة المحتوى فقط. أي حشوة وحد تضيفهما يُحسبان فوق العرض والارتفاع المحددين، مما يجعل الحجم المعروض الفعلي للعنصر أكبر من الأبعاد التي كتبتها في CSS.

مثال: مشكلة content-box

/* مع content-box (الافتراضي): */
.card {
    width: 300px;
    padding: 20px;
    border: 5px solid #333;

    /* لنحسب العرض المعروض الفعلي:
       عرض المحتوى:      300px (ما حددته)
       + الحشوة اليسرى:   20px
       + الحشوة اليمنى:   20px
       + الحد الأيسر:       5px
       + الحد الأيمن:       5px
       = العرض الإجمالي:  350px (أكثر بـ 50px مما أردت!)
    */
}

/* هذا يصبح مشكلة حقيقية مع النسب المئوية: */
.half-width {
    width: 50%;
    padding: 20px;
    border: 1px solid #ccc;

    /* العرض الإجمالي = 50% + 40px حشوة + 2px حد
       هذا يتجاوز الحاوية الأم لأن الإجمالي
       يتعدى 50% من عرض الأم! اثنان من هذه
       جنباً إلى جنب لن يتسعا في صف واحد. */
}

مع content-box، عليك دائماً إجراء حسابات ذهنية. المساحة الفعلية التي يشغلها العنصر أفقياً هي: width + padding-left + padding-right + border-left-width + border-right-width. عمودياً هي: height + padding-top + padding-bottom + border-top-width + border-bottom-width. هذا يجعل التخطيطات المتجاوبة محبطة لأن العروض المبنية على النسب المئوية تنكسر فور إضافة أي حشوة أو حد. كما يجعل من المستحيل تقريباً إنشاء تخطيطات متعددة الأعمدة تجمع بالضبط إلى 100%.

border-box: الخيار الأفضل

عندما تعيّن box-sizing: border-box، يتغير السلوك بشكل جذري. الآن خاصيتا width و height تشملان المحتوى والحشوة والحد. يطرح المتصفح تلقائياً الحشوة والحد من العرض المحدد لتحديد مساحة منطقة المحتوى. النتيجة أن الحجم المعروض الإجمالي للعنصر يطابق دائماً ما حددته.

مثال: بساطة border-box

/* مع border-box: */
.card {
    box-sizing: border-box;
    width: 300px;
    padding: 20px;
    border: 5px solid #333;

    /* العرض المعروض الإجمالي = 300px (بالضبط ما حددته)
       يحسب المتصفح:
       منطقة المحتوى = 300 - 20 - 20 - 5 - 5 = 250px
       منطقة المحتوى تتقلص لاستيعاب الحشوة والحد،
       لكن العنصر الكلي يبقى بالضبط 300px. */
}

/* العروض بالنسب المئوية تعمل بشكل مثالي الآن: */
.half-width {
    box-sizing: border-box;
    width: 50%;
    padding: 20px;
    border: 1px solid #ccc;

    /* العرض الإجمالي = بالضبط 50% من الأم. انتهى.
       منطقة المحتوى تتكيف تلقائياً.
       اثنان من هذه جنباً إلى جنب يتسعان بشكل مثالي! */
}

/* تخطيط بثلاثة أعمدة يعمل فعلاً: */
.column {
    box-sizing: border-box;
    width: 33.333%;
    padding: 15px;
    border: 1px solid #ddd;
    float: left;
    /* الأعمدة الثلاثة تتسع في بالضبط 100% عرض */
}

لماذا يُفضَّل border-box عالمياً

تقريباً كل إطار عمل CSS حديث وإعادة تعيين CSS ومطور محترف يستخدم border-box كافتراضي لجميع العناصر. الأسباب عملية بالكامل:

  • أبعاد متوقعة -- عندما تعيّن width: 300px، يكون العنصر 300px عرضاً. بلا مفاجآت، بلا حسابات إضافية، بلا تجاوز.
  • التخطيطات المتجاوبة تعمل -- يمكنك تعيين width: 50% وإضافة حشوة وحد بالقدر الذي تريده دون تجاوز الأم أبداً.
  • تخطيطات الشبكة والأعمدة -- إنشاء تخطيط بثلاثة أعمدة حيث كل عمود width: 33.33% مع حشوة يعمل فعلاً، بدلاً من الانكسار لأن الحشوة تدفع العرض الإجمالي متجاوزاً 100%.
  • حسابات ذهنية أقل -- لم تعد بحاجة لطرح الحشوة والحد من العرض الإجمالي المطلوب. ما تكتبه هو ما تحصل عليه.
  • التوافق مع أدوات التصميم -- عندما يقول المصمم "هذه البطاقة عرضها 300px"، يقصد البطاقة كاملة بما فيها الحشوة والحد. border-box يطابق هذا النموذج الذهني.

إعادة تعيين box-sizing العالمية

لأن border-box مفضل عالمياً، يطبقه المطورون على كل عنصر في الصفحة. المنهج الموصى به يستخدم نمط inherit، مما يسهل التجاوز لمكونات محددة إذا لزم الأمر:

مثال: إعادة تعيين box-sizing العالمية

/* إعادة التعيين العالمية الموصى بها */
html {
    box-sizing: border-box;
}

*, *::before, *::after {
    box-sizing: inherit;
}

/*
لماذا نستخدم inherit بدلاً من تعيين border-box على * مباشرة؟

إذا كتبت: * { box-sizing: border-box; }
هذا يعمل، لكنه يعني أن كل عنصر مقفل على border-box.
إذا أدمجت أداة خارجية تتوقع content-box،
لا يمكنك تغييرها دون تجاوز كل عنصر بداخلها.

مع منهج inherit:
- جميع العناصر ترث border-box من html افتراضياً
- إذا احتاج مكون خارجي content-box، تعيّنه
  على العنصر الجذر للمكون، وجميع أبنائه
  ترث content-box تلقائياً.
*/

/* مثال: أداة خارجية تتطلب content-box */
.legacy-widget {
    box-sizing: content-box;
    /* جميع أبناء .legacy-widget ترث content-box
       لأنها تستخدم box-sizing: inherit */
}

/* منهج آخر (أبسط، تستخدمه كثير من الأُطر): */
*, *::before, *::after {
    box-sizing: border-box;
}
/* هذا أبسط لكن أقل مرونة لتجاوزات الأطراف الخارجية */
نصيحة: أضف إعادة تعيين box-sizing: border-box العالمية في أعلى ملف CSS الخاص بك، قبل أي أنماط أخرى. هذه القاعدة الواحدة ستنقذك من صداع تخطيط لا يُحصى طوال مشروعك بالكامل. كل إطار عمل CSS رئيسي -- Bootstrap وTailwind CSS وFoundation وBulma -- يتضمن هذه الإعادة. تُعتبر ممارسة مثلى مطلقة في CSS الحديث.

الحشوة بالتفصيل

الحشوة تنشئ مسافة بين محتوى العنصر وحده. تُعتبر "داخل" العنصر: لون خلفية العنصر وصورة الخلفية يمتدان عبر منطقة الحشوة. قيم الحشوة لا يمكن أن تكون سالبة -- إذا احتجت لسحب المحتوى في الاتجاه المعاكس، تحتاج لاستخدام هوامش سالبة بدلاً من ذلك.

اختصار الحشوة والخصائص الفردية

خاصية padding المختصرة تقبل قيمة واحدة أو اثنتين أو ثلاث أو أربع قيم. المتصفح يفسرها بشكل مختلف حسب العدد المقدم:

مثال: قيم اختصار الحشوة

/* قيمة واحدة: تُطبق على جميع الجوانب الأربعة */
.box-a {
    padding: 20px;
    /* أعلى = 20px، يمين = 20px، أسفل = 20px، يسار = 20px */
}

/* قيمتان: عمودي | أفقي */
.box-b {
    padding: 10px 30px;
    /* أعلى = 10px، يمين = 30px، أسفل = 10px، يسار = 30px */
}

/* ثلاث قيم: أعلى | أفقي | أسفل */
.box-c {
    padding: 10px 20px 30px;
    /* أعلى = 10px، يمين = 20px، أسفل = 30px، يسار = 20px */
}

/* أربع قيم: أعلى | يمين | أسفل | يسار (باتجاه عقارب الساعة) */
.box-d {
    padding: 10px 20px 30px 40px;
    /* أعلى = 10px، يمين = 20px، أسفل = 30px، يسار = 40px */
}

/* الخصائص الفردية */
.box-e {
    padding-top: 10px;
    padding-right: 20px;
    padding-bottom: 30px;
    padding-left: 40px;
}
نصيحة: تذكر ترتيب القيم الأربع باتجاه عقارب الساعة من الأعلى: أعلى، يمين، أسفل، يسار. وسيلة تذكر مفيدة هي TRouBLe (TRBL). نفس الترتيب باتجاه عقارب الساعة ينطبق على خصائص margin و border-width و border-style و border-color المختصرة أيضاً. بمجرد حفظه، ستستخدمه في كل مكان.

الحشوة بالنسب المئوية

عندما تستخدم نسباً مئوية للحشوة، يحدث شيء مفاجئ: جميع الجوانب الأربعة -- بما في ذلك padding-top و padding-bottom -- تُحسب نسبة إلى عرض الكتلة الحاوية للعنصر، وليس ارتفاعها. هذا يبدو غير بديهي في البداية، لكنه مصمم عمداً وله تطبيق مفيد.

مثال: الحشوة بالنسب المئوية لنسب العرض إلى الارتفاع

/* الحشوة بالنسب المئوية نسبية إلى عرض الأم */
.parent {
    width: 400px;
}
.child {
    padding: 10%;
    /* جميع الجوانب الأربعة تحصل على 40px (10% من 400px عرض الأم) */
    /* حتى padding-top وpadding-bottom مبنية على العرض */
}

/* حيلة كلاسيكية: الحفاظ على نسب العرض إلى الارتفاع بالحشوة المئوية */
.aspect-ratio-16-9 {
    width: 100%;
    height: 0;
    padding-bottom: 56.25%; /* 9/16 = 0.5625 = 56.25% */
    position: relative;
    overflow: hidden;
}
.aspect-ratio-16-9 > * {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

/* البديل الحديث: خاصية aspect-ratio */
.modern-aspect-ratio {
    width: 100%;
    aspect-ratio: 16 / 9;
    /* أبسط بكثير! لكن حيلة الحشوة لا تزال مفيدة
       لدعم المتصفحات القديمة */
}
ملاحظة: حيلة الحشوة المئوية لنسب العرض إلى الارتفاع كانت المنهج القياسي لتضمين الفيديو المتجاوب وحاويات الصور لسنوات عديدة. خاصية aspect-ratio الحديثة توفر الآن حلاً أبسط، لكن فهم حيلة الحشوة لا يزال قيّماً لأنك ستصادفها في قواعد الأكواد القديمة وتعمل في المتصفحات التي لا تدعم بعد aspect-ratio.

الهامش بالتفصيل

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

اختصار الهامش والخصائص الفردية

اختصار margin يتبع نفس نمط القيم من واحدة إلى أربع مثل الحشوة (ترتيب TRBL باتجاه عقارب الساعة):

مثال: اختصار الهامش وقيمة auto

/* نفس نمط الاختصار مثل الحشوة */
.box {
    margin: 20px;                    /* جميع الجوانب الأربعة */
    margin: 10px 30px;               /* عمودي | أفقي */
    margin: 10px 20px 30px;          /* أعلى | أفقي | أسفل */
    margin: 10px 20px 30px 40px;     /* أعلى | يمين | أسفل | يسار */
}

/* خصائص الجوانب الفردية */
.box-sides {
    margin-top: 10px;
    margin-right: auto;
    margin-bottom: 10px;
    margin-left: auto;
}

/* قيمة AUTO: توسيط العناصر الكتلية أفقياً */
.centered-box {
    width: 600px;
    margin: 0 auto;
    /* كيف يعمل هذا:
       - هوامش الأعلى والأسفل هي 0
       - هوامش اليسار واليمين هي "auto"
       - يحسب المتصفح المساحة الأفقية المتبقية
         في الأم ويقسمها بالتساوي بين اليسار واليمين
       - النتيجة: العنصر موسط أفقياً بشكل مثالي
    */
}

/* نمط شائع لحاويات الصفحة */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
    /* موسط، لا يتجاوز أبداً 1200px، مع حشوة
       أفقية حتى لا يلمس المحتوى الحواف */
}

/* دفع عنصر لليمين باستخدام auto */
.push-right {
    margin-left: auto;
    /* في حاوية flex، هذا يدفع العنصر
       إلى أقصى الجانب الأيمن */
}
ملاحظة: قيمة auto للهامش تعمل فقط للتوسيط الأفقي على العناصر الكتلية التي لها عرض محدد. لا تعمل للتوسيط العمودي في التدفق الطبيعي للمستند، ولا تعمل على العناصر السطرية. للتوسيط العمودي، تحتاج Flexbox (align-items: center) أو Grid (place-items: center). لكن في سياق Flexbox، margin: auto تعمل فعلاً في كلا الاتجاهين، وهي تقنية توسيط قوية.

انهيار الهوامش

انهيار الهوامش هو أحد أكثر السلوكيات إرباكاً وإدهاشاً في CSS. عندما يتلامس هامشان عموديان (أعلى/أسفل)، لا يُجمعان. بدلاً من ذلك، ينهاران إلى هامش واحد حجمه يساوي الأكبر من القيمتين. هذا السلوك ينطبق فقط على الهوامش العمودية في التدفق الطبيعي للمستند -- الهوامش الأفقية (يسار/يمين) لا تنهار أبداً.

السيناريو 1: الأشقاء المتجاورون

عندما يتراكم شقيقان كتليان عمودياً، الهامش السفلي للعنصر الأول ينهار مع الهامش العلوي للعنصر الثاني:

مثال: انهيار الهوامش بين الأشقاء

<style>
.first {
    margin-bottom: 30px;
    background: lightblue;
    padding: 20px;
}
.second {
    margin-top: 20px;
    background: lightcoral;
    padding: 20px;
}
</style>

<div class="first">العنصر الأول (margin-bottom: 30px)</div>
<div class="second">العنصر الثاني (margin-top: 20px)</div>

/* الفجوة المتوقعة: 30px + 20px = 50px؟ خطأ!
   الفجوة الفعلية: 30px (الأكبر من القيمتين يفوز)
   الهوامش تنهار إلى هامش واحد 30px. */

/* إذا كان كلا الهامشين متساويين: */
.equal-a { margin-bottom: 25px; }
.equal-b { margin-top: 25px; }
/* الفجوة = 25px (وليس 50px) */

/* إذا كان أحد الهوامش 0: */
.has-margin { margin-bottom: 40px; }
.no-margin { margin-top: 0; }
/* الفجوة = 40px (الأكبر بين 40 و0) */

السيناريو 2: الأب والابن الأول/الأخير

إذا لم يكن هناك حد أو حشوة أو محتوى سطري أو سياق تنسيق كتلي يفصل هامش الأب عن هامش ابنه الأول (أو الأخير)، تنهار تلك الهوامش معاً. هامش الابن "يتسرب" فعلياً عبر الأب:

مثال: انهيار هوامش الأب والابن

<style>
/* المشكلة: */
.parent {
    margin-top: 40px;
    background: lightyellow;
    /* لا padding-top، لا border-top! */
}
.child {
    margin-top: 25px;
    background: lightgreen;
    padding: 10px;
}
</style>

<div class="parent">
    <div class="child">ابن بهامش علوي 25px</div>
</div>

/* هامش الابن العلوي 25px ينهار مع هامش الأب العلوي
   40px. النتيجة هامش واحد 40px فوق الأب.
   داخل الأب، يظهر الابن ملاصقاً للأعلى --
   لا توجد مسافة بين حافة الأب العلوية والابن.
   الهامش "تسرّب للخارج." */

/* الإصلاح: أضف أي فصل بين الهوامش */

/* إصلاح 1: أضف حشوة للأم */
.parent-fix-1 {
    margin-top: 40px;
    padding-top: 1px; /* حتى 1px تمنع الانهيار! */
    background: lightyellow;
}

/* إصلاح 2: أضف حداً للأم */
.parent-fix-2 {
    margin-top: 40px;
    border-top: 1px solid transparent;
    background: lightyellow;
}

/* إصلاح 3: أنشئ سياق تنسيق كتلي جديد (BFC) */
.parent-fix-3 {
    margin-top: 40px;
    overflow: hidden; /* ينشئ BFC */
    background: lightyellow;
}

/* إصلاح 4: استخدم display: flow-root (حديث، BFC صريح) */
.parent-fix-4 {
    margin-top: 40px;
    display: flow-root;
    background: lightyellow;
}

السيناريو 3: الكتل الفارغة

إذا لم يكن لعنصر كتلي ارتفاع ولا حشوة ولا حد ولا محتوى، فإن هامشيه العلوي والسفلي ينهاران إلى هامش واحد:

مثال: انهيار هوامش الكتل الفارغة

<style>
.empty {
    margin-top: 20px;
    margin-bottom: 30px;
    /* لا ارتفاع، لا حشوة، لا حد، لا محتوى */
}
</style>

<div class="above">فوق</div>
<div class="empty"></div>
<div class="below">تحت</div>

/* هامشا العنصر الفارغ العلوي (20px) والسفلي (30px)
   ينهاران إلى هامش واحد 30px. ثم قد ينهار هذا الهامش
   30px أيضاً مع هوامش العناصر فوقه وتحته! */

كيفية منع انهيار الهوامش

هناك عدة طرق موثوقة لمنع انهيار الهوامش:

  • أضف padding (حتى 1px) بين الهوامش المنهارة.
  • أضف border (حتى 1px solid transparent) بين الهوامش.
  • استخدم overflow: hidden أو overflow: auto على الأب لإنشاء سياق تنسيق كتلي جديد (BFC).
  • استخدم display: flex أو display: grid على الأب. حاويات Flex وGrid لا يحدث فيها انهيار هوامش بين أبنائها.
  • استخدم display: flow-root على الأب، مما ينشئ صراحة BFC جديد بدون أي آثار جانبية.
  • أضف محتوى سطرياً (حتى مسافة غير منقسمة &nbsp;) بين الأب والابن.
تحذير: انهيار الهوامش هو أحد أكثر المصادر شيوعاً لأخطاء التباعد الغامضة. إذا رأيت فجوات غير متوقعة (أو فجوات مفقودة) بين العناصر، فانهيار الهوامش هو السبب المرجح جداً. افحص الهوامش في أدوات المطور لترى ما إذا كانت الهوامش المتجاورة متداخلة. استراتيجية مستخدمة على نطاق واسع لتجنب الانهيار تماماً هي استخدام margin-bottom فقط على العناصر (أبداً margin-top)، أو العكس. بهذه الطريقة، الهوامش العمودية لا تتلامس أبداً ولا يمكن أن تنهار.

الهوامش السالبة واستخداماتها

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

مثال: الهوامش السالبة عملياً

/* سحب عنصر للأعلى، متداخلاً مع العنصر فوقه */
.overlap-up {
    margin-top: -20px;
    /* العنصر يتحرك للأعلى 20px من موقعه الطبيعي،
       متداخلاً مع ما فوقه */
}

/* سحب عنصر لليسار */
.pull-left {
    margin-left: -15px;
}

/* استخدام عملي 1: الخروج من حشوة الحاوية */
.container {
    padding: 30px;
    background: #f5f5f5;
}
.full-width-image {
    margin-left: -30px;
    margin-right: -30px;
    /* الصورة تمتد إلى حواف الحاوية،
       متجاهلة حشوة الحاوية 30px على كلا الجانبين */
}

/* استخدام عملي 2: تصميم بطاقات متداخلة */
.card-overlap {
    margin-top: -40px;
    position: relative; /* ليظهر فوق العنصر خلفه */
    z-index: 1;
    background: white;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

/* استخدام عملي 3: إزالة التباعد الزائد من العناصر الأبناء */
.grid {
    display: flex;
    flex-wrap: wrap;
    margin: -10px; /* مقاومة هامش 10px على الأبناء */
}
.grid-item {
    margin: 10px;
    width: calc(33.333% - 20px);
}

/* استخدام عملي 4: التوسيط القديم (تاريخي) */
.old-center {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 400px;
    height: 300px;
    margin-top: -150px;  /* نصف الارتفاع */
    margin-left: -200px; /* نصف العرض */
    /* البديل الحديث: transform: translate(-50%, -50%) */
}
نصيحة: الهوامش السالبة ليست خدعة -- هي ميزة CSS مدعومة بالكامل ومقصودة. نمط "الهامش السالب على الغلاف لمقاومة هوامش الأبناء" يُستخدم على نطاق واسع في أُطر العمل مثل Bootstrap لأنظمة الشبكة الخاصة بها. لكن استخدمها بتأنٍّ: الاستخدام المفرط للهوامش السالبة يمكن أن يجعل التخطيطات هشة وصعبة الصيانة. إذا وجدت نفسك تستخدم كثيراً من الهوامش السالبة، فكر فيما إذا كان Flexbox gap أو CSS Grid gap سيكون حلاً أنظف.

العرض والارتفاع: سلوك auto وخصائص min/max

خاصيتا width و height تتحكمان في أبعاد منطقة المحتوى (في content-box) أو الصندوق بالكامل حتى الحد (في border-box). فهم كيف تتصرف هذه الخصائص افتراضياً، وكيفية استخدام قيود min/max، ضروري للتصميم المتجاوب.

الافتراضي: auto

افتراضياً، كل من width و height مضبوطتان على auto. لكن "auto" تعني أشياء مختلفة لكل منهما:

  • width: auto على عنصر كتلي تعني "خذ العرض المتاح بالكامل للأم، مطروحاً منه هوامشي." العنصر يتمدد ليملأ حاويته.
  • height: auto تعني "كن بالضبط بالارتفاع المطلوب لاحتواء كل محتواي." العنصر يتقلص ليناسب.
  • width: auto على عنصر سطري أو inline-block تعني "كن بالعرض الذي يحتاجه محتواي."

مثال: سلوك العرض والارتفاع

/* عنصر كتلي مع width: auto (الافتراضي) */
.block-default {
    /* width: auto -- يملأ عرض الأم */
    /* height: auto -- يتقلص ليناسب المحتوى */
    background: lightblue;
    padding: 20px;
}

/* تعيين عرض ثابت */
.fixed-width {
    width: 400px;
    /* مشكلة: على الشاشات الأضيق من 400px، هذا يتجاوز! */
}

/* استخدام max-width للأبعاد المتجاوبة */
.responsive-width {
    width: 100%;
    max-width: 400px;
    /* يأخذ عرض الأم الكامل لكن لا يتجاوز أبداً 400px */
    /* على الشاشات الصغيرة: 100% من الأم (متجاوب) */
    /* على الشاشات الكبيرة: يتوقف عند 400px */
}

/* min-width يضع أرضية */
.min-example {
    min-width: 200px;
    /* العنصر لن يكون أضيق من 200px أبداً،
       حتى لو كان أبوه أضيق (سيتجاوز) */
}

/* نمط حاوية متجاوبة شائع */
.container {
    width: 100%;
    max-width: 1200px;
    min-width: 320px;
    margin: 0 auto;
    padding: 0 20px;
}

/* min-height: بهذا الارتفاع على الأقل، لكن يمكن أن ينمو */
.hero-section {
    min-height: 100vh;
    /* على الأقل بارتفاع نافذة العرض الكاملة،
       لكنه ينمو أكثر إذا احتاج المحتوى ذلك */
}

/* max-height مع التعامل مع التجاوز */
.scrollable-list {
    max-height: 400px;
    overflow-y: auto;
    /* لا يتجاوز أبداً 400px؛ يعرض شريط تمرير إذا لزم الأمر */
}

/* خطر: ارتفاع ثابت على حاويات النص */
.bad-practice {
    height: 200px;
    /* إذا كان محتوى النص أطول من 200px،
       سيتجاوز ويتداخل مع العناصر أسفله!
       لا تعيّن أبداً ارتفاعاً ثابتاً على حاويات النص. */
}
.good-practice {
    min-height: 200px;
    /* 200px على الأقل، لكن ينمو مع المحتوى. آمن! */
}
تحذير: تعيين height ثابت على العناصر التي تحتوي نصاً هو أحد أكثر أخطاء CSS شيوعاً. طول النص يتفاوت بناءً على اللغة وعرض نافذة العرض وإعدادات حجم خط المستخدم والمحتوى الديناميكي من قواعد البيانات. استخدم min-height إذا كنت بحاجة لبعد أدنى، واترك العنصر ينمو طبيعياً. بالمثل، فضّل max-width على width الثابت للتخطيطات المتجاوبة.

الفرق بين نموذج صندوق العناصر السطرية والكتلية

خاصية display تغير بشكل جوهري كيف يشارك العنصر في نموذج الصندوق. قيمتا العرض الأكثر أساسية -- block و inline -- تعاملان خصائص نموذج الصندوق بشكل مختلف جداً، وفهم هذه الاختلافات أمر حاسم.

العناصر الكتلية

العناصر الكتلية (مثل <div> و <p> و <h1>-<h6> و <section> و <article>) تحترم جميع خصائص نموذج الصندوق بالكامل:

  • width و height تعملان كما هو متوقع.
  • padding على جميع الجوانب الأربعة تعمل بشكل طبيعي -- تدفع المحتوى للداخل وتوسع العنصر بصرياً.
  • margin على جميع الجوانب الأربعة تعمل بشكل طبيعي -- تنشئ مسافة حول العنصر.
  • افتراضياً، العنصر الكتلي يشغل العرض المتاح بالكامل لأمه.
  • كل عنصر كتلي يبدأ على سطر جديد.

العناصر السطرية

العناصر السطرية (مثل <span> و <a> و <strong> و <em> و <code>) لها قيود كبيرة في نموذج الصندوق:

  • width و height تُتجاهلان تماماً. حجم العنصر يحدده محتواه.
  • padding-top و padding-bottom تُطبقان بصرياً (الخلفية تمتد)، لكنهما لا تدفعان المحتوى المحيط. الحشوة تتداخل مع السطور المجاورة.
  • margin-top و margin-bottom تُتجاهلان تماماً.
  • padding-left و padding-right و margin-left و margin-right تعمل بشكل طبيعي.
  • عناصر سطرية متعددة تجلس جنباً إلى جنب على نفس السطر، تتدفق مثل النص.

inline-block: أفضل ما في العالمين

قيمة display: inline-block تمنحك سلوك التدفق السطري (العناصر تجلس جنباً إلى جنب) مع سلوك نموذج الصندوق الكتلي (جميع الخصائص تعمل):

مثال: كتلي مقابل سطري مقابل سطري-كتلي

<style>
/* كتلي: جميع خصائص نموذج الصندوق تعمل، عرض كامل، سطر جديد */
.block-demo {
    display: block;
    width: 250px;
    height: 80px;
    padding: 15px;
    margin: 15px;
    border: 2px solid blue;
    background: lightblue;
}

/* سطري: العرض/الارتفاع يُتجاهلان، الهامش العمودي يُتجاهل */
.inline-demo {
    display: inline;
    width: 250px;     /* يُتجاهل */
    height: 80px;     /* يُتجاهل */
    padding: 15px;    /* الأفقية تعمل؛ العمودية تتداخل */
    margin: 15px;     /* الأفقية تعمل؛ العمودية تُتجاهل */
    border: 2px solid red;
    background: lightcoral;
}

/* سطري-كتلي: جميع الخصائص تعمل، بدون سطر جديد إجباري */
.inline-block-demo {
    display: inline-block;
    width: 250px;     /* يعمل! */
    height: 80px;     /* يعمل! */
    padding: 15px;    /* يعمل على جميع الجوانب! */
    margin: 15px;     /* يعمل على جميع الجوانب! */
    border: 2px solid green;
    background: lightgreen;
}
</style>

<div class="block-demo">كتلي</div>
<span class="inline-demo">سطري</span>
<span class="inline-block-demo">سطري-كتلي</span>

مثال: استخدام inline-block العملي

/* روابط تنقل مع inline-block */
.nav-links a {
    display: inline-block;
    padding: 10px 20px;
    margin-right: 4px;
    background: #2c3e50;
    color: white;
    text-decoration: none;
    border-radius: 4px;
    transition: background 0.2s;
}
.nav-links a:hover {
    background: #34495e;
}

/* شبكة بطاقات مع inline-block */
.card {
    display: inline-block;
    width: 30%;
    margin: 1.5%;
    padding: 20px;
    vertical-align: top; /* محاذاة أعلى البطاقات */
    border: 1px solid #ddd;
    border-radius: 8px;
    box-sizing: border-box;
}

/* مهم: عناصر inline-block تتأثر بالمسافات البيضاء
   في HTML. المسافات أو أسطر جديدة بين عناصر
   inline-block تنشئ فجوات صغيرة. الحلول:
   1. إزالة المسافات البيضاء في HTML
   2. تعيين font-size: 0 على الأم، وإعادة تعيينه على الأبناء
   3. استخدام flexbox بدلاً من ذلك (موصى به) */
ملاحظة: رغم أن inline-block لا يزال صالحاً تماماً، فإن Flexbox و CSS Grid حلّا محله إلى حد كبير لتخطيطات العناصر المتعددة. Flexbox يزيل مشكلة فجوات المسافات البيضاء ويوفر تحكماً أفضل بكثير في المحاذاة والتباعد والتوزيع. استخدم inline-block للتنسيق البسيط لمرة واحدة، و Flexbox أو Grid لتخطيطات المكونات والصفحات.

استخدام أدوات المطور لفحص نموذج الصندوق

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

خطوات فحص نموذج الصندوق بأدوات المطور

/* الخطوة 1: افتح أدوات المطور */
/* انقر بزر الفأرة الأيمن على أي عنصر -> "فحص" (أو اضغط F12 / Cmd+Opt+I) */

/* الخطوة 2: حدد عنصراً في لوحة العناصر */
/* انقر على أي عنصر في شجرة DOM */

/* الخطوة 3: ابحث عن مخطط نموذج الصندوق */
/* في Chrome: ابحث في تبويب "Computed" أو مرر لأسفل في "Styles" */
/* في Firefox: ابحث في تبويب "Layout" أو قسم "Box Model" */

/* الخطوة 4: اقرأ القيم */
/* المخطط يعرض مستطيلات متداخلة:
   - الخارجي (برتقالي): قيم الهامش
   - التالي (أصفر/زيتوني): قيم الحد
   - التالي (أخضر): قيم الحشوة
   - الداخلي (أزرق): أبعاد المحتوى (العرض × الارتفاع)
*/

/* الخطوة 5: مرر المؤشر فوق العناصر */
/* عند تمرير المؤشر فوق عنصر في لوحة العناصر،
   تبرز الصفحة:
   - منطقة المحتوى بالأزرق
   - الحشوة بالأخضر
   - الحد بالأصفر/الزيتوني
   - الهامش بالبرتقالي
   هذا التراكب البصري مفيد بشكل لا يصدق
   لفهم مشاكل التباعد. */

/* الخطوة 6: حرر القيم مباشرة */
/* يمكنك النقر على أي قيمة في مخطط نموذج الصندوق
   وتغييرها مباشرة. الصفحة تُحدّث في الوقت الفعلي.
   هذا مثالي للتجريب مع التباعد. */

/* الخطوة 7: تحقق من box-sizing المحسوبة */
/* في تبويب Computed، ابحث عن "box-sizing" لترى
   ما إذا كان العنصر يستخدم content-box أو border-box.
   هذا غالباً يفسر لماذا العنصر أعرض/أضيق
   مما توقعت. */
نصيحة: اجعلها عادة أن تفحص العناصر بأدوات المطور كلما لم يبدُ التخطيط صحيحاً. تسع مرات من أصل عشر، المشكلة متعلقة بنموذج الصندوق: حشوة غير متوقعة، انهيار هوامش، وضع box-sizing خاطئ، أو عناصر سطرية لا تستجيب للعرض/الارتفاع. تراكب نموذج الصندوق (التمييز الملون عند التمرير) سيُظهر لك فوراً من أين تأتي المساحة الزائدة.

الأخطاء الشائعة وكيفية تتبعها

الخطأ 1: العنصر أعرض من المتوقع

/* العرض: عنصرك يتجاوز حاويته */

/* السبب: استخدام content-box مع حشوة أو حد */
.problem {
    width: 100%;
    padding: 20px;
    border: 1px solid #ccc;
    /* العرض الإجمالي = 100% + 40px + 2px = يتجاوز! */
}

/* الإصلاح: التحويل إلى border-box */
.solution {
    box-sizing: border-box;
    width: 100%;
    padding: 20px;
    border: 1px solid #ccc;
    /* العرض الإجمالي = بالضبط 100%. انتهى. */
}

الخطأ 2: فجوة غامضة فوق حاوية

/* العرض: هناك فجوة فوق حاويتك لا تستطيع تفسيرها،
   حتى لو عيّنت margin: 0 عليها */

/* السبب: انهيار هوامش الأب والابن. هامش الابن العلوي
   "يتسرب عبر" الأب. */
.parent {
    background: white;
    /* لا padding-top أو border-top */
}
.first-child {
    margin-top: 30px;
    /* هذا الهامش 30px يتسرب خارج الأب! */
}

/* الإصلاح: أضف padding-top أو overflow للأم */
.parent-fixed {
    background: white;
    padding-top: 1px; /* يمنع الانهيار */
    /* أو: overflow: hidden; */
    /* أو: display: flow-root; */
}

الخطأ 3: العرض/الارتفاع لا يعملان على span أو a

/* العرض: عيّنت width وheight على <span> أو <a>
   لكن لا شيء يحدث */

/* السبب: هذه عناصر سطرية. العناصر السطرية
   تتجاهل width وheight. */
span.tag {
    width: 100px;   /* يُتجاهل! */
    height: 30px;   /* يُتجاهل! */
    padding: 5px 10px;
}

/* الإصلاح: غيّر display إلى inline-block أو block */
span.tag {
    display: inline-block; /* الآن width/height يعملان! */
    width: 100px;
    height: 30px;
    padding: 5px 10px;
}

الخطأ 4: الهوامش المتجاورة لا تُجمع

/* العرض: تتوقع 60px بين عنصرين لكن تحصل فقط على 40px */

/* السبب: انهيار الهوامش بين الأشقاء */
.element-a { margin-bottom: 40px; }
.element-b { margin-top: 20px; }
/* الفجوة = 40px (وليس 60px) -- الهامش الأكبر يفوز */

/* خيار الإصلاح 1: استخدم حشوة على عنصر واحد بدلاً من ذلك */
.element-a { margin-bottom: 0; padding-bottom: 40px; }
.element-b { margin-top: 20px; }

/* خيار الإصلاح 2: استخدم الهامش في اتجاه واحد فقط */
.element-a { margin-bottom: 40px; }
.element-b { margin-top: 0; }

/* خيار الإصلاح 3: غلّف في حاوية flex */
.wrapper {
    display: flex;
    flex-direction: column;
    /* أبناء flex لا يحدث لهم انهيار هوامش */
}

تمرين 1: استكشاف نموذج الصندوق

أنشئ صفحة HTML بأربعة عناصر <div>. أعط الأول box-sizing: content-box مع width: 300px و padding: 20px و border: 5px solid black. أعط الثاني box-sizing: border-box بنفس العرض والحشوة والحد بالضبط. قارن عروضهما المعروضة في أدوات المطور. الأول يجب أن يكون 350px إجمالي؛ والثاني يجب أن يكون 300px إجمالي. للثالث، أنشئ بطاقة متجاوبة باستخدام max-width: 400px و width: 100% و box-sizing: border-box مع حشوة وفيرة وحد، وتحقق من أنها لا تتجاوز أبداً 400px لكنها تتقلص على الشاشات الصغيرة. للرابع، أنشئ حاوية موسطة باستخدام margin: 0 auto و max-width: 800px، ثم ضع شعاراً بعرض كامل بداخلها باستخدام هوامش سالبة للخروج من حشوة الحاوية.

تمرين 2: مختبر انهيار الهوامش

ابنِ صفحة توضح الأنواع الثلاثة لانهيار الهوامش. أولاً، أنشئ عنصري <div> شقيقين حيث الأول له margin-bottom: 50px والثاني له margin-top: 30px. استخدم أدوات المطور للتحقق من أن الفجوة 50px (وليس 80px). ثانياً، أنشئ <div> أباً بلون خلفية و <div> ابناً بـ margin-top: 40px. لاحظ كيف يتسرب هامش الابن خارج الأب. ثم أصلحه باستخدام أربع طرق مختلفة: (أ) padding-top: 1px، (ب) border-top: 1px solid transparent، (ج) overflow: hidden، و(د) display: flow-root. ثالثاً، أنشئ <div> فارغاً بـ margin-top: 20px و margin-bottom: 40px وتحقق من أن مساهمته الإجمالية في التباعد هي 40px. أخيراً، غلّف جميع الأمثلة في حاوية display: flex; flex-direction: column ولاحظ أن الانهيار يتوقف.

ES
Edrees Salih
منذ 16 ساعة

We are still cooking the magic in the way!