Kodax

Z-index و Stacking Context — تصميم CSS

z-index وسياق التراكب

عندما تتداخل العناصر (بسبب position أو هوامش سالبة أو transform)، أيها يظهر فوق؟

الترتيب الافتراضي

بلا z-index: العنصر الذي يأتي لاحقاً في HTML يظهر فوق، والعناصر المُموضَعة فوق غير المُموضَعة.

z-index

.dropdown {
    position: absolute;
    z-index: 10;       /* أعلى = أقرب للمستخدم */
}

- يعمل مع العناصر المُموضَعة (relative و absolute و fixed و sticky)، ومع عناصر Flex و Grid الأبناء.

- على عنصر static لا أثر له — أول ما تفحصه عندما "لا يعمل z-index".

- يقبل أرقاماً سالبة (خلف الأب في بعض الحالات).

سياق التراكب: سبب أغلب المشاكل

بعض الخصائص تنشئ سياق تراكب جديداً: مجموعة مغلقة تُرتَّب طبقاتها داخلياً، ثم تُعامل كطبقة واحدة مع ما حولها.

<div class="header">            <!-- position: relative; z-index: 1 -->
    <div class="menu">قائمة</div> <!-- z-index: 9999 -->
</div>
<main class="content">محتوى</main> <!-- position: relative; z-index: 2 -->

القائمة ستظهر تحت المحتوى رغم 9999! لأنها محبوسة داخل سياق .header (قيمته 1)، و .content قيمته 2. لا رقم داخل الصندوق يخرجه من صندوقه.

ما الذي ينشئ سياق تراكب؟

- position غير static مع z-index غير auto.

- position: fixed أو sticky.

- opacity أقل من 1.

- transform أو filter أو perspective بأي قيمة غير none.

- عنصر Flex/Grid ابن بـ z-index غير auto.

- isolation: isolate (لإنشائه عمداً).

طريقة التشخيص

1. هل العنصر مُموضَع؟ إن لم يكن، z-index لا يعمل.

2. اصعد في الآباء: من منهم ينشئ سياق تراكب؟ (transform و opacity مشتبهان معتادان.)

3. قارن قيم z-index بين الأشقاء على نفس المستوى، لا بين عناصر في صناديق مختلفة.

4. الحل غالباً: ارفع z-index على الأب المشترك، أو انقل العنصر (النوافذ المنبثقة توضع عادة كابن مباشر لـ body).

نظام قيم ثابت

بدل أرقام عشوائية (999، 9999، 99999...) عرّف سلّماً واحداً في متغيرات:

:root {
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-overlay: 300;
    --z-modal: 400;
    --z-toast: 500;
}
.modal { position: fixed; z-index: var(--z-modal); }

isolation: isolate

لمكوّن (بطاقة مثلاً) يستخدم z-index داخلياً، isolation: isolate يحبس طبقاته داخله فلا تتصادم مع بقية الصفحة.

تمرين

أنشئ رأساً sticky وقائمة منسدلة داخله، ثم بطاقة تحته فيها transform: translateY(-4px) عند المرور. هل تختفي القائمة خلف البطاقة؟ شخّص المشكلة بالخطوات أعلاه وأصلحها.

/* نظام طبقات منظم */
:root {
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-fixed: 300;
    --z-modal-backdrop: 400;
    --z-modal: 500;
    --z-popover: 600;
    --z-tooltip: 700;
}

.dropdown-menu {
    position: absolute;
    z-index: var(--z-dropdown);
}

.sticky-header {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
}

.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: var(--z-modal-backdrop);
}

.modal {
    position: fixed;
    z-index: var(--z-modal);
}

.tooltip {
    position: absolute;
    z-index: var(--z-tooltip);
}
اختبر فهمك