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

التموضع: ثابت، نسبي، مطلق، ثابت بالنسبة للنافذة، لاصق

35 دقيقة الدرس 21 من 60

مقدمة إلى التموضع في CSS

يُعد التموضع في CSS من أكثر آليات التخطيط أهمية وقوة المتاحة لمطوري الويب. بينما تتعامل الأدوات الحديثة مثل Flexbox وGrid مع معظم مهام التخطيط، تظل خاصية position ضرورية للتحكم في كيفية وضع العناصر الفردية في المستند، وكيفية تفاعلها مع التدفق الطبيعي، وكيفية ارتباطها بكتل الاحتواء الخاصة بها. سواء كنت تبني شريط تنقل ثابت، أو تلميح أدوات يظهر بالقرب من زر، أو شريط جانبي لاصق يتبع المستخدم أثناء التمرير، أو طبقة حوار مشروط تغطي الشاشة بأكملها، فإن خاصية position هي أداتك الأساسية.

تقبل خاصية position خمس قيم: static وrelative وabsolute وfixed وsticky. كل قيمة تغير بشكل جذري كيفية وضع العنصر في المستند، وما إذا كان يبقى في التدفق الطبيعي، وما يستخدمه كنقطة مرجعية لخصائص الإزاحة، وكيف يتفاعل مع العناصر المحيطة. فهم كل من هذه القيم بعمق -- بما في ذلك الاختلافات الدقيقة بينها -- أمر بالغ الأهمية لبناء تخطيطات قوية ويمكن التنبؤ بها.

في هذا الدرس، سنستكشف كل قيمة تموضع بالتفصيل، وندرس خصائص الإزاحة (top وright وbottom وleft) التي تعمل معها، ونفهم مفهوم كتل الاحتواء، ونبني أمثلة عملية توضح أنماط الاستخدام في العالم الحقيقي.

التدفق الطبيعي و position: static

قبل أن تتمكن من فهم قيم التموضع الأخرى، تحتاج إلى فهم متين للتدفق الطبيعي للمستند. عندما يعرض المتصفح HTML، فإنه يضع العناصر على الصفحة وفقا لقواعد التدفق الطبيعي. العناصر على مستوى الكتلة مثل <div> و<p> و<h1> تتراص عموديا من أعلى إلى أسفل، كل منها يشغل العرض المتاح بالكامل. العناصر المضمنة مثل <span> و<a> و<strong> تتدفق أفقيا داخل عنصرها الأب، وتلتف إلى السطر التالي عندما ينفد المكان.

القيمة الافتراضية لخاصية position هي static. يُقال عن العنصر ذي position: static أنه "غير موضع" -- فهو يشارك بالكامل في التدفق الطبيعي. خصائص الإزاحة (top وright وbottom وleft) ليس لها أي تأثير على عنصر موضع بشكل ثابت. خاصية z-index أيضا ليس لها تأثير على العناصر الثابتة.

مثال: التموضع الثابت (السلوك الافتراضي)

<div class="container">
    <div class="box box-a">صندوق أ</div>
    <div class="box box-b">صندوق ب</div>
    <div class="box box-c">صندوق ج</div>
</div>

/* CSS */
.box {
    position: static; /* هذا هو الافتراضي -- لست بحاجة لكتابة هذا أبدا */
    top: 50px;        /* ليس له أي تأثير على عنصر ثابت */
    left: 100px;      /* ليس له أي تأثير على عنصر ثابت */
    width: 200px;
    height: 100px;
    margin-bottom: 10px;
    background-color: #3498db;
    color: white;
    padding: 20px;
}

/* النتيجة: جميع الصناديق الثلاثة تتراص عموديا في التدفق الطبيعي.
   إعلانات top و left يتم تجاهلها تماما. */
ملاحظة: نادرا ما ستكتب position: static بشكل صريح، لأنها القيمة الافتراضية. ومع ذلك، قد تستخدمها لإعادة تعيين موضع العنصر إلى الافتراضي إذا قام استعلام وسائط أو فئة بتعيينه مسبقا إلى شيء آخر.

position: relative (التموضع النسبي)

عندما تعين position: relative على عنصر، يحدث شيئان مهمان. أولا، يبقى العنصر في التدفق الطبيعي للمستند -- لا يزال يشغل مساحته الأصلية، والعناصر المحيطة تتصرف كما لو أن العنصر النسبي لم يتحرك على الإطلاق. ثانيا، يمكن الآن إزاحة العنصر من موضعه الأصلي باستخدام خصائص top وright وbottom وleft. الإزاحة تكون نسبة إلى المكان الذي كان سيكون فيه العنصر في التدفق الطبيعي.

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

مثال: التموضع النسبي

<div class="container">
    <div class="box box-a">صندوق أ</div>
    <div class="box box-b">صندوق ب (نسبي)</div>
    <div class="box box-c">صندوق ج</div>
</div>

/* CSS */
.box {
    width: 200px;
    height: 80px;
    margin-bottom: 10px;
    padding: 15px;
    background-color: #3498db;
    color: white;
}

.box-b {
    position: relative;
    top: 20px;    /* يتحرك 20 بكسل لأسفل من موضعه الطبيعي */
    left: 40px;   /* يتحرك 40 بكسل لليمين من موضعه الطبيعي */
    background-color: #e74c3c;
}

/* النتيجة: صندوق ب يتحرك بصريا 20 بكسل للأسفل و40 بكسل لليمين،
   لكن صندوق ج يبقى في نفس المكان تماما كما لو أن صندوق ب لم يتحرك.
   هناك فجوة مرئية حيث كان صندوق ب في الأصل. */

تعمل خصائص الإزاحة كالتالي للعناصر الموضعة نسبيا: top: 20px تدفع العنصر 20 بكسل للأسفل من موضعه الأصلي، left: 40px تدفعه 40 بكسل لليمين، bottom: 20px ستدفعه 20 بكسل للأعلى، وright: 40px ستدفعه 40 بكسل لليسار. إذا تم تحديد كل من top وbottom، تفوز top. إذا تم تحديد كل من left وright، يعتمد الاتجاه على وضع الكتابة -- في اللغات من اليسار إلى اليمين، تفوز left.

نصيحة: أحد أكثر استخدامات position: relative شيوعا وأهمية ليس لتحريك العنصر نفسه على الإطلاق، بل لإنشاء سياق تموضع للعناصر الأبناء الموضعة بشكل مطلق. سنستكشف هذا النمط بشكل مكثف في قسم التموضع المطلق.

إنشاء سياق التموضع

سلوك بالغ الأهمية لـ position: relative هو أنه ينشئ سياق تموضع جديد. هذا يعني أن أي عنصر سليل له position: absolute سيستخدم هذا السلف الموضع نسبيا كنقطة مرجعية له، بدلا من نافذة العرض أو سلف أبعد. هذا هو السبب الرئيسي الذي يجعل المطورين يستخدمون position: relative في الممارسة العملية.

مثال: الأب النسبي كسياق تموضع

.card {
    position: relative;   /* ينشئ سياق تموضع */
    padding: 20px;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card .badge {
    position: absolute;   /* موضع نسبة إلى .card */
    top: -10px;
    right: -10px;
    background: #e74c3c;
    color: white;
    border-radius: 50%;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
}

/* تظهر الشارة 10 بكسل فوق و10 بكسل يمين الزاوية العلوية اليمنى
   للبطاقة، مما يخلق تأثير شارة الإشعار. */

position: absolute (التموضع المطلق)

التموضع المطلق هو أحد أقوى قيم التموضع وأكثرها سوء فهم. عندما تعين position: absolute على عنصر، يتم إزالة العنصر بالكامل من التدفق الطبيعي للمستند. لم يعد يشغل أي مساحة في التخطيط، والعناصر المحيطة تتصرف كما لو أنه غير موجود. يتم بعد ذلك تموضع العنصر نسبة إلى أقرب سلف موضع -- أي أقرب سلف له قيمة position تساوي relative أو absolute أو fixed أو sticky. إذا لم يكن هناك سلف موضع، يتم تموضع العنصر نسبة إلى كتلة الاحتواء الأولية، والتي عادة ما تكون نافذة العرض.

مثال: أساسيات التموضع المطلق

<div class="parent">
    <div class="sibling">أنا عنصر شقيق</div>
    <div class="absolute-box">أنا مطلق</div>
    <div class="sibling">أنا عنصر شقيق آخر</div>
</div>

/* CSS */
.parent {
    position: relative;  /* سياق التموضع للابن المطلق */
    width: 400px;
    height: 300px;
    background: #ecf0f1;
    padding: 20px;
}

.sibling {
    background: #3498db;
    color: white;
    padding: 10px;
    margin-bottom: 10px;
}

.absolute-box {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 150px;
    padding: 15px;
    background: #e74c3c;
    color: white;
}

/* النتيجة: يتم إزالة absolute-box من التدفق. العنصران الشقيقان
   يتراصان بشكل طبيعي كما لو أن الصندوق المطلق غير موجود. يوضع
   الصندوق المطلق 20 بكسل من الأعلى و20 بكسل من الحافة اليمنى لـ .parent. */
تحذير: إذا نسيت تعيين position: relative (أو قيمة تموضع أخرى) على العنصر الأب المقصود، فإن الابن الموضع بشكل مطلق سيهرب ويموضع نفسه نسبة إلى نافذة العرض أو أقرب سلف موضع أبعد في شجرة DOM. هذا أحد أكثر أخطاء تخطيط CSS شيوعا. تأكد دائما من أنك أنشأت سياق التموضع الصحيح.

كتلة الاحتواء للعناصر المطلقة

تُحدد كتلة الاحتواء للعنصر الموضع بشكل مطلق بواسطة أقرب سلف موضع. تُحسب خصائص إزاحة العنصر (top وright وbottom وleft) نسبة إلى حواف صندوق حشوة كتلة الاحتواء هذه. هذا يعني أنه إذا كان للأب حشوة، فإن إزاحات العنصر المطلق تُقاس من الحافة الداخلية للحشوة، وليس الحد.

مثال: توضيح كتلة الاحتواء

.outer {
    position: relative;
    width: 500px;
    height: 400px;
    padding: 30px;
    border: 3px solid #333;
    background: #f0f0f0;
}

.inner-absolute {
    position: absolute;
    top: 0;
    left: 0;
    /* يجلس هذا العنصر في الزاوية العلوية اليسرى من صندوق حشوة .outer،
       وهو داخل الحد لكن في بداية منطقة الحشوة. */
    background: rgba(231, 76, 60, 0.8);
    padding: 10px;
}

.inner-absolute-bottom-right {
    position: absolute;
    bottom: 0;
    right: 0;
    /* يجلس هذا العنصر في الزاوية السفلية اليمنى من صندوق حشوة .outer. */
    background: rgba(52, 152, 219, 0.8);
    padding: 10px;
}

التوسيط باستخدام التموضع المطلق

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

مثال: التوسيط مع position: absolute

/* الطريقة 1: استخدام transform */
.centered-transform {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* الطريقة 2: استخدام inset والهوامش التلقائية */
.centered-inset {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 200px;   /* يجب أن يكون لها عرض صريح */
    height: 100px;  /* يجب أن يكون لها ارتفاع صريح */
}

/* الطريقة 3: استخدام جميع الإزاحات الأربع مع هوامش تلقائية (صيغة أقدم) */
.centered-offsets {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
    width: 200px;
    height: 100px;
}

/* مثال عملي: توسيط نافذة حوار مشروط */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-content {
    background: white;
    padding: 40px;
    border-radius: 12px;
    max-width: 500px;
    width: 90%;
}

الاستخدامات العملية للتموضع المطلق

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

مثال: تلميح أدوات بالتموضع المطلق

/* مكون تلميح الأدوات */
.tooltip-wrapper {
    position: relative;
    display: inline-block;
}

.tooltip-text {
    position: absolute;
    bottom: calc(100% + 8px);  /* فجوة 8 بكسل فوق عنصر التشغيل */
    left: 50%;
    transform: translateX(-50%);
    background: #333;
    color: white;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 14px;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s, visibility 0.2s;
    pointer-events: none;
}

/* سهم على تلميح الأدوات */
.tooltip-text::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    border-top-color: #333;
}

.tooltip-wrapper:hover .tooltip-text {
    opacity: 1;
    visibility: visible;
}

/* مثال طبقة الصورة العلوية */
.image-card {
    position: relative;
    overflow: hidden;
    border-radius: 8px;
}

.image-card img {
    display: block;
    width: 100%;
    height: auto;
}

.image-card .overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
    display: flex;
    align-items: flex-end;
    padding: 20px;
    color: white;
}

position: fixed (التموضع الثابت بالنسبة للنافذة)

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

مثال: شريط تنقل ثابت

/* شريط تنقل ثابت في الأعلى */
.navbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;       /* يمتد بالعرض الكامل */
    height: 64px;
    background: white;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
    padding: 0 24px;
    z-index: 1000;  /* يبقى فوق المحتوى الآخر */
}

/* إضافة حشوة للجسم لمنع المحتوى من الاختفاء خلف شريط التنقل الثابت */
body {
    padding-top: 64px;
}

/* زر ثابت للعودة للأعلى */
.back-to-top {
    position: fixed;
    bottom: 24px;
    right: 24px;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: #3498db;
    color: white;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    z-index: 999;
    transition: opacity 0.3s, transform 0.3s;
}

.back-to-top:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}

/* لافتة ملفات تعريف الارتباط الثابتة */
.cookie-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: #2c3e50;
    color: white;
    padding: 16px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    z-index: 2000;
}
تحذير: هناك تحذير مهم مع position: fixed. إذا كان لأي سلف من العنصر الثابت خاصية transform أو perspective أو filter معينة بقيمة غير none، فسيتم تموضع العنصر الثابت نسبة إلى ذلك السلف بدلا من نافذة العرض. هذا يمكن أن يسبب سلوكا محيرا حيث يتمرر عنصر "ثابت" مع الصفحة. تحقق دائما من العناصر السلف إذا لم يتصرف التموضع الثابت كما هو متوقع.

الفرق بين fixed و absolute

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

مثال: مقارنة بين fixed و absolute

.container {
    position: relative;
    height: 2000px;  /* حاوية طويلة لتمكين التمرير */
    padding: 20px;
    background: #f0f0f0;
}

.absolute-element {
    position: absolute;
    top: 20px;
    right: 20px;
    padding: 15px;
    background: #e74c3c;
    color: white;
    /* يبقى هذا العنصر 20 بكسل من الزاوية العلوية اليمنى لـ .container.
       عندما تمرر للأسفل، يتمرر بعيدا مع الحاوية. */
}

.fixed-element {
    position: fixed;
    top: 20px;
    right: 20px;
    padding: 15px;
    background: #3498db;
    color: white;
    /* يبقى هذا العنصر 20 بكسل من الزاوية العلوية اليمنى لنافذة العرض.
       يظل مرئيا في نفس المكان بغض النظر عن مقدار التمرير. */
}

position: sticky (التموضع اللاصق)

التموضع اللاصق هو مزيج بين التموضع النسبي والثابت، تم تقديمه لحل نمط واجهة مستخدم شائع كان يتطلب سابقا JavaScript. يتصرف العنصر اللاصق كعنصر موضع نسبيا حتى يتمرر المستخدم متجاوزا عتبة محددة، وعندها يصبح "ملتصقا" ويتصرف كعنصر ثابت ضمن حدود كتلة الاحتواء الخاصة به. بمجرد أن تتمرر كتلة الاحتواء خارج نطاق الرؤية، يتمرر العنصر اللاصق معها.

تُعرف العتبة باستخدام خصائص الإزاحة -- عادة top، لكن bottom وleft وright تعمل أيضا. قيمة top: 0 تعني أن العنصر يصبح لاصقا عندما تصل حافته العلوية إلى أعلى نافذة العرض. قيمة top: 20px تعني أنه يصبح لاصقا عندما تكون حافته العلوية على بعد 20 بكسل من أعلى نافذة العرض.

مثال: رأس لاصق

/* رؤوس أقسام لاصقة */
.section-header {
    position: sticky;
    top: 0;
    background: white;
    padding: 16px 24px;
    font-size: 18px;
    font-weight: bold;
    border-bottom: 2px solid #3498db;
    z-index: 10;
}

/* هيكل HTML */
<div class="section">
    <h2 class="section-header">القسم 1: المقدمة</h2>
    <p>محتوى القسم 1...</p>
    <p>المزيد من المحتوى...</p>
</div>
<div class="section">
    <h2 class="section-header">القسم 2: البداية</h2>
    <p>محتوى القسم 2...</p>
    <p>المزيد من المحتوى...</p>
</div>
<div class="section">
    <h2 class="section-header">القسم 3: مواضيع متقدمة</h2>
    <p>محتوى القسم 3...</p>
    <p>المزيد من المحتوى...</p>
</div>

/* كل رأس يلتصق بالأعلى أثناء التمرير عبر قسمه،
   ثم يتم دفعه بعيدا عندما يصل رأس القسم التالي. */
ملاحظة: سلوك "الالتصاق" للعنصر اللاصق مقيد بعنصره الأب. سيتوقف العنصر عن الالتصاق ويتمرر بعيدا بمجرد أن يتمرر أبوه بالكامل خارج نطاق الرؤية. هذا يعني أن العنصر الأب يجب أن يكون طويلا بما يكفي (يحتوي على محتوى كافٍ) حتى يكون السلوك اللاصق مرئيا.

متطلبات التموضع اللاصق

للتموضع اللاصق عدة متطلبات، إذا لم تتحقق، ستمنعه من العمل. فهم هذه المتطلبات يساعدك في تصحيح مشاكل التموضع اللاصق:

  • يجب تعيين خاصية إزاحة واحدة على الأقل: يجب تحديد واحدة على الأقل من top أو right أو bottom أو left. بدون إزاحة، لا يعرف المتصفح متى يُفعّل السلوك اللاصق.
  • يجب أن يحتوي الأب على محتوى قابل للتمرير: يجب أن تكون حاوية الأب طويلة بما يكفي بحيث يفيض محتواها عن نافذة العرض. إذا كان الأب بنفس ارتفاع العنصر اللاصق، فلا يوجد شيء للتمرير، وبالتالي لا شيء يلتصق.
  • لا يوجد سلف مع overflow: hidden أو auto أو scroll: إذا كان لأي سلف بين العنصر اللاصق وحاوية التمرير overflow: hidden أو overflow: auto أو overflow: scroll، فسيكون السلوك اللاصق مقيدا بمنطقة تمرير ذلك السلف، مما قد لا ينتج النتيجة المتوقعة. هذا هو السبب الأكثر شيوعا لفشل التموضع اللاصق بشكل غير متوقع.

مثال: شريط جانبي لاصق

/* تخطيط عمودين مع شريط جانبي لاصق */
.page-layout {
    display: flex;
    gap: 30px;
    align-items: flex-start;  /* مهم: يمنع الشريط الجانبي من التمدد */
}

.main-content {
    flex: 1;
    /* محتوى طويل يسبب التمرير */
}

.sidebar {
    position: sticky;
    top: 80px;     /* ارتفاع شريط التنقل 64 بكسل + فجوة 16 بكسل */
    width: 300px;
    flex-shrink: 0;
    padding: 20px;
    background: #f8f9fa;
    border-radius: 8px;
    max-height: calc(100vh - 96px);  /* ارتفاع نافذة العرض ناقص الإزاحة */
    overflow-y: auto;                /* السماح بتمرير الشريط الجانبي إذا كان المحتوى طويلا جدا */
}

/* رأس جدول لاصق */
.data-table {
    width: 100%;
    border-collapse: collapse;
}

.data-table thead th {
    position: sticky;
    top: 64px;   /* أسفل شريط التنقل الثابت */
    background: #2c3e50;
    color: white;
    padding: 12px 16px;
    text-align: left;
    z-index: 5;
}

.data-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid #eee;
}

خصائص الإزاحة: top و right و bottom و left

تحدد خصائص الإزاحة مقدار إزاحة العنصر من حافة معينة لكتلة الاحتواء الخاصة به. تؤثر فقط على العناصر الموضعة (تلك التي لها قيمة position غير static). يختلف سلوك هذه الخصائص حسب قيمة التموضع.

ملخص: كيف تعمل الإزاحات مع كل قيمة تموضع

/* STATIC: يتم تجاهل الإزاحات تماما */
.static-el {
    position: static;
    top: 50px;   /* يتم التجاهل */
    left: 100px; /* يتم التجاهل */
}

/* RELATIVE: الإزاحات تحرك العنصر من موضعه الطبيعي */
.relative-el {
    position: relative;
    top: 20px;   /* يتحرك 20 بكسل للأسفل من الموضع الطبيعي */
    left: 30px;  /* يتحرك 30 بكسل لليمين من الموضع الطبيعي */
}

/* ABSOLUTE: الإزاحات تموضع من صندوق حشوة أقرب سلف موضع */
.absolute-el {
    position: absolute;
    top: 0;      /* محاذاة لأعلى كتلة الاحتواء */
    right: 0;    /* محاذاة ليمين كتلة الاحتواء */
}

/* FIXED: الإزاحات تموضع من حواف نافذة العرض */
.fixed-el {
    position: fixed;
    bottom: 24px; /* 24 بكسل من أسفل نافذة العرض */
    right: 24px;  /* 24 بكسل من يمين نافذة العرض */
}

/* STICKY: الإزاحات تعرف عتبة الالتصاق */
.sticky-el {
    position: sticky;
    top: 0;       /* يلتصق عندما تصل الحافة العلوية لأعلى نافذة العرض */
}

خاصية الاختصار inset

خاصية inset هي اختصار لتعيين top وright وbottom وleft في وقت واحد. تتبع نفس نمط الاختصار مثل margin وpadding: قيمة واحدة تعين جميع الجوانب الأربعة، قيمتان تعينان أعلى/أسفل ويسار/يمين، ثلاث قيم تعين أعلى ويسار/يمين وأسفل، وأربع قيم تعين كل جانب على حدة بترتيب عقارب الساعة.

مثال: استخدام اختصار inset

/* inset: جميع الجوانب الأربعة */
.full-overlay {
    position: absolute;
    inset: 0;
    /* يعادل: top: 0; right: 0; bottom: 0; left: 0; */
    background: rgba(0, 0, 0, 0.5);
}

/* inset: عمودي أفقي */
.padded-overlay {
    position: absolute;
    inset: 20px 30px;
    /* يعادل: top: 20px; right: 30px; bottom: 20px; left: 30px; */
}

/* inset: أعلى أفقي أسفل */
.custom-overlay {
    position: absolute;
    inset: 10px 20px 30px;
    /* يعادل: top: 10px; right: 20px; bottom: 30px; left: 20px; */
}

/* inset: أعلى يمين أسفل يسار */
.specific-overlay {
    position: absolute;
    inset: 10px 20px 30px 40px;
    /* يعادل: top: 10px; right: 20px; bottom: 30px; left: 40px; */
}

/* عملي: خلفية نافذة حوار مشروط بملء الشاشة */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 9999;
}
نصيحة: اختصار inset مفيد بشكل خاص لإنشاء طبقات تغطية كاملة. بدلا من كتابة top: 0; right: 0; bottom: 0; left: 0; في كل مرة، يمكنك ببساطة كتابة inset: 0;. وهو مفيد أيضا للتوسيط مع تقنية margin: auto على العناصر الموضعة بشكل مطلق.

فهم كتلة الاحتواء

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

  • position: static أو relative: كتلة الاحتواء هي صندوق المحتوى لأقرب سلف على مستوى الكتلة (أو العنصر الذي ينشئ سياق تنسيق).
  • position: absolute: كتلة الاحتواء هي صندوق حشوة أقرب سلف موضع (أي سلف مع position معينة إلى relative أو absolute أو fixed أو sticky). إذا لم يوجد سلف موضع، فإن كتلة الاحتواء هي كتلة الاحتواء الأولية (نافذة العرض للوسائط المستمرة).
  • position: fixed: كتلة الاحتواء عادة هي نافذة العرض. ومع ذلك، إذا كان لسلف خاصية transform أو perspective أو filter أو contain: paint أو will-change معينة بإحدى تلك القيم، تتغير كتلة الاحتواء إلى صندوق حشوة ذلك السلف.
  • position: sticky: كتلة الاحتواء تعمل كالتموضع النسبي لأغراض التخطيط، لكن أقرب سلف قابل للتمرير يحدد "منفذ التمرير" الذي يُحسب اللاصقية مقابله.

مثال: سلسلة كتل الاحتواء

<div class="grandparent">
    <div class="parent">
        <div class="child">أين أنا موضع؟</div>
    </div>
</div>

/* السيناريو 1: فقط الجد هو الموضع */
.grandparent {
    position: relative;
    padding: 40px;
    background: #eee;
}
.parent {
    /* position: static (افتراضي) -- ليس عنصرا موضعا */
    padding: 40px;
    background: #ddd;
}
.child {
    position: absolute;
    top: 0;
    left: 0;
    /* موضع نسبة إلى .grandparent، لأن .parent هو static
       ولا يُعد سلفا موضعا. */
}

/* السيناريو 2: الأب موضع */
.grandparent {
    position: relative;
    padding: 40px;
    background: #eee;
}
.parent {
    position: relative;  /* الآن هو عنصر موضع */
    padding: 40px;
    background: #ddd;
}
.child {
    position: absolute;
    top: 0;
    left: 0;
    /* الآن موضع نسبة إلى .parent، لأن .parent هو
       أقرب سلف موضع. */
}

أنماط التموضع الشائعة

دعنا نلقي نظرة على بعض الأنماط العملية الواقعية التي تجمع بين تقنيات تموضع متعددة. تظهر هذه الأنماط بشكل متكرر في تطوير الويب الحديث وتشكل اللبنات الأساسية لمكونات واجهة المستخدم الشائعة.

مثال: قائمة منسدلة

/* مشغل وقائمة منسدلة */
.dropdown {
    position: relative;
    display: inline-block;
}

.dropdown-trigger {
    padding: 10px 20px;
    background: #3498db;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
}

.dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);   /* أسفل المشغل مباشرة مع فجوة صغيرة */
    left: 0;
    min-width: 200px;
    background: white;
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    padding: 8px 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
    z-index: 100;
}

.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-menu a {
    display: block;
    padding: 10px 16px;
    color: #333;
    text-decoration: none;
}

.dropdown-menu a:hover {
    background: #f0f0f0;
}

مثال: رأس لاصق مع إجراءات ثابتة

/* تخطيط صفحة يجمع بين التموضع الثابت واللاصق والمطلق */

/* شريط التنقل الثابت */
.main-nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 64px;
    background: white;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 24px;
}

/* إزاحة الجسم لشريط التنقل الثابت */
body {
    padding-top: 64px;
}

/* تنقل فرعي لاصق (يلتصق أسفل شريط التنقل الثابت) */
.sub-nav {
    position: sticky;
    top: 64px;       /* يلتصق أسفل شريط التنقل الثابت مباشرة */
    background: #f8f9fa;
    padding: 12px 24px;
    border-bottom: 1px solid #dee2e6;
    z-index: 999;
}

/* منطقة المحتوى مع حاوية موضعة للأبناء المطلقين */
.content-section {
    position: relative;
    padding: 40px 24px;
}

/* عنصر زخرفي مطلق */
.content-section .accent-line {
    position: absolute;
    top: 0;
    left: 0;
    width: 4px;
    height: 100%;
    background: linear-gradient(to bottom, #3498db, #2ecc71);
}

/* زر إجراء عائم ثابت */
.fab {
    position: fixed;
    bottom: 32px;
    right: 32px;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #3498db;
    color: white;
    border: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    z-index: 1001;
    font-size: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
}

تصحيح مشاكل التموضع

مشاكل التموضع هي من أكثر مشاكل تخطيط CSS شيوعا. إليك المشاكل الأكثر تكرارا وكيفية تصحيحها:

  • العنصر المطلق موضع نسبة إلى السلف الخطأ: افتح أدوات المطور وافحص العنصر. تحقق من كل سلف لمعرفة أيهم لديه قيمة position غير static. إذا كان السلف الخطأ موضعا، إما أزل تموضعه أو أضف position: relative للأب المقصود.
  • العنصر الثابت يتمرر مع الصفحة: تحقق مما إذا كان لأي سلف خاصية transform أو perspective أو filter أو will-change. أي من هذه على سلف ستكسر السلوك الثابت نسبة إلى نافذة العرض.
  • العنصر اللاصق لا يلتصق: تحقق من أنك عينت خاصية إزاحة (مثل top: 0). تحقق من أنه لا يوجد سلف مع overflow: hidden أو overflow: auto. تأكد من أن الأب طويل بما يكفي لحدوث التمرير. في أدوات المطور، يُبرز Chrome العناصر اللاصقة ويعرض قيود الالتصاق الخاصة بها.
  • العنصر يختفي عند التموضع: تحقق مما إذا كان العنصر قد انهار إلى عرض أو ارتفاع صفر. العناصر الموضعة بشكل مطلق تتقلص لتناسب محتواها افتراضيا، لذا إذا لم يكن هناك محتوى أو أبعاد صريحة، قد تكون غير مرئية. تحقق أيضا مما إذا كان العنصر خلف عنصر آخر (مشكلة z-index).
  • المحتوى المتداخل: تذكر أن العناصر المطلقة والثابتة مُزالة من التدفق. العناصر الأخرى لن تفسح المجال لها. قد تحتاج لإضافة حشوة أو هامش للعناصر المحيطة لمنع التداخل.

مثال: إصلاح شائع لعدم عمل اللاصق

/* معطل: اللاصق لا يعمل لأن الأب لديه overflow: hidden */
.broken-layout {
    overflow: hidden;  /* هذا يمنع اللاصق من العمل! */
}

.broken-layout .sticky-header {
    position: sticky;
    top: 0;
    /* لن يلتصق لأن الأب لديه overflow: hidden */
}

/* مُصلح: إزالة overflow من الأب */
.fixed-layout {
    /* لا خاصية overflow، أو overflow: visible (الافتراضي) */
}

.fixed-layout .sticky-header {
    position: sticky;
    top: 0;
    background: white;
    z-index: 10;
    /* الآن يلتصق بشكل صحيح! */
}

/* إصلاح بديل: إذا كنت تحتاج overflow على سلف،
   تأكد من أن العنصر اللاصق هو ابن مباشر لحاوية التمرير */
.scroll-container {
    overflow-y: auto;
    height: 500px;
}

.scroll-container .sticky-header {
    position: sticky;
    top: 0;
    /* يعمل لأن العنصر اللاصق داخل حاوية التمرير،
       وليس خارجها. */
}

جدول ملخص خاصية التموضع

إليك مقارنة شاملة لجميع قيم التموضع الخمس لتكون مرجعا سريعا:

مرجع سريع: جميع قيم التموضع

/*
+----------+-------------+------------------+------------------+----------+
| القيمة   | في التدفق؟  | موضع نسبة إلى   | يتمرر مع         | z-index  |
+----------+-------------+------------------+------------------+----------+
| static   | نعم         | غير متاح         | الصفحة           | لا       |
| relative | نعم         | موضعه الطبيعي    | الصفحة           | نعم      |
| absolute | لا          | أقرب سلف موضع   | كتلة الاحتواء    | نعم      |
| fixed    | لا          | نافذة العرض*     | يبقى مكانه       | نعم      |
| sticky   | نعم (حتى    | عتبة التمرير     | سلوك مختلط       | نعم      |
|          | العتبة)     |                  |                  |          |
+----------+-------------+------------------+------------------+----------+

* التموضع الثابت يستخدم نافذة العرض ما لم يكن لسلف
  transform أو perspective أو filter أو will-change معينة.
*/

/* مثال كامل يجمع بين جميع أنواع التموضع */
.page {
    position: relative;   /* سياق للأبناء المطلقين */
}

.page .background-decoration {
    position: absolute;   /* عنصر زخرفي مُزال من التدفق */
    top: 0;
    right: 0;
    width: 300px;
    height: 300px;
    opacity: 0.1;
}

.page .navbar {
    position: fixed;      /* مرئي دائما في الأعلى */
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
}

.page .sidebar-heading {
    position: sticky;     /* يلتصق أثناء تمرير القسم */
    top: 70px;
}

.page .nudged-element {
    position: relative;   /* منزاح قليلا من الموضع الطبيعي */
    top: -5px;
    left: 2px;
}

التمرين 1: بناء بطاقة منتج مع طبقات علوية

أنشئ مكون بطاقة منتج يستخدم تقنيات تموضع متعددة. يجب أن تحتوي البطاقة على صورة منتج، وفوق الصورة تحتاج إلى: شارة "تخفيض" في الزاوية العلوية اليسرى باستخدام التموضع المطلق، وأيقونة قلب (مفضلة) في الزاوية العلوية اليمنى باستخدام التموضع المطلق، وطبقة تدرج علوية في أسفل الصورة تعرض اسم المنتج باستخدام التموضع المطلق. أسفل الصورة، أضف وصف المنتج والسعر. يجب أن تحتوي البطاقة بأكملها على تأثير تمرير حيث تتكبر الصورة قليلا (استخدم transform: scale(1.05)) بينما حاوية البطاقة لديها overflow: hidden لقص الصورة المكبرة. تأكد من أن العناصر المتراكبة تبقى في مكانها أثناء انتقال التمرير. أخيرا، اعرض أربع بطاقات منتج في صف باستخدام CSS Grid أو Flexbox.

التمرين 2: تخطيط صفحة متعدد الطبقات

ابنِ تخطيط صفحة كامل يوضح جميع قيم التموضع الخمس تعمل معا. ابدأ بشريط تنقل ثابت في أعلى الصفحة (ارتفاع 64 بكسل، عرض كامل). أسفله، أنشئ تخطيط عمودين: منطقة محتوى رئيسية على اليسار وشريط جانبي لاصق على اليمين يلتصق على بعد 80 بكسل من الأعلى (لمراعاة شريط التنقل مع بعض الفجوة). داخل منطقة المحتوى الرئيسية، أنشئ عدة أقسام، كل منها مع رأس قسم لاصق يلتصق على بعد 72 بكسل من الأعلى (أسفل شريط التنقل مباشرة). أضف زر إجراء عائم في الزاوية السفلية اليمنى لنافذة العرض باستخدام التموضع الثابت. داخل أحد الأقسام، أنشئ مقارن صور "قبل/بعد" حيث يتم تموضع خط الفاصل بشكل مطلق داخل حاوية نسبية، ويمكن للمستخدم سحبه يسارا ويمينا (يمكنك استخدام نهج CSS فقط بسيط مع resize: horizontal أو وصف التفاعل بـ JavaScript). أضف تلميح أدوات يظهر عند التمرير فوق أحد رؤوس الأقسام، باستخدام التموضع المطلق داخل الرأس الموضع نسبيا. اختبر التخطيط بأكمله بالتمرير وتحقق من أن كل عنصر يتصرف بشكل صحيح: شريط التنقل يبقى ثابتا، والشريط الجانبي يلتصق، ورؤوس الأقسام تلتصق وتستبدل بعضها، وزر الإجراء العائم يبقى في الزاوية، وتلميح الأدوات يظهر بشكل صحيح.