Lesson 14 / Layout

CSS Positioning

Time
55 min
Type
Reading + Interactive
Level
Intermediate
Use
Core

Use CSS positioning to precisely control element placement, stacking, sticky headers, and layered components.

Course Role

Core

Part of the main course path. Prioritize this before moving to optional polish or deeper references.

Teacher Notes / In-Class Use

Demo Live

  • Walk through the interactive demo before students start changing their own project files.
  • Connect the demo back to the first goal: Understand `static`, `relative`, `absolute`, `fixed`, and `sticky` positioning

Try In Class

  • Create one `relative` element and move it with `top` and `left`.
  • Have students make one visible change, save, refresh, and explain what changed.

Submit Or Check

  • Ask students to show the work in the browser, not only in the editor.
  • Have students commit their progress with a clear message when the checkpoint is stable.

Watch For

  • Students copying code without checking file paths, spelling, or capitalization.
  • Visual changes that work locally but break when the project is published.

Learning Goals

  • Understand `static`, `relative`, `absolute`, `fixed`, and `sticky` positioning
  • Use `top`, `right`, `bottom`, `left`, `inset`, and positioned ancestors correctly
  • Use `z-index` and positioning to control layer order without breaking layout or accessibility

Interactive Demo

How to use this demo.

Use the demo as a small lab. Change one thing, observe the result, then connect it back to your own project.

What To Try

  • Switch between static, relative, absolute, and sticky positioning.
  • Adjust top, left, and z-index to see which properties affect the target.

What Changes

  • The target element moves differently depending on the positioning mode.
  • The code sample updates with the active position and offset values.

What To Notice

  • Static elements ignore top and left.
  • Absolute positioning is measured from the nearest positioned ancestor.

Apply It

  • Use positioning for one layered or anchored UI element, then verify nearby content still behaves.

Interactive Demo

CSS Positioning Playground

Change the positioning mode and offsets to see how an element leaves, keeps, or responds to normal flow.

Normal block
Target
Later block

This demo uses extra JavaScript for teaching. The code sample shows the pattern to practice. View full demo source.

Introduction

CSS positioning gives you precise control over where an element appears on the page.

Use positioning for layers, badges, overlays, sticky headers, tooltips, and small visual adjustments. For the main structure of a page, use normal flow, Flexbox, or Grid first.

Positioning is powerful, but it is usually not the first tool for full page layout.

How to Choose a Positioning Method

Start by asking what the element needs to do. Most elements should stay in normal document flow. Reach for position when something needs to be nudged, layered, attached to a parent, or attached to the viewport.

NeedUseCommon Example
Normal page layoutNo special positioningText, sections, cards, and page structure
Nudge an element visuallyposition: relativeMove an icon or label slightly
Place something inside a parentParent relative, child absoluteBadge in the corner of a card
Keep something in the viewportposition: fixedCookie banner or floating button
Stick while scrollingposition: stickyHeader or sidebar that sticks at the top

Normal Flow and Static Positioning

Normal flow is the browser's default layout behavior. Block elements stack vertically, inline content flows with text, and each element takes up space in the document.

position: static is the default value. Static elements ignore top, right, bottom, left, and z-index.

.element {
  position: static;
}

Relative Positioning

position: relative keeps an element in normal flow, then lets you visually move it from its original spot.

The original space is still reserved, so nearby elements behave as if the element never moved.

.element {
  position: relative;
  top: 10px;
  left: 20px;
}

Absolute Positioning and Containing Blocks

position: absolute removes an element from normal flow and places it relative to its nearest positioned ancestor.

A positioned ancestor is a parent or ancestor with position set to anything other than static. If no positioned ancestor exists, the element can position itself relative to the page instead, which is a common source of bugs.

.card {
  position: relative;
}

.badge {
  position: absolute;
  top: 1rem;
  right: 1rem;
}

Fixed Positioning

position: fixed locks an element to the browser viewport. It stays in the same place even when the page scrolls.

Use fixed positioning carefully. Fixed elements can cover content, especially on small screens.

.site-alert {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

Sticky Positioning

position: sticky starts in normal flow, then sticks when it reaches a scroll offset such as top: 0.

Sticky elements need room to scroll. They can also stop working when a parent has certain overflow values, such as overflow: hidden.

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
}

Offset Properties

top, right, bottom, and left control where positioned elements move or attach.

inset is shorthand for setting all four sides at once. It is especially useful for overlays that need to fill a parent.

.image-overlay {
  position: absolute;
  inset: 0;
}

Position Types Compared

ValueIn Normal Flow?Positioned Relative ToUse For
staticYesNormal document flowDefault layout
relativeYesIts original positionSmall visual adjustments or creating an anchor for children
absoluteNoNearest positioned ancestorBadges, close buttons, overlays, and layered card pieces
fixedNoViewportFloating buttons, alerts, and persistent UI
stickyYes until it sticksNearest scrolling areaSticky headers, labels, and sidebars

Z-index and Layer Order

z-index controls which positioned elements appear in front of other elements.

Use small, intentional values instead of huge numbers. A simple scale is easier to maintain than random values like 999999.

.card {
  position: relative;
  z-index: 0;
}

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

.modal {
  position: fixed;
  z-index: 100;
}

Stacking Contexts

Sometimes z-index appears not to work because the element is inside a stacking context. A stacking context is a layer group. Children cannot always escape the layer order of their parent.

Positioned elements with z-index, transformed elements, elements with certain opacity values, and several other CSS properties can create stacking contexts.

When z-index is confusing, inspect the parent elements too. The problem is often higher in the HTML tree.

Example: Card Badge

This pattern places a badge in the corner of a card. The card becomes the positioning anchor, and the badge is placed inside that anchor.

<article class="card">
  <span class="badge">New</span>
  <h2>CSS Positioning</h2>
  <p>Build layered interface pieces.</p>
</article>

.card {
  position: relative;
}

.badge {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
}

Example: Notification Dot

A notification dot uses the same parent-child pattern. The button is the anchor, and the dot is positioned inside it.

.icon-button {
  position: relative;
}

.notification-dot {
  position: absolute;
  top: 0;
  right: 0;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
}

Example: Image Overlay

For overlay text on an image, the image can fill the card with position: absolute and inset: 0. The text stays above it with position: relative and z-index.

.hero-card {
  position: relative;
  overflow: hidden;
}

.hero-card img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-card-content {
  position: relative;
  z-index: 1;
}

Accessibility and Usability Notes

  • Do not use positioning to visually reorder meaningful content. Keep the HTML reading order logical.
  • Make sure fixed and sticky elements do not cover headings, links, form fields, or important content.
  • Use scroll-padding-top when a sticky header covers anchor links.
  • Modal overlays need keyboard focus management and a clear way to close.
  • Check positioned UI on small screens. A layout that works on desktop can cover content on mobile.
html {
  scroll-padding-top: 5rem;
}

Common Pitfalls and Troubleshooting

ProblemWhy It HappensFix
Absolute child appears in the wrong placeNo positioned parent existsAdd position: relative to the intended parent
z-index does nothingThe element is not participating in a stack as expectedAdd positioning when appropriate and inspect parent stacking contexts
Sticky element does not stickThere is not enough scroll room or a parent has restrictive overflowCheck parent containers and remove unnecessary overflow: hidden
Fixed header covers contentThe header is removed from normal flowAdd spacing, use scroll-padding-top, and test anchor links
Whole page is built with positioningPositioning is being used instead of layout toolsUse normal flow, Flexbox, or Grid for structure

Debugging Checklist

  • Inspect the element and identify its nearest positioned ancestor.
  • Temporarily add outline or background colors to parent and child elements.
  • Toggle position values in DevTools to see which rule changes the behavior.
  • Check whether a parent has overflow: hidden, transform, opacity, or a z-index that creates a stacking context.
  • Confirm whether the element should stay in normal flow or be removed from it.

Checkpoint

Before moving on, make sure these feel true.

  • I can explain the main concept in my own words.
  • I can apply this lesson to my current project.
  • I can verify the result in the browser.
  • I can commit the change with a clear message.

Practice

  • Create one relative element and move it with top and left.
  • Add an absolute badge inside a card by making the card position: relative.
  • Create a notification dot inside an icon button.
  • Build a sticky header and add scroll-padding-top so anchor links are not covered.
  • Layer text over an image with position: absolute, inset: 0, and z-index.
  • Debug a broken badge that is positioning itself relative to the wrong parent.