Z-Index وسياقات التكديس
مقدمة في التكديس و Z-Index
عندما تبني صفحات ويب، لا توجد العناصر في بُعدين فقط (أفقي وعمودي). فهي موجودة أيضًا على محور ثالث -- محور z -- الذي يتحكم في أي العناصر تظهر أمام أو خلف عناصر أخرى. خاصية z-index هي أداة CSS الأساسية للتحكم في ترتيب التكديس، لكنها واحدة من أكثر الخصائص سوء فهم في CSS بأكمله. يواجه العديد من المطورين سلوكًا غير متوقع مع z-index، ويكتبون قيمًا مثل z-index: 99999 بإحباط، ولا يفهمون أبدًا بشكل كامل لماذا لا تتكدس عناصرهم بشكل صحيح.
جذر الالتباس هو مفهوم سياقات التكديس. سياق التكديس هو تصور ثلاثي الأبعاد لعناصر HTML على طول محور z خيالي. يتم تكديس العناصر داخل سياق التكديس وفقًا لقواعد محددة، والأهم أن سياق التكديس يُشكل وحدة مستقلة. لا يمكن للعناصر داخل سياق التكديس الظهور أمام أو خلف عناصر في سياق تكديس شقيق، بغض النظر عن قيم z-index الخاصة بها. فهم سياقات التكديس هو مفتاح إتقان z-index.
في هذا الدرس، سنغطي كيف يعمل ترتيب التكديس الافتراضي بدون z-index، وكيف يتحكم z-index في التكديس على العناصر المُعينة الموضع، وما الذي يُنشئ سياقات التكديس ولماذا تهم، والخصائص الشائعة التي تُنشئ سياقات تكديس بشكل غير متوقع، ولماذا z-index: 9999 هو علامة على كود سيئ، وكيفية إدارة قيم z-index بمتغيرات CSS المخصصة، وخاصية isolation: isolate، وكيفية تصحيح مشاكل التكديس باستخدام أدوات المطور، وأنماط عملية للنوافذ المنبثقة والقوائم المنسدلة وتلميحات الأدوات.
ترتيب التكديس الافتراضي (بدون Z-Index)
قبل أن نفكر حتى في z-index، من المهم فهم كيف تُكدس المتصفحات العناصر بشكل طبيعي عندما لا يتم تحديد تكديس صريح. يتبع المتصفح ترتيب رسم محدد معرّف في مواصفات CSS. العناصر المرسومة لاحقًا تظهر فوق العناصر المرسومة سابقًا.
ترتيب التكديس الافتراضي من الخلف إلى الأمام هو:
- الخلفية والحدود للعنصر الجذر (عنصر
<html>) - عناصر الكتلة غير المُعينة الموضع بالترتيب الذي تظهر فيه في HTML (التدفق الطبيعي)
- العناصر العائمة -- تُرسم فوق عناصر الكتلة غير المُعينة الموضع
- العناصر السطرية -- المحتوى السطري يُرسم فوق العوامات
- العناصر المُعينة الموضع مع
position: relativeأوabsoluteأوfixedأوsticky(وz-index: auto) -- بالترتيب الذي تظهر فيه في HTML
مثال على ترتيب التكديس الافتراضي
<div class="container">
<div class="block-one">عنصر كتلة 1</div>
<div class="block-two">عنصر كتلة 2</div>
<div class="positioned">عنصر مُعيّن الموضع</div>
<div class="floated">عنصر عائم</div>
</div>
/* CSS */
.block-one {
background: lightcoral;
/* بدون موضع -- تدفق طبيعي، يُرسم أولًا */
}
.block-two {
background: lightblue;
margin-top: -20px;
/* بدون موضع -- لا يزال تدفق طبيعي، يُرسم بعد block-one */
/* لذا block-two يظهر فوق block-one */
}
.positioned {
position: relative;
top: -40px;
background: lightgreen;
/* مُعيّن الموضع -- يُرسم فوق جميع العناصر غير المُعينة الموضع */
}
.floated {
float: left;
background: lightyellow;
margin-top: -60px;
/* عائم -- يُرسم فوق كتل غير مُعينة الموضع، تحت المُعينة */
}
position: relative إلى عنصر (حتى بدون أي قيم إزاحة) يمكن أن يُغير تكديسه البصري.Z-Index: التحكم في ترتيب التكديس
تسمح لك خاصية z-index بالتحكم صراحة في ترتيب تكديس العناصر. ومع ذلك، هناك قاعدة حاسمة تُربك العديد من المطورين: z-index يعمل فقط على العناصر المُعينة الموضع. يجب أن يكون لعنصر ما خاصية position مُعينة إلى relative أو absolute أو fixed أو sticky حتى يكون لـ z-index أي تأثير. على عنصر position: static (الافتراضي)، يتم تجاهل خاصية z-index تمامًا.
الاستخدام الأساسي لـ Z-Index
/* z-index ليس له تأثير على العناصر الثابتة */
.static-element {
position: static; /* الافتراضي */
z-index: 100; /* يُتجاهل -- ليس له تأثير */
}
/* z-index يعمل على العناصر المُعينة الموضع */
.relative-element {
position: relative;
z-index: 10; /* يعمل -- العنصر يتكدس فوق z-index: auto */
}
.absolute-element {
position: absolute;
z-index: 5; /* يعمل -- يتكدس تحت العنصر النسبي أعلاه */
}
.fixed-element {
position: fixed;
z-index: 20; /* يعمل -- يتكدس فوق كلا العنصرين أعلاه */
}
.sticky-element {
position: sticky;
top: 0;
z-index: 15; /* يعمل -- يتكدس بين الثابت والنسبي */
}
قيم Z-Index: موجبة وسالبة و auto
تقبل خاصية z-index قيمًا صحيحة (موجبة أو سالبة أو صفر) والكلمة المفتاحية auto. فهم الفرق بين هذه القيم ضروري لسلوك تكديس قابل للتنبؤ.
أنواع قيم Z-Index
/* auto -- القيمة الافتراضية */
.auto-z {
position: relative;
z-index: auto;
/* لا يُنشئ سياق تكديس جديد */
/* العنصر يشارك في سياق تكديس أبيه */
}
/* z-index موجب -- يتكدس فوق عناصر z-index: auto و z-index: 0 */
.positive-z {
position: relative;
z-index: 10;
/* يُنشئ سياق تكديس جديد */
/* القيم الموجبة الأعلى تتكدس فوق القيم الأقل */
}
/* z-index: 0 -- نفس الطبقة المرئية كـ auto، لكن يُنشئ سياق تكديس */
.zero-z {
position: relative;
z-index: 0;
/* مهم: على عكس auto، z-index: 0 يُنشئ سياق تكديس جديد */
}
/* z-index سالب -- يتكدس خلف خلفية العنصر الأب */
.negative-z {
position: relative;
z-index: -1;
/* يُنشئ سياق تكديس جديد */
/* يُعرض خلف خلفية ومحتوى الأب */
}
/* عنصر خلف الأب */
.behind-parent {
position: absolute;
z-index: -1;
/* هذا العنصر يظهر خلف أبيه المُعيّن الموضع */
/* مفيد للخلفيات الزخرفية والعناصر الزائفة */
}
z-index: auto و z-index: 0. بينما ينتجان نفس موقع التكديس المرئي، فإن z-index: 0 يُنشئ سياق تكديس جديد بينما z-index: auto لا يفعل ذلك. هذا التمييز مهم بشكل كبير عندما يكون لديك عناصر متداخلة بقيم z-index خاصة بها. عند الشك، فضّل z-index: auto (أو احذف z-index تمامًا) ما لم تكن بحاجة تحديدًا إلى سياق تكديس جديد.سياقات التكديس: مفتاح فهم Z-Index
سياق التكديس هو تجميع هرمي للعناصر على طول محور z. فكر فيه كـ "مجموعة طبقات" -- جميع العناصر داخل سياق التكديس تتكدس نسبة لبعضها البعض، ثم يتم تكديس المجموعة بأكملها كوحدة نسبة لسياقات التكديس الأخرى. هذا هو المفهوم الأهم لفهم لماذا لا يعمل z-index أحيانًا كما هو متوقع.
العنصر الجذر (<html>) يُنشئ دائمًا سياق التكديس الأولي. أي عنصر يُنشئ سياق تكديس جديد يصبح "جذر" عالم تكديس مصغر خاص به. العناصر داخل هذا السياق يمكنها فقط التنافس على موقع التكديس مع عناصر أخرى في نفس السياق -- لا يمكنها الهروب من سياق تكديس أبيها، بغض النظر عن مدى ارتفاع z-index الخاص بها.
عرض سياق التكديس
<!-- HTML -->
<div class="parent-a">
<div class="child-a">الابن أ (z-index: 999)</div>
</div>
<div class="parent-b">
<div class="child-b">الابن ب (z-index: 1)</div>
</div>
/* CSS */
.parent-a {
position: relative;
z-index: 1; /* يُنشئ سياق تكديس بقيمة z-index 1 */
background: lightcoral;
padding: 20px;
}
.child-a {
position: relative;
z-index: 999; /* z-index عالي، لكنه محبوس في سياق parent-a */
background: red;
padding: 10px;
}
.parent-b {
position: relative;
z-index: 2; /* يُنشئ سياق تكديس بقيمة z-index 2 */
background: lightblue;
padding: 20px;
margin-top: -30px;
}
.child-b {
position: relative;
z-index: 1; /* z-index منخفض، لكنه في سياق parent-b */
background: blue;
color: white;
padding: 10px;
}
/* النتيجة: child-b (z-index: 1) يظهر فوق child-a (z-index: 999)!
لماذا؟ لأن parent-b (z-index: 2) يتكدس فوق parent-a (z-index: 1).
سياق تكديس parent-b بأكمله، بما في ذلك جميع أبنائه،
يكون فوق سياق تكديس parent-a بأكمله.
z-index: 999 الخاص بـ child-a يهم فقط داخل parent-a. */
z-index: 9999 لا يظهر في الأعلى، فالمشكلة تكاد تكون بالتأكيد أن أباه (أو سلفه) قد أنشأ سياق تكديس بقيمة z-index أقل من سياق تكديس شقيق. الحل ليس أبدًا زيادة z-index أكثر -- بل هو فهم وإعادة هيكلة سياقات التكديس. ابحث صعودًا في شجرة DOM لإيجاد أي سلف يُنشئ سياق التكديس المُسبب للمشكلة.ما الذي يُنشئ سياق تكديس
تُنشئ العديد من خصائص CSS سياقات تكديس جديدة، وبعضها ليس واضحًا على الإطلاق. هذا مصدر رئيسي آخر لسلوك z-index غير المتوقع. قد تُضيف خاصية CSS تبدو بريئة إلى عنصر وتجد فجأة أن ترتيب تكديس أحفاده قد تغير.
المُحفزات الكلاسيكية
محفزات سياق التكديس الكلاسيكية
/* 1. العنصر الجذر -- يُنشئ دائمًا سياق التكديس الأولي */
html {
/* هذا هو سياق تكديس تلقائيًا */
}
/* 2. عنصر مُعيّن الموضع مع z-index غير auto */
.positioned-with-z {
position: relative; /* أو absolute أو fixed أو sticky */
z-index: 1; /* أي قيمة صحيحة، بما في ذلك 0 */
/* يُنشئ سياق تكديس */
}
/* 3. عنصر مُعيّن الموضع مع z-index: auto لا يُنشئ واحدًا */
.positioned-without-z {
position: relative;
z-index: auto; /* الافتراضي -- لا سياق تكديس */
}
الخصائص التي تُنشئ سياقات تكديس بشكل غير متوقع
تُنشئ خصائص CSS التالية سياقات تكديس حتى بدون تعيين أي position أو z-index. هذه هي منشئات سياق التكديس "الصامتة" التي غالبًا ما تسبب أخطاء z-index غامضة.
محفزات سياق التكديس غير المتوقعة
/* opacity أقل من 1 */
.transparent {
opacity: 0.99;
/* يُنشئ سياق تكديس! حتى 0.99 تُفعّله. */
/* لهذا السبب إضافة opacity إلى أب يمكن أن تكسر z-index للأبناء */
}
/* transform (أي قيمة غير none) */
.transformed {
transform: translateX(0);
/* يُنشئ سياق تكديس! حتى تحويل الهوية مثل هذا */
}
/* filter (أي قيمة غير none) */
.filtered {
filter: blur(0px);
/* يُنشئ سياق تكديس! */
}
.brightness {
filter: brightness(1);
/* يُنشئ سياق تكديس! حتى لو لم يُغير شيئًا بصريًا */
}
/* backdrop-filter */
.backdrop {
backdrop-filter: blur(10px);
/* يُنشئ سياق تكديس */
}
/* mix-blend-mode (أي قيمة غير normal) */
.blended {
mix-blend-mode: multiply;
/* يُنشئ سياق تكديس */
}
/* isolation: isolate */
.isolated {
isolation: isolate;
/* يُنشئ سياق تكديس -- هذا هو غرضه بالكامل */
}
/* will-change */
.optimized {
will-change: transform;
/* يُنشئ سياق تكديس حتى قبل تطبيق transform */
}
/* position: fixed أو sticky */
.fixed {
position: fixed;
/* يُنشئ دائمًا سياق تكديس، حتى بدون z-index */
}
.sticky {
position: sticky;
top: 0;
/* يُنشئ دائمًا سياق تكديس، حتى بدون z-index */
}
/* contain: layout أو paint أو strict */
.contained {
contain: paint;
/* يُنشئ سياق تكديس */
}
/* clip-path و mask وغيرها أيضًا تُنشئ سياقات تكديس */
.clipped {
clip-path: circle(50%);
/* يُنشئ سياق تكديس */
}
transform و opacity (أقل من 1) و filter. إذا أضفت transform: translateY(-5px) إلى تأثير تمرير بطاقة، فإن هذه البطاقة الآن تُنشئ سياق تكديس. أي عناصر ابن بـ z-index سالب كانت تُعرض سابقًا خلف البطاقة ستكون الآن محبوسة داخل سياق تكديس البطاقة. وبالمثل، إضافة opacity: 0.99 أو filter: drop-shadow() ستُنشئ سياقات تكديس وربما تكسر تسلسل z-index الخاص بك.ترتيب التكديس داخل السياق
داخل سياق تكديس واحد، تُرسم العناصر بالترتيب التالي من الخلف إلى الأمام. فهم هذا الترتيب يُفسر العديد من سلوكيات التكديس التي تبدو غامضة للوهلة الأولى.
ترتيب التكديس الكامل داخل السياق
/*
ترتيب الرسم داخل سياق تكديس (من الخلف للأمام):
1. خلفية وحدود جذر سياق التكديس
2. العناصر الابن بـ z-index سالب (الأكثر سلبية أولًا)
3. عناصر الكتلة غير المُعينة الموضع وغير العائمة (بترتيب DOM)
4. العناصر العائمة (بترتيب DOM)
5. العناصر السطرية (بترتيب DOM)
6. العناصر المُعينة الموضع بـ z-index: auto أو z-index: 0 (بترتيب DOM)
7. العناصر الابن بـ z-index موجب (الأقل أولًا)
*/
/* عرض عملي */
.stacking-context-root {
position: relative;
z-index: 0; /* يُنشئ سياق تكديس */
background: #f0f0f0;
padding: 40px;
}
/* الطبقة 1 (خلف كل شيء): z-index سالب */
.background-decoration {
position: absolute;
z-index: -1;
background: rgba(52, 152, 219, 0.1);
top: 10px;
left: 10px;
right: 10px;
bottom: 10px;
border-radius: 8px;
}
/* الطبقة 2: كتلة غير مُعينة الموضع */
.normal-block {
background: lightcoral;
padding: 10px;
}
/* الطبقة 3: عنصر عائم */
.floated-box {
float: left;
width: 100px;
height: 100px;
background: lightyellow;
}
/* الطبقة 4: مُعيّن الموضع بـ z-index: auto */
.positioned-auto {
position: relative;
background: lightgreen;
padding: 10px;
}
/* الطبقة 5: z-index موجب (منخفض) */
.low-z {
position: relative;
z-index: 1;
background: lightskyblue;
padding: 10px;
}
/* الطبقة 6: z-index موجب (عالي) */
.high-z {
position: relative;
z-index: 10;
background: plum;
padding: 10px;
}
لماذا z-index: 9999 هو علامة على كود سيئ
من المحتمل أنك رأيت قواعد كود CSS مليئة بقيم مثل z-index: 9999 أو z-index: 99999 أو حتى z-index: 2147483647 (أقصى عدد صحيح 32 بت). هذه دائمًا تقريبًا أعراض عدم فهم سياقات التكديس، وتُنشئ كابوس صيانة.
سباق تسلح Z-Index (نمط مضاد)
/* المشكلة: سباق تسلح z-index */
.header {
position: sticky;
top: 0;
z-index: 100; /* "عالي كفاية" للرأس */
}
.dropdown {
position: absolute;
z-index: 200; /* يحتاج أن يكون فوق الرأس، فـ 200 */
}
.modal-overlay {
position: fixed;
z-index: 999; /* يحتاج أن يكون فوق كل شيء، فـ 999 */
}
.modal {
position: fixed;
z-index: 1000; /* يجب أن يكون فوق الطبقة */
}
.toast-notification {
position: fixed;
z-index: 9999; /* يجب أن يكون فوق النافذة المنبثقة! */
}
.tooltip {
position: absolute;
z-index: 99999; /* يجب أن يكون فوق كل شيء! */
}
/* بعد ستة أشهر، يُضيف شخص ما: */
.cookie-banner {
position: fixed;
z-index: 999999; /* يجب أن يكون فوق التلميح... */
}
/* هذا غير قابل للصيانة تمامًا! */
إدارة Z-Index بمتغيرات CSS المخصصة
حل سباق تسلح z-index هو تعريف مقياس z-index واضح وموثق باستخدام متغيرات CSS المخصصة. يُنشئ هذا مصدرًا واحدًا للحقيقة لجميع قيم z-index في مشروعك.
مقياس Z-Index بمتغيرات CSS المخصصة
/* تعريف مقياس z-index واضح */
:root {
--z-below: -1;
--z-normal: 0;
--z-dropdown: 10;
--z-sticky: 20;
--z-fixed: 30;
--z-overlay: 40;
--z-modal: 50;
--z-popover: 60;
--z-tooltip: 70;
--z-toast: 80;
--z-max: 90;
}
/* استخدم المقياس بشكل متسق */
.header {
position: sticky;
top: 0;
z-index: var(--z-sticky);
}
.dropdown-menu {
position: absolute;
z-index: var(--z-dropdown);
}
.modal-overlay {
position: fixed;
z-index: var(--z-overlay);
}
.modal-dialog {
position: fixed;
z-index: var(--z-modal);
}
.tooltip {
position: absolute;
z-index: var(--z-tooltip);
}
.toast {
position: fixed;
z-index: var(--z-toast);
}
/* الفوائد:
1. كل z-index في المشروع موثق في مكان واحد
2. التسلسل واضح: التلميحات > النوافذ المنبثقة > القوائم المنسدلة > الرؤوس
3. لا مزيد من التخمين -- فقط تحقق من المقياس
4. سهل التعديل: غيّر متغيرًا واحدًا، يتحدث كل شيء
5. الفجوات بين القيم (10، 20، 30) تسمح بإدراج طبقات جديدة لاحقًا
*/
isolation: isolate -- أداة سياق التكديس
تم تقديم خاصية isolation خصيصًا لمنح المطورين طريقة نظيفة لإنشاء سياقات تكديس جديدة بدون أي آثار جانبية بصرية. غرضها الوحيد هو إنشاء سياق تكديس. على عكس استخدام position: relative; z-index: 0 أو opacity: 0.99 أو transform: translateZ(0) (جميعها حيل شائعة لإنشاء سياقات تكديس)، تفعل isolation: isolate شيئًا واحدًا فقط: تُنشئ سياق تكديس جديد بدون أي تأثيرات أخرى على التخطيط أو المظهر أو الأداء.
استخدام isolation: isolate
/* المشكلة: ابن بـ z-index: -1 يهرب من أبيه */
.card {
position: relative;
background: white;
/* z-index: auto (الافتراضي) -- ليس سياق تكديس */
}
.card-decoration {
position: absolute;
z-index: -1;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 16px;
/* خلل: هذا يذهب خلف أب الأب، وليس فقط البطاقة! */
}
/* الحل 1: الحيلة القديمة -- position + z-index: 0 */
.card-fix-old {
position: relative;
z-index: 0; /* يُنشئ سياق تكديس، لكنه أيضًا يُعيّن z-index إلى 0 */
}
/* الحل 2: النهج النظيف -- isolation: isolate */
.card-fix-clean {
isolation: isolate;
/* يُنشئ سياق تكديس بدون آثار جانبية!
لا حاجة لـ position: relative
لا يتم تعيين قيمة z-index
لا تغييرات بصرية
z-index: -1 الخاص بالابن يبقى الآن خلف البطاقة */
}
/* مثال واقعي: عزل المكونات */
.widget {
isolation: isolate;
/* جميع قيم z-index داخل هذا المكون محتواة الآن.
لا يمكنها التداخل مع عناصر خارج المكون.
ولا يمكن لقيم z-index الخارجية الوصول إلى الداخل أيضًا. */
}
isolation: isolate هي الطريقة الموصى بها لإنشاء سياق تكديس عندما تحتاج واحدًا لأغراض الاحتواء فقط. إنها واضحة دلاليًا (أي شخص يقرأ الكود يفهم أنك تُنشئ حدود عزل عمدًا)، وليس لها آثار جانبية، وتعمل بدون الحاجة لتعيين موضع أو z-index. استخدمها بسخاء في بنيات قائمة على المكونات لمنع تسرب z-index بين المكونات.تصحيح التكديس باستخدام أدوات المطور
عندما تواجه مشاكل z-index، فإن أدوات المطور في المتصفح هي أفضل صديق لك. تقدم كل من أدوات Chrome و Firefox أدوات لفحص وتصور سياقات التكديس.
خطوات وتقنيات التصحيح
/* الخطوة 1: حدد العناصر المعنية */
/* في أدوات المطور، افحص كلا العنصرين: الذي تريده في الأعلى
والذي يحجبه */
/* الخطوة 2: تحقق مما إذا كان z-index مُطبقًا */
/* في علامة التبويب Computed، ابحث عن:
- position: هل هو أي شيء غير static؟
- z-index: هل القيمة المحسوبة ما تتوقعه؟
- إذا كان الموضع static، سيُظهر z-index دائمًا "auto" */
/* الخطوة 3: اعثر على حدود سياق التكديس */
/* امشِ صعودًا في شجرة DOM من العنصر المُشكل.
لكل سلف، تحقق مما إذا كان يُنشئ سياق تكديس.
ابحث عن هذه الخصائص في علامة التبويب Computed:
- position + z-index (ليس auto)
- opacity < 1
- transform (ليس none)
- filter (ليس none)
- isolation: isolate
- will-change: transform/opacity/filter
- position: fixed أو sticky
- mix-blend-mode (ليس normal) */
/* الخطوة 4: تصور التسلسل */
/* يمكنك إضافة خطوط خارجية مؤقتة لرؤية حدود سياق التكديس: */
.debug-stacking-context {
outline: 3px solid red !important;
}
/* الخطوة 5: استخدم عرض Firefox ثلاثي الأبعاد */
/* لدى أدوات المطور في Firefox عرض ثلاثي أبعاد فريد
يُظهر ترتيب التكديس بصريًا */
/* الخطوة 6: الإصلاحات الشائعة */
/* الإصلاح أ: أزل سياق التكديس العرضي من السلف */
.ancestor-that-breaks-things {
/* أزل أو عدّل: */
/* transform: none; */
/* opacity: 1; */
/* filter: none; */
}
/* الإصلاح ب: أنشئ سياق تكديس مقصود على العنصر الصحيح */
.component-root {
isolation: isolate;
}
/* الإصلاح ج: انقل العنصر أعلى في تسلسل DOM */
/* إذا كان عنصر يحتاج أن يكون فوق كل شيء، فكر
في نقله ليكون ابنًا مباشرًا لـ body */
أمثلة عملية: أنماط تكديس واقعية
الآن دعنا نطبق كل ما تعلمناه لبناء أنماط واجهة مستخدم شائعة تعتمد على إدارة z-index صحيحة.
نمط النافذة المنبثقة / مربع الحوار
نافذة منبثقة كاملة مع تكديس صحيح
<!-- HTML: النافذة المنبثقة في المستوى الأعلى من body لتجنب مشاكل التكديس -->
<body>
<header class="site-header">
<nav>التنقل</nav>
</header>
<main class="site-content">
<div class="card-with-transform">
<!-- يُنشئ سياق تكديس بسبب transform -->
</div>
</main>
<!-- النافذة المنبثقة شقيقة للرأس و main، وليست متداخلة بداخلهما -->
<div class="modal-overlay" id="modal">
<div class="modal-dialog">
<div class="modal-header">
<h2>عنوان النافذة المنبثقة</h2>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>محتوى النافذة المنبثقة يوضع هنا.</p>
</div>
</div>
</div>
</body>
/* CSS */
:root {
--z-header: 20;
--z-overlay: 40;
--z-modal: 50;
}
.site-header {
position: sticky;
top: 0;
z-index: var(--z-header);
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: var(--z-overlay);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal-dialog {
background: white;
border-radius: 12px;
padding: 24px;
max-width: 500px;
width: 90%;
z-index: var(--z-modal);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
نمط القائمة المنسدلة
قائمة منسدلة مع تكديس صحيح
<nav class="navigation">
<div class="nav-item has-dropdown">
<button class="nav-link">المنتجات</button>
<div class="dropdown-menu">
<a href="#" class="dropdown-item">المنتج أ</a>
<a href="#" class="dropdown-item">المنتج ب</a>
<a href="#" class="dropdown-item">المنتج ج</a>
</div>
</div>
</nav>
/* CSS */
.navigation {
position: sticky;
top: 0;
z-index: var(--z-sticky);
background: white;
display: flex;
gap: 4px;
padding: 0 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.nav-item {
position: relative;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background: white;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
padding: 8px 0;
z-index: var(--z-dropdown);
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition: all 0.2s ease;
}
.nav-item:hover .dropdown-menu,
.nav-item:focus-within .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.dropdown-item {
display: block;
padding: 10px 16px;
color: #333;
text-decoration: none;
transition: background-color 0.15s ease;
}
.dropdown-item:hover {
background: #f5f5f5;
}
نمط تلميح الأدوات
تلميح أدوات مع وعي بسياق التكديس
/* حاوية التلميح */
.tooltip-wrapper {
position: relative;
display: inline-block;
}
/* محتوى التلميح */
.tooltip-content {
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 8px 14px;
border-radius: 6px;
font-size: 13px;
white-space: nowrap;
z-index: var(--z-tooltip);
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
pointer-events: none;
}
/* سهم التلميح */
.tooltip-content::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
}
/* إظهار عند التمرير والتركيز */
.tooltip-wrapper:hover .tooltip-content,
.tooltip-wrapper:focus-within .tooltip-content {
opacity: 1;
visibility: visible;
}
/* تنوع التلميح -- يمين */
.tooltip-content.tooltip-right {
bottom: auto;
left: calc(100% + 8px);
top: 50%;
transform: translateY(-50%);
}
.tooltip-content.tooltip-right::after {
top: 50%;
left: auto;
right: 100%;
transform: translateY(-50%);
border: 6px solid transparent;
border-right-color: #333;
}
رأس لاصق مع قائمة منسدلة وإشعارات Toast
نظام تكديس كامل
/* مقياس z-index */
:root {
--z-below: -1;
--z-normal: 0;
--z-dropdown: 10;
--z-sticky: 20;
--z-fixed: 30;
--z-overlay: 40;
--z-modal: 50;
--z-popover: 60;
--z-tooltip: 70;
--z-toast: 80;
}
/* رأس لاصق -- يبقى فوق المحتوى المتمرر */
.site-header {
position: sticky;
top: 0;
z-index: var(--z-sticky);
background: white;
}
/* بطاقات المحتوى التي قد يكون لها transforms */
.content-card {
position: relative;
isolation: isolate; /* احتواء جميع z-index داخل البطاقة */
}
.content-card:hover {
transform: translateY(-4px);
/* بسبب isolation: isolate على البطاقة،
هذا التحويل لا يؤثر على التكديس الخارجي */
}
/* إشعارات Toast -- دائمًا في الأعلى */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: var(--z-toast);
display: flex;
flex-direction: column;
gap: 8px;
pointer-events: none;
}
.toast {
background: #333;
color: white;
padding: 14px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
pointer-events: auto;
animation: toast-in 0.3s ease-out;
}
@keyframes toast-in {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
isolation: isolate على بطاقات المحتوى لمنع transforms من التداخل مع قائمة الرأس المنسدلة.
z-index: 9999 والابن الثاني z-index: 1. ثم أعطِ الحاويتين الأبويتين قيم z-index 1 و 2 على التوالي، مما يُظهر أن الابن بـ z-index 9999 لا يزال يظهر خلف الابن بـ z-index 1 لأن أباه لديه سياق تكديس أقل. أضف تعليقات توضيحية تشرح لماذا يحدث هذا.