CSS Portal

CSS Position Visualizer

CSS position is one of the most fundamental, and most misunderstood properties in web development. It controls how an element is placed in the document, whether it stays in the normal flow of the page, and what it uses as a reference point for its coordinates. Getting it wrong is responsible for an enormous number of layout bugs: overlapping elements, content hidden behind navbars, tooltips stuck in the wrong place, z-index seemingly doing nothing.

This tool gives you a live, interactive playground for every position value. Click through static, relative, absolute, fixed, and sticky, drag the sliders, and see exactly what changes and why. Then go further with the z-index and stacking context sections, covering the gotcha that trips up even experienced developers - and finish with the Layout Debugger, a realistic mini-page where you can deliberately break things to understand what position values actually do in the real world.

If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!
CSS Position Visualizer

position: static

The default value. Elements flow in the normal document order - top to bottom, left to right. The top, right, bottom, left, and z-index properties have no effect.

default value normal flow offsets ignored z-index ignored
position: static; /* top, left, right, bottom, z-index all have no effect */
1Box 1 - flows normally in document order
2Box 2 - flows normally in document order
3Box 3 - position: static - no offsets possible
4Box 4 - flows normally in document order
5Box 5 - flows normally in document order
ℹ Use the top slider in the sidebar - no matter what value you set, Box 3 won't budge. Static elements completely ignore offset properties.
Box 1 - normal flow
Box 2 - original space is still reserved here
Box 2 - position: relative (offset from its original spot)
Box 3 - not displaced, original space is still reserved above
Box 4 - normal flow
position: relative (positioned ancestor)
position: absolute
Removed from normal flow Positions relative to its nearest non-static ancestor (the dashed box). Move the sliders to reposition it.
No space reserved Unlike relative, other elements behave as if this element simply doesn't exist.
No positioned ancestor? The element falls back to the initial containing block, essentially the top-left of the page.
📌 position: fixed
Scroll me - I stay put
↓ Scroll this area - the badge stays locked to the corner regardless
1Content scrolls normally…
2Content scrolls normally…
3Content scrolls normally…
4Content scrolls normally…
5Content scrolls normally…
6Content scrolls normally…
7The fixed badge is still up there ↑
8Content scrolls normally…
9Content scrolls normally…
10Content scrolls normally…
11Still there ↑ - anchored to the viewport, not the page
12Content scrolls normally…
13End of content - badge never moved!
Item A-1 - scroll down, the header will stick at the top
Item A-2
Item A-3
Item A-4
Item A-5
Item B-1 - Section A's header gets pushed off as B arrives
Item B-2
Item B-3
Item B-4
Item B-5
Item C-1 - each section header takes its turn at the top
Item C-2
Item C-3
Item C-4
Item C-5
Box Az-index: 1
Box Bz-index: 2
Box Cz-index: 3
What is z-index? Controls the stacking order of overlapping positioned elements. Higher values sit in front. Only works when position is not static.
Things to try Set Box A's z-index higher than B and C to bring it to the front. Set any box to -1 and watch it slip behind the others. Make them all equal to see natural DOM order take over.
✓ No stacking context on parent
Parent Box B z-index: 1
Box A z-index: 100
Box A has z-index: 100, Box B has z-index: 1.
Both compete in the same stacking context - higher z-index wins. Box A is on top. ✓
✗ Parent creates a new stacking context
Parent
Box A z-index: 100
Box B z-index: 1
Apply a trigger below. Box A's z-index: 100 becomes scoped inside its parent's stacking context - it can no longer compete with Box B in the outer context.
Apply a trigger to Box A's parent: Each of these creates a new stacking context - no visual change, but Box B (z-index: 1) will leap in front of Box A (z-index: 100):
← Click a trigger above to see the stacking context gotcha in action
Layout Debugger

This is a miniature webpage - nav bar, sidebar, content area, tooltip and footer. Each element has a position value you can change using the dropdowns below. Watch the layout break in real time as you experiment, and read the status strip to understand what went wrong. Hit Reset to restore the working defaults.

Nav bar
Sidebar
Tooltip
Footer
MySite HomeAboutWork fixed
Dashboard
Analytics
Settings
Help
relative
← Hover tip
position: absolute absolute
Welcome back! Here's your overview for today.
Main content area - articles, data, widgets go here. Try changing the sidebar to absolute and watch this area expand to fill the gap.
Recent activity feed item #1
Recent activity feed item #2
Status: Nav ✓ fixed to top Sidebar ✓ Tooltip ✓ anchored Footer ✓
💡 Everything is working correctly. Try changing a value to break the layout and learn why it happens.
If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!

About this tool

What you will learn

  • The difference between every position value and when to use each one
  • Why static ignores top, left, right, bottom and z-index entirely
  • How relative moves an element without disturbing surrounding content
  • How absolute removes an element from flow and anchors it to a parent
  • Why fixed is viewport-relative and immune to scrolling
  • How sticky combines relative and fixed with a scroll threshold
  • How z-index controls stacking order and when it does nothing at all
  • What a stacking context is and why it breaks z-index in surprising ways

The stacking context gotcha

The most common advanced CSS bug is z-index seemingly refusing to work. An element with z-index: 100 sits behind another with z-index: 1. The reason is almost always a stacking context: a parent element somewhere in the tree has a property like opacity, transform, filter, or will-change applied to it, which creates an isolated z-index layer. The child's z-index only competes within that layer, not the rest of the page. The stacking context section demonstrates this interactively, apply each trigger and watch the order flip.

The layout debugger

Abstract examples only go so far. The Layout Debugger shows a realistic mini-page - nav bar, sidebar, tooltip, and footer - each with their correct default position value. You can change any element to any value and immediately see the consequences: the sidebar going absolute removes it from flow so the content expands, the tooltip going relative drops into the document and pushes content down, the nav going static scrolls away with the page. The status strip tells you what is working and what is broken, so you understand not just what happened but why.

How to use this tool

  • Use the sidebar to switch between position values and concepts
  • Each scene has a description card and a live code snippet that updates as you interact
  • Use sliders on the relative, absolute, and sticky scenes to adjust offsets in real time
  • On the static scene, drag the top slider to see the code update, but the element never moves
  • On stacking context, click each trigger button to create a new context and observe z-index breaking
  • On layout debugger, change any dropdown, break things deliberately, then hit Reset

CSS Position Values at a Glance

Value In normal flow? Reference point Scrolls with page? z-index works?
static ✅ Yes N/A ✅ Yes ❌ No
relative ✅ Yes Its own original position ✅ Yes ✅ Yes
absolute ❌ No Nearest non-static ancestor ✅ Yes ✅ Yes
fixed ❌ No Viewport ❌ No ✅ Yes
sticky ✅ / ❌ Both Nearest scrolling ancestor ✅ / ❌ Both ✅ Yes

Frequently Asked Questions

What is the difference between position: absolute and position: relative in CSS?

position: relative keeps the element in the document flow and offsets it visually from where it would naturally appear - other elements still behave as if it is in its original spot. position: absolute removes the element from the flow entirely, so surrounding elements collapse as if it does not exist. An absolutely positioned element anchors itself to the nearest ancestor with a non-static position value, or the page root if none exists.

When should I use position: fixed vs position: sticky?

Use fixed when you want an element - such as a nav bar or chat button - to remain visible at all times regardless of where the user has scrolled. Use sticky when you want an element to scroll normally at first but then lock into place once it reaches a certain point, such as a table header that sticks to the top of the screen as you scroll through a long table. The key difference is that a sticky element is still bound to its parent container and will scroll away when the parent does; a fixed element never moves at all.

Why is my z-index not working?

The most common reason is that the element has position: static, which causes z-index to be completely ignored - it only works on elements with a non-static position value. The second most common reason is a stacking context. If a parent element has a property like opacity (anything less than 1), transform, filter, or will-change applied to it, the browser creates an isolated stacking layer for that subtree. Any z-index on children only competes within that layer, not against the rest of the page - so a child with z-index: 100 can still sit behind an outside element with z-index: 1.

What does position: static mean in CSS?

position: static is the default value for every HTML element. It means the element is placed according to the normal document flow - top to bottom, left to right - with no special positioning applied. The top, right, bottom, left, and z-index properties have absolutely no effect on a static element.

What is a stacking context in CSS?

A stacking context is an isolated layer in which child elements are stacked and painted relative to each other, independent of elements outside that layer. It is created automatically by certain CSS properties, including opacity values below 1, any transform, filter, or perspective value, will-change, and isolation: isolate. Once a stacking context exists, a child element's z-index only controls its position within that context - it cannot compete with elements at a higher level of the stacking order.

Can I use position: sticky without a fixed height on the parent?

Yes, but the parent must have overflow set to visible (the default) for sticky to work. A very common gotcha is that if any ancestor in the chain has overflow: hidden, overflow: auto, or overflow: scroll set, the sticky behaviour will break silently - the element simply scrolls away as if it were relative.

What happens if an absolutely positioned element has no positioned ancestor?

It falls back to the initial containing block, which is effectively the top-left corner of the <html> element (the page root). This is a frequent source of confusion when an absolute element appears to jump unexpectedly to a completely different location - it usually means the intended positioned ancestor is missing a position declaration.

Page last updated on:

If this site has been useful, we’d love your support! Consider buying us a coffee to keep things going strong!