CSS Positioning - z-index & Stacking Contexts

Learning Objectives: By the end of this lesson, you will be able to:

  • Understand how stacking contexts work in CSS and when they are created
  • Use z-index effectively to control the layering of positioned elements
  • Identify and troubleshoot common stacking context issues in complex layouts
  • Create intentional layered designs with proper element stacking

1. Welcome and Introduction (5 minutes)

Icebreaker Activity

Welcome! Think about organizing your desk or workspace - how do you decide what goes on top of what? Maybe important documents go on top, or your coffee mug sits above everything else. CSS stacking contexts work similarly - they let us control which elements appear in front of others. If you could stack any two things in real life and guarantee one always stays on top, what would you choose and why?

Lesson Overview

  • Duration: 90 minutes
  • Prerequisites: Understanding of CSS positioning (static, relative, absolute, fixed) and basic CSS properties
  • Materials Needed: Code editor, web browser with developer tools for visualizing stacking contexts

2. Learning Objectives and Session Outline (5 minutes)

What We’ll Cover Today:

  1. Stacking Contexts - Understanding the 3D layer system in CSS
  2. Z-index Property - Controlling element stacking order with numbers
  3. Stacking Context Creation - Properties that establish new stacking contexts
  4. Complex Scenarios - Debugging and managing multiple stacking contexts
  5. Real-world applications like modals, dropdowns, and overlays

Success Criteria:

By the end of this session, you should be able to create complex layered interfaces where elements stack predictably, and troubleshoot when elements don’t appear in the expected order.

3. Foundation Knowledge (15-20 minutes)

Core Concepts

Understanding the Third Dimension in CSS

While CSS primarily deals with width (x-axis) and height (y-axis), stacking contexts introduce the concept of depth (z-axis). This creates a three-dimensional space where elements can be layered on top of each other, much like a stack of papers on a desk.

Real-World Analogy: Imagine looking down at a desk with multiple documents, photos, and objects scattered on it. Some items naturally sit on top of others based on when they were placed, but you can also intentionally move specific items to the top or bottom of the stack. CSS stacking contexts work the same way.

Why This Matters: Modern web interfaces rely heavily on layered elements - modals that appear over content, dropdown menus that overlay other elements, tooltips that appear above everything else. Without understanding stacking contexts, these elements might appear behind other content or in unexpected orders.

The Natural Stacking Order

Without any CSS intervention, elements stack in a predictable order:

  1. Background and borders of root element
  2. Block-level elements in normal flow, in order of appearance
  3. Floating elements
  4. Inline elements in normal flow
  5. Positioned elements (relative, absolute, fixed, sticky)

What Creates a Stacking Context

A stacking context is created when an element has:

  • position: relative/absolute/fixed/sticky with z-index other than auto
  • opacity less than 1
  • transform property (any value except none)
  • filter property (any value except none)
  • perspective property
  • isolation: isolate
  • Several other modern CSS properties

Key Terminology

  • Stacking Context: A 3D conceptual space where elements are layered along the z-axis
  • Z-index: A CSS property that controls the stacking order of positioned elements
  • Stacking Order: The sequence in which elements are layered from back to front
  • Root Stacking Context: The main stacking context created by the HTML element
  • Local Stacking Context: A new stacking context created by certain CSS properties

4. Detailed Explanation and Examples (25-30 minutes)

Understanding Z-index

The z-index property controls the stacking order of positioned elements. It accepts integer values (positive, negative, or zero) and the keyword “auto”. Higher values appear in front of lower values, but z-index only works on positioned elements (position: relative, absolute, fixed, or sticky).

How Z-index Works

  1. Positioned Elements Only: Z-index has no effect on statically positioned elements
  2. Integer Values: Can be positive, negative, or zero
  3. Comparative Ordering: Elements with higher z-index appear in front of those with lower values
  4. Context Dependency: Z-index values are compared within the same stacking context

Common Z-index Patterns

  • Negative values (-1, -10): Push elements behind normal content
  • Zero (0): Default stacking level
  • Low positive (1-10): Slight elevation above normal content
  • Medium values (100-999): UI elements like dropdowns and tooltips
  • High values (1000+): Critical overlays like modals and notifications

Understanding Stacking Contexts

Stacking contexts create isolated layering environments. Elements within a stacking context can only be reordered relative to their siblings within that same context. This is crucial for understanding why sometimes a very high z-index doesn’t bring an element to the front.

Key Principles

  1. Isolation: Elements in different stacking contexts can’t directly compete for stacking order
  2. Inheritance: Child elements participate in their parent’s stacking context
  3. Independence: Each stacking context manages its own internal layering
  4. Hierarchy: Parent stacking contexts always contain their children’s contexts

Common Misconceptions

  1. Misconception: Higher z-index always means the element appears on top Reality: Z-index only works within the same stacking context

  2. Misconception: Only z-index creates stacking contexts Reality: Many CSS properties can create stacking contexts

  3. Misconception: Z-index values need to be sequential (1, 2, 3…) Reality: Any integer values work; many developers use increments of 10 or 100

5. Live Coding Demonstration (20-25 minutes)

Example 1: Basic Z-index Usage

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Basic Z-index Demo</title>
    <style>
        .container {
            position: relative;
            width: 400px;
            height: 300px;
            margin: 50px auto;
            border: 2px solid #333;
        }
        
        .box {
            position: absolute;
            width: 120px;
            height: 120px;
            border: 2px solid #000;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: 18px;
        }
        
        .box1 {
            background: rgba(255, 100, 100, 0.8);
            top: 50px;
            left: 50px;
            z-index: 3;
        }
        
        .box2 {
            background: rgba(100, 255, 100, 0.8);
            top: 100px;
            left: 100px;
            z-index: 1;
        }
        
        .box3 {
            background: rgba(100, 100, 255, 0.8);
            top: 150px;
            left: 150px;
            z-index: 2;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="box box1">Box 1<br>z-index: 3</div>
        <div class="box box2">Box 2<br>z-index: 1</div>
        <div class="box box3">Box 3<br>z-index: 2</div>
    </div>
</body>
</html>

Explanation:

  • Lines 29, 35, 41: Despite DOM order being 1, 2, 3, the stacking order is Box 1 (z-index: 3), Box 3 (z-index: 2), Box 2 (z-index: 1)
  • Lines 18-26: All boxes are positioned absolutely within the same stacking context
  • The transparent backgrounds let you see the layering effect clearly

Example 2: Stacking Context Creation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stacking Context Demo</title>
    <style>
        .parent {
            position: relative;
            width: 300px;
            height: 200px;
            margin: 20px;
            border: 3px solid #333;
            display: inline-block;
        }
        
        .parent1 {
            z-index: 1;
            background: rgba(255, 200, 200, 0.5);
        }
        
        .parent2 {
            z-index: 2;
            background: rgba(200, 255, 200, 0.5);
            /* This creates a new stacking context */
            opacity: 0.99;
        }
        
        .child {
            position: absolute;
            width: 100px;
            height: 80px;
            background: rgba(255, 255, 100, 0.9);
            border: 2px solid #666;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
        }
        
        .child1 {
            top: 80px;
            left: 150px;
            z-index: 999; /* Very high z-index */
        }
        
        .child2 {
            top: 50px;
            left: 120px;
            z-index: 1; /* Low z-index */
        }
    </style>
</head>
<body>
    <div class="parent parent1">
        Parent 1 (z-index: 1)
        <div class="child child1">Child 1<br>z-index: 999</div>
    </div>
    
    <div class="parent parent2">
        Parent 2 (z-index: 2)<br>opacity: 0.99
        <div class="child child2">Child 2<br>z-index: 1</div>
    </div>
</body>
</html>

Key Points:

  • Line 22: opacity: 0.99 creates a new stacking context for parent2
  • Line 38: Child1 has z-index: 999 but still appears behind Child2
  • Line 43: Child2’s parent has higher stacking context order, so Child2 appears on top
  • This demonstrates that z-index is relative to the stacking context, not global

Example 3: Real-world Modal Implementation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Modal with Proper Stacking</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
        }
        
        .page-content {
            position: relative;
            background: #f0f0f0;
            padding: 30px;
            margin-bottom: 20px;
        }
        
        .dropdown-container {
            position: relative;
            display: inline-block;
        }
        
        .dropdown-button {
            background: #3498db;
            color: white;
            border: none;
            padding: 10px 20px;
            cursor: pointer;
            border-radius: 4px;
        }
        
        .dropdown-menu {
            position: absolute;
            top: 100%;
            left: 0;
            background: white;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 100;
            min-width: 150px;
            display: none;
        }
        
        .dropdown-menu.show {
            display: block;
        }
        
        .dropdown-item {
            padding: 10px 15px;
            cursor: pointer;
            border-bottom: 1px solid #eee;
        }
        
        .dropdown-item:hover {
            background: #f5f5f5;
        }
        
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 1000;
            opacity: 0;
            visibility: hidden;
            transition: opacity 0.3s, visibility 0.3s;
        }
        
        .modal-overlay.show {
            opacity: 1;
            visibility: visible;
        }
        
        .modal-content {
            background: white;
            padding: 30px;
            border-radius: 8px;
            max-width: 500px;
            width: 90%;
            position: relative;
            transform: scale(0.9);
            transition: transform 0.3s;
        }
        
        .modal-overlay.show .modal-content {
            transform: scale(1);
        }
        
        .close-button {
            position: absolute;
            top: 10px;
            right: 15px;
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            z-index: 1;
        }
        
        .trigger-button {
            background: #e74c3c;
            color: white;
            border: none;
            padding: 12px 24px;
            cursor: pointer;
            border-radius: 4px;
            margin: 10px;
        }
    </style>
</head>
<body>
    <div class="page-content">
        <h2>Main Content Area</h2>
        <p>This content has various interactive elements with different z-index values.</p>
        
        <div class="dropdown-container">
            <button class="dropdown-button" onclick="toggleDropdown()">Dropdown Menu</button>
            <div class="dropdown-menu" id="dropdown">
                <div class="dropdown-item">Option 1</div>
                <div class="dropdown-item">Option 2</div>
                <div class="dropdown-item">Option 3</div>
            </div>
        </div>
        
        <button class="trigger-button" onclick="showModal()">Open Modal</button>
    </div>
    
    <div class="modal-overlay" id="modal">
        <div class="modal-content">
            <button class="close-button" onclick="hideModal()">&times;</button>
            <h2>Modal Dialog</h2>
            <p>This modal demonstrates proper stacking with z-index: 1000</p>
            <p>Notice how it appears above everything else, including the dropdown menu.</p>
            <button class="trigger-button" onclick="hideModal()">Close Modal</button>
        </div>
    </div>
    
    <script>
        function toggleDropdown() {
            document.getElementById('dropdown').classList.toggle('show');
        }
        
        function showModal() {
            document.getElementById('modal').classList.add('show');
        }
        
        function hideModal() {
            document.getElementById('modal').classList.remove('show');
        }
        
        // Close dropdown when clicking outside
        document.addEventListener('click', function(e) {
            if (!e.target.closest('.dropdown-container')) {
                document.getElementById('dropdown').classList.remove('show');
            }
        });
        
        // Close modal when clicking overlay
        document.getElementById('modal').addEventListener('click', function(e) {
            if (e.target === this) {
                hideModal();
            }
        });
    </script>
</body>
</html>

Debugging Common Stacking Issues

/* Problem: Z-index not working */
.not-working {
    z-index: 999;
    /* Missing position property */
}

/* Solution: Add position property */
.working {
    position: relative;
    z-index: 999;
}

/* Problem: Element with high z-index appearing behind another */
.high-z-index {
    position: absolute;
    z-index: 9999;
    /* This might still appear behind elements in different stacking contexts */
}

/* Solution: Check parent stacking contexts */
.parent-context {
    position: relative;
    z-index: 1; /* This creates a stacking context */
    /* All children are contained within this context */
}

.child-element {
    position: absolute;
    z-index: 9999; /* Only compared to siblings within parent-context */
}

6. Hands-On Practice (30-40 minutes)

Exercise 1: Basic Stacking Practice (10 minutes)

Objective: Create a card layout with overlapping elements and control their stacking order

Instructions:

  1. Create three overlapping cards with different background colors
  2. Use z-index to control their stacking order
  3. Add hover effects that bring hovered cards to the front
  4. Experiment with different z-index values

Starter Code:

<div class="card-container">
    <div class="card card1">Card 1</div>
    <div class="card card2">Card 2</div>
    <div class="card card3">Card 3</div>
</div>
.card-container {
    position: relative;
    width: 400px;
    height: 300px;
    margin: 50px auto;
}

.card {
    position: absolute;
    width: 150px;
    height: 100px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    cursor: pointer;
    transition: transform 0.3s, z-index 0.3s;
}

.card1 {
    background: #ff6b6b;
    top: 50px;
    left: 50px;
    /* Add z-index here */
}

.card2 {
    background: #4ecdc4;
    top: 80px;
    left: 120px;
    /* Add z-index here */
}

.card3 {
    background: #45b7d1;
    top: 110px;
    left: 190px;
    /* Add z-index here */
}

/* Add hover effects */

Solution:

.card1 {
    background: #ff6b6b;
    top: 50px;
    left: 50px;
    z-index: 1;
}

.card2 {
    background: #4ecdc4;
    top: 80px;
    left: 120px;
    z-index: 2;
}

.card3 {
    background: #45b7d1;
    top: 110px;
    left: 190px;
    z-index: 3;
}

.card:hover {
    transform: scale(1.05);
    z-index: 10;
}

Exercise 2: Complex Stacking Context Challenge (15 minutes)

Objective: Build a layout with multiple stacking contexts and troubleshoot stacking issues

Requirements:

  • Create two parent containers with different z-index values
  • Each parent should have child elements with their own z-index values
  • Demonstrate how children are confined to their parent’s stacking context
  • Add visual indicators to show the stacking order

Hints:

  • Use opacity or transform to create stacking contexts
  • Try giving a child a very high z-index and observe the results
  • Use browser dev tools to inspect stacking contexts

Exercise 3: Interactive Tooltip System (15 minutes)

Objective: Create a tooltip system that properly handles stacking contexts

Challenge: Build a page with multiple interactive elements that show tooltips on hover. The tooltips should:

  • Always appear above all other content
  • Work correctly even when triggered from elements in different stacking contexts
  • Have smooth animations and proper positioning

Extension Ideas:

  • Add different tooltip styles for different types of content
  • Implement click-to-show tooltips that remain visible until clicked elsewhere
  • Create a tooltip that can contain interactive content

7. Common Pitfalls and Troubleshooting (10 minutes)

Frequent Mistakes

  1. Problem: Z-index not working at all Cause: Element is not positioned (still has position: static) Solution: Add position: relative, absolute, fixed, or sticky

  2. Problem: High z-index element still appears behind another element Cause: Elements are in different stacking contexts Solution: Check parent elements for stacking context creators (opacity, transform, etc.)

  3. Problem: Unintentional stacking context creation Cause: CSS properties like opacity, transform, or filter creating new contexts Solution: Review CSS properties that create stacking contexts and use them intentionally

  4. Problem: Modal appearing behind other content Cause: Modal is placed within a container that has a lower stacking context Solution: Move modal to document root or create a higher stacking context

Debugging Tips

  • Use browser dev tools to inspect computed z-index values
  • Look for the “Layers” panel in Chrome DevTools to visualize stacking contexts
  • Temporarily add bright background colors to see element boundaries
  • Use the “isolation: isolate” property to intentionally create stacking contexts
  • Check for CSS properties that create stacking contexts when debugging unexpected behavior

8. Best Practices and Professional Tips (5 minutes)

Industry Standards

  • Z-index Scale: Use meaningful increments (10, 100, 1000) rather than arbitrary numbers
  • Documentation: Comment complex stacking arrangements for future maintainability
  • Minimal Usage: Only use z-index when necessary; prefer layout solutions when possible
  • Consistent Values: Establish a z-index scale for your project (tooltips: 100-199, modals: 1000-1099, etc.)

CSS Organization for Z-index

/* Define z-index variables for consistency */
:root {
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-fixed: 300;
    --z-modal-backdrop: 1000;
    --z-modal: 1001;
    --z-popover: 1002;
    --z-tooltip: 1003;
}

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

.tooltip {
    z-index: var(--z-tooltip);
}

Next Steps in Your Learning Journey

  • Study CSS Grid and Flexbox for modern layout alternatives
  • Learn about CSS transforms and their effect on stacking contexts
  • Explore CSS animations and how they interact with z-index
  • Practice building complex UI components like multi-level dropdowns

9. Summary and Review (5 minutes)

Key Takeaways

  1. Z-index only works on positioned elements (relative, absolute, fixed, sticky)
  2. Stacking contexts isolate z-index comparisons to elements within the same context
  3. Many CSS properties create stacking contexts, not just z-index
  4. Higher z-index doesn’t always mean “on top” if elements are in different stacking contexts

Quick Review Questions

  1. What CSS properties are required for z-index to work?
  2. Name three CSS properties (other than z-index) that create stacking contexts.
  3. If element A has z-index: 1000 and element B has z-index: 1, which will appear on top?

Answers:

  1. Any position value except static (relative, absolute, fixed, sticky)
  2. opacity < 1, transform (any value), filter (any value), perspective, isolation: isolate
  3. It depends on their stacking contexts - if they’re in the same context, A will be on top. If they’re in different contexts, the context with higher stacking order wins.

For Instructors

Teaching Tips

  • Use visual demonstrations with browser dev tools to show stacking contexts
  • Create intentional “broken” examples where z-index doesn’t work as expected, then explain why
  • Emphasize the relationship between parent stacking contexts and child elements
  • Use physical analogies (deck of cards, stack of papers) to explain concepts

Assessment Ideas

  • Quick Quiz: Z-index requirements and stacking context creation
  • Debugging Exercise: Provide broken stacking examples for students to fix
  • Design Challenge: Build a complex interface with proper layering (modal + dropdown + tooltips)

Adaptation Notes

  • For Beginners: Focus more on basic z-index usage before introducing stacking contexts
  • For Advanced Students: Include performance implications and modern CSS alternatives
  • Online Delivery: Use screen sharing with dev tools to clearly show stacking visualization
  • Time Management: Adjust exercise complexity based on student comprehension of basic concepts