HTML & CSS: The Document ModelThe CascadeLesson 8 of 18

Display and Positioning

The display property controls how an element participates in layout. The position property removes elements from normal flow and places them relative to a reference point. Together, these properties form the foundation of CSS layout.

Display: Outer and Inner

The display property has two components:

  • Outer display: How the element behaves in its parent's layout (block or inline)
  • Inner display: How the element's children are laid out (flow, flex, grid)

The shorthand display: flex is actually display: block flex. The element is block-level externally but uses flexbox internally.

Outer Display Types

block — Element takes full available width and starts on a new line:

div, p, h1 { display: block; }

Block elements stack vertically. Width defaults to 100% of the parent.

inline — Element takes only the width of its content and stays on the same line:

span, a, strong { display: inline; }

Inline elements flow horizontally like text. They ignore width, height, and vertical margin.

inline-block — Inline externally, block internally. Flows inline but respects block properties:

.tag {
  display: inline-block;
  padding: 0.25em 0.5em;
  width: 80px; /* Works, unlike inline */
}

Use inline-block for:

  • Buttons that should flow inline
  • Horizontal lists of sized items
  • Inline elements that need padding/dimensions

Inner Display Types

flow — Normal block and inline layout (default):

div { display: block flow; }
/* Usually written as just: display: block; */

flex — Flexbox layout for children:

.container {
  display: flex;
  gap: 1rem;
}

grid — Grid layout for children:

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

We'll explore flex and grid in later modules.

display: contents

display: contents makes the element's box disappear. Its children are laid out as if the element doesn't exist:

<div class="container">
  <div class="wrapper">
    <p>Child 1</p>
    <p>Child 2</p>
  </div>
</div>
.container { display: flex; }
.wrapper { display: contents; }
/* Children of .wrapper become flex items of .container */

This is useful for:

  • Removing wrapper divs from layout without changing HTML
  • Making semantic elements layout-invisible
  • Flattening nested grid structures

Positioning: static (Default)

position: static is the default. The element is in normal flow:

div { position: static; }
/* top, right, bottom, left have no effect */

position: relative

position: relative keeps the element in normal flow but allows offset:

.nudge {
  position: relative;
  top: 10px;   /* Moves down 10px */
  left: 5px;   /* Moves right 5px */
}

The element's original space is preserved. Other elements don't move to fill the gap.

Use cases:

  • Small adjustments for optical alignment
  • Creating a positioning context for absolute children
  • Animating position without disrupting layout

position: absolute

position: absolute removes the element from flow. It's positioned relative to its nearest positioned ancestor (any ancestor with position other than static):

.container {
  position: relative; /* Positioning context */
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

If no positioned ancestor exists, it's positioned relative to the initial containing block (usually the viewport).

Containing block: The reference for positioning. For absolute, it's the nearest positioned ancestor. For fixed, it's the viewport.

<div class="container"> <!-- position: relative -->
  <div class="child"> <!-- position: absolute -->
    <!-- Positioned relative to .container -->
  </div>
</div>

position: fixed

position: fixed is like absolute, but positioned relative to the viewport. It doesn't scroll:

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 100;
}

Fixed elements are common for:

  • Sticky headers
  • Floating action buttons
  • Modal overlays

position: sticky

position: sticky is a hybrid. The element is in flow until it reaches a threshold, then becomes fixed:

.sidebar {
  position: sticky;
  top: 20px; /* Sticks 20px from top when scrolling */
}

The element sticks within its parent's boundaries. Once the parent scrolls out of view, the sticky element scrolls with it.

Requirements:

  • Must specify at least one threshold (top, bottom, left, or right)
  • Parent must have a scrollable height (not height-less)

Use sticky for:

  • Table headers that stick while scrolling
  • Section headings in long pages
  • Floating sidebars

The inset Shorthand

Instead of top, right, bottom, left, use inset:

/* Old way */
.overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

/* New way */
.overlay {
  position: absolute;
  inset: 0;
}

inset follows the same syntax as margin:

  • inset: 0 — All sides 0
  • inset: 10px 20px — Top/bottom 10px, left/right 20px
  • inset: 10px 20px 30px 40px — Top, right, bottom, left

Stacking Contexts

Positioned elements can overlap. The z-index property controls stacking order, but only for positioned elements (position other than static):

.background {
  position: absolute;
  z-index: 1;
}

.foreground {
  position: absolute;
  z-index: 2; /* Renders on top */
}

Higher z-index renders on top. Negative values are allowed.

A stacking context is a 3D layer. Elements inside a stacking context can't interleave with elements outside. Creating a stacking context isolates z-index to that context.

Stacking contexts are created by:

  • position: absolute|relative|fixed|sticky with z-index other than auto
  • position: fixed or position: sticky (always creates context)
  • opacity less than 1
  • transform, filter, perspective (non-none values)
  • isolation: isolate (explicitly creates context)

Example: Stacking Context Isolation

<div class="modal"> <!-- z-index: 200 -->
  <div class="overlay"></div> <!-- z-index: 1 -->
  <div class="content"></div> <!-- z-index: 2 -->
</div>
<div class="dropdown"> <!-- z-index: 100 -->
  <div class="menu"></div> <!-- z-index: 9999 (ineffective) -->
</div>
.modal {
  position: fixed;
  z-index: 200;
}

.dropdown {
  position: absolute;
  z-index: 100;
}

.menu {
  z-index: 9999; /* Doesn't matter—still behind modal */
}

The .menu can't escape .dropdown's stacking context. Even with z-index: 9999, it renders behind .modal (which has z-index: 200).

Managing Z-Index

Use a z-index scale instead of arbitrary numbers:

:root {
  --z-base: 0;
  --z-dropdown: 100;
  --z-sticky: 200;
  --z-modal: 300;
  --z-toast: 400;
  --z-tooltip: 500;
}

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

This prevents z-index wars where you add 1 to beat the previous element.

Safe Areas for Mobile

On mobile devices with notches or rounded corners, fixed elements can be obscured. Use safe area insets:

.footer {
  position: fixed;
  bottom: 0;
  padding-bottom: env(safe-area-inset-bottom);
}

env(safe-area-inset-bottom) is the distance from the screen edge to the safe area. On iPhone X, this is 34px at the bottom. On desktop, it's 0.

Also available:

  • env(safe-area-inset-top)
  • env(safe-area-inset-left)
  • env(safe-area-inset-right)

For full-screen elements:

.fullscreen {
  position: fixed;
  inset: env(safe-area-inset-top)
         env(safe-area-inset-right)
         env(safe-area-inset-bottom)
         env(safe-area-inset-left);
}

Stacking Context Visualization

In this diagram, even though D has z-index: 999, it can't render above C (which has z-index: 200) because D is isolated within B's stacking context.

Check Your Understanding

An inline element has `width: 200px` and `height: 100px` set. What happens?

Not quite. The correct answer is highlighted.

An element has `position: absolute`, and its parent has `position: static`. What is its containing block?

Not quite. The correct answer is highlighted.
Use to create a stacking context without positioning.
Not quite.Expected: isolation: isolate

Why use a z-index scale instead of arbitrary numbers?

Not quite. The correct answer is highlighted.

Practice

Summary

  • Outer display: block (full width, new line), inline (content width, same line), inline-block (inline flow, block properties).
  • Inner display: flow (default), flex (flexbox), grid (grid).
  • display: contents: Element's box disappears; children laid out as if element doesn't exist.
  • position: relative: In flow, but offset. Creates positioning context for absolute children.
  • position: absolute: Out of flow, positioned relative to nearest positioned ancestor.
  • position: fixed: Positioned relative to viewport. Doesn't scroll.
  • position: sticky: In flow until threshold, then fixed within parent boundaries.
  • inset shorthand: Replaces top/right/bottom/left. Cleaner syntax.
  • Stacking contexts: Isolate z-index. Created by positioned elements with z-index, opacity, transforms, or isolation: isolate.
  • Z-index scale: Use CSS variables to prevent escalation wars.
  • isolation: isolate: Create stacking context without positioning.
  • Safe areas: Use env(safe-area-inset-*) for notched mobile devices.
  • scroll-margin-top: Offset anchor scroll targets for fixed headers.