Advanced Positioning

CSS positioning controls where an element sits using the position property: relative nudges from its normal spot, absolute pins it to its nearest positioned ancestor, fixed locks it to the viewport, and sticky scrolls until it hits a threshold and then stays put.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

By the end you'll be able to pin badges, centre overlays, build sticky headers, and finally understand why z-index sometimes refuses to work.

Prerequisites: You should be comfortable with selectors and the box model first. If not, revisit CSS Basics & Selectors before continuing.

Static is a book on a shelf — it sits exactly where the flow puts it. Relative is sliding that book a few centimetres while leaving its gap on the shelf. Absolute is a sticky note pinned inside an open drawer — move the drawer (the parent) and the note moves with it. Fixed is a sticker on your monitor glass — it never moves no matter what scrolls behind it. Sticky is a fridge magnet that slides down with the door until it hits the bottom rail and stays there.

1. The five position values

Every element has a position . The default is static — it ignores top/right/bottom/left and sits where the normal flow puts it. The other four switch on placement powers. The single most important idea is the containing block : the box that an element's offsets are measured from.

In the worked example below, position: relative nudges box 2 down and right, but keeps its original gap in the flow — that is the giveaway that relative elements still occupy their space.

2. The absolute + relative pattern

Step 1: put position: relative on the parent. It barely changes anything visually, but it becomes the containing block.

Step 2: put position: absolute on the child plus the offsets you want ( top/right/bottom/left ).

The child is now placed against the parent's edges. This one pattern powers notification badges, close buttons, tooltips and dropdown menus.

Fill in the ___ blanks so the white card sits dead-centre of the blue stage, both horizontally and vertically. Use the classic absolute-centring trick: top: 50%; left: 50%; transform: translate(-50%, -50%) .

3. Fixed and sticky: persistent UI

fixed glues an element to the viewport — perfect for headers, floating action buttons and cookie bars that must stay visible.

A sticky element flows normally until you scroll past its threshold (e.g. top: 56px ), then it locks there until its parent scrolls away. Sticky is the modern way to build section headers and table headers without JavaScript.

Sticky needs a threshold and a scroll container. You must give it at least one of top/right/bottom/left , and no ancestor may have overflow: hidden/scroll/auto — that quietly clips sticky and it silently stops working.

Fill in the ___ blanks so the pink SALE badge sits in the product card's top-right corner. Remember the two steps: make the card relative , make the badge absolute .

4. z-index and stacking contexts

When positioned elements overlap, z-index decides who is on top — higher numbers win. But there is a catch that trips up nearly everyone: z-index is only compared within the same stacking context .

A stacking context is a self-contained layer pile. A child's z-index — even 9999 — can never escape its parent's context. The cure for z-index wars is not bigger numbers; it is creating contexts deliberately, e.g. with isolation: isolate .

A new stacking context is created by, among others: a positioned element with a z-index other than auto , opacity less than 1, any transform or filter , and isolation: isolate .

Now with the scaffolding removed. The starter has only a comment outline — write the .back-to-top rule yourself so a circular blue button stays glued to the bottom-right corner while the page scrolls.

Next up: turn boxes into custom silhouettes in Shapes & Clip-Path .

Practice quiz

Which position value keeps an element in the normal flow but lets you nudge it with top/left, reserving its original space?

  • absolute
  • fixed
  • relative
  • sticky

Answer: relative. position: relative nudges the box with offsets while still occupying its original slot in the flow.

An element with position: absolute is positioned relative to what?

  • The nearest ancestor that is itself positioned (relative, absolute, fixed or sticky)
  • The viewport
  • Its own normal position
  • The document body, always

Answer: The nearest ancestor that is itself positioned (relative, absolute, fixed or sticky). Absolute is measured from the nearest positioned ancestor. If none exists, it falls back to the initial containing block (the page).

Which position value locks an element to the viewport so it ignores scrolling?

  • relative
  • absolute
  • static
  • fixed

Answer: fixed. position: fixed is measured from the viewport, so the element stays put while the page scrolls.

How does position: sticky behave?

  • It is always glued to the viewport
  • It acts like relative until you scroll past its threshold, then like fixed within its scroll container
  • It removes the element from the flow immediately
  • It ignores all offset values

Answer: It acts like relative until you scroll past its threshold, then like fixed within its scroll container. Sticky is a hybrid: relative until its threshold (e.g. top: 0) is reached, then it locks like fixed inside its scroll container.

What must a position: sticky element have to actually stick?

  • At least one threshold (top, right, bottom or left)
  • A z-index value
  • display: flex
  • A fixed ancestor

Answer: At least one threshold (top, right, bottom or left). Sticky needs a threshold, room to scroll, and no ancestor with overflow hidden/scroll/auto clipping it.

The classic way to perfectly centre an absolutely positioned child is top: 50%; left: 50%; plus what?

  • margin: auto
  • text-align: center
  • transform: translate(-50%, -50%)
  • position: fixed

Answer: transform: translate(-50%, -50%). top/left 50% places the child's top-left corner at the centre; translate(-50%, -50%) pulls it back by half its own size so its centre lands on the centre.

z-index only applies to elements with which positions?

  • Any element regardless of position
  • relative, absolute, fixed or sticky
  • Only static elements
  • Only fixed elements

Answer: relative, absolute, fixed or sticky. z-index has no effect on static elements; the element must be positioned (relative, absolute, fixed or sticky).

Why can a child with z-index: 9999 still appear below another element?

  • 9999 is too large and overflows
  • z-index only works on images
  • The browser caps z-index at 100
  • z-index is only compared within the same stacking context; the child may be trapped in its parent's context

Answer: z-index is only compared within the same stacking context; the child may be trapped in its parent's context. z-index is scoped to a stacking context. A child's huge z-index cannot escape a parent that created its own context.

Which of these creates a new stacking context?

  • display: block
  • opacity less than 1, a transform, a filter, or isolation: isolate
  • margin: auto
  • position: static

Answer: opacity less than 1, a transform, a filter, or isolation: isolate. opacity < 1, any transform or filter, isolation: isolate, and a positioned element with a z-index other than auto all create stacking contexts.

The 'absolute + relative' pattern for badges and tooltips means:

  • Both parent and child use position: absolute
  • The parent is fixed and the child is sticky
  • The parent is position: relative (the containing block) and the child is position: absolute
  • The child uses float instead of position

Answer: The parent is position: relative (the containing block) and the child is position: absolute. Make the parent relative so it becomes the containing block, then place the child absolutely against the parent's edges.