Home Module 04 Position

1. Introduction

By default, HTML elements flow naturally from top to bottom. But many layouts require elements to be placed in specific positions — a sticky navigation bar, a tooltip appearing next to a button, an overlay covering the entire screen, or a "Back to Top" button floating in the corner.

The CSS position property lets you take elements out of the normal flow and place them precisely. This lesson covers all five position values and when to use each.

Once you set a position other than static, you can use the offset properties top, right, bottom, left (and the shorthand inset) to move the element.

2. Theory

position: static (default)

Every element starts as static. It sits in the normal document flow. Offset properties (top, left, etc.) have no effect.

div { position: static; } /* default — no need to set this */

position: relative

The element stays in the normal flow (its original space is preserved), but you can shift it visually using offset properties. The shift is relative to its own normal position.

.nudge {
  position: relative;
  top: 10px;   /* moves 10px down from where it would normally be */
  left: 20px;  /* moves 20px to the right */
}
/* The element still occupies its original space — other elements
   are NOT affected by the visual shift */

Most important use of relative: It creates a positioning context for absolutely positioned children.

position: absolute

The element is removed from the normal flow — it no longer takes up space. It is positioned relative to its nearest positioned ancestor (an ancestor with position other than static). If none exists, it positions relative to the initial viewport.

.parent {
  position: relative; /* creates positioning context */
  width: 200px;
  height: 200px;
}

.badge {
  position: absolute;
  top: 8px;    /* 8px from parent's top edge */
  right: 8px;  /* 8px from parent's right edge */
  /* This badge is anchored to .parent */
}

position: fixed

The element is removed from the flow and positioned relative to the viewport (browser window). It stays in the same screen position even when the page scrolls.

.sticky-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;          /* stretch across the full width */
  /* or: width: 100%; */
  z-index: 100;      /* appear above other content */
  background: white;
}

.back-to-top {
  position: fixed;
  bottom: 24px;
  right: 24px;       /* bottom-right corner of screen */
}

position: sticky

A hybrid of relative and fixed. The element behaves as relative until it reaches a specified scroll threshold, then "sticks" like fixed until its parent ends.

.table-header {
  position: sticky;
  top: 0;          /* sticks when it reaches 0px from the top of the viewport */
  background: white;
  z-index: 10;
}

.sidebar {
  position: sticky;
  top: 80px;       /* sticks 80px from top (below a fixed navbar) */
  height: fit-content; /* important — don't let it stretch to parent height */
}

z-index — Stacking Order

When positioned elements overlap, z-index controls which appears on top. Higher value = on top. Default: auto (follows DOM order).

.modal-overlay { z-index: 1000; }
.modal-content { z-index: 1001; } /* above the overlay */
.tooltip       { z-index: 9999; } /* always on top */

Important: z-index only works on positioned elements (not static).

The inset Shorthand

Modern shorthand for top + right + bottom + left.

/* These are equivalent */
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;

position: absolute;
inset: 0; /* shorthand for all four = 0 */

Position Summary Table

ValueIn flow?Positioned relative toCommon use
staticYesDefault
relativeYes (original space kept)Its own normal positionContext for absolute children, minor nudges
absoluteNoNearest positioned ancestorOverlays, badges, dropdowns, tooltips
fixedNoViewportFixed navbar, floating buttons, modals
stickyYes (then sticks)Scroll containerSticky table headers, sidebars

3. Real World Example

Examples of position in real websites:

  • Fixed navbar — stays at the top as you scroll (position: fixed; top: 0;)
  • Shopping cart badge — number badge on a cart icon, anchored to the top-right of the icon (position: absolute on badge, position: relative on icon wrapper)
  • Dropdown menu — appears below and aligned to its trigger button (position: absolute relative to the button's container)
  • Cookie consent banner — fixed to the bottom of the screen (position: fixed; bottom: 0;)
  • Sticky table of contents — stays visible while scrolling a long article (position: sticky; top: 100px;)

4. Code Example

/* ===== POSITION EXAMPLES ===== */

/* Fixed navbar */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 64px;
  background: #1e293b;
  z-index: 100;
  display: flex;
  align-items: center;
  padding: 0 24px;
}

/* Push page content below fixed navbar */
body { padding-top: 64px; }

/* ---- Notification badge on icon ---- */
.icon-wrapper {
  position: relative;  /* creates context for badge */
  display: inline-block;
}

.notification-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 18px;
  height: 18px;
  background: #ef4444;
  color: white;
  font-size: 0.625rem;
  font-weight: 700;
  border-radius: 50%;
  display: grid;
  place-items: center;
}

/* ---- Dropdown menu ---- */
.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-menu {
  position: absolute;
  top: 100%;     /* just below the button */
  left: 0;
  min-width: 180px;
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  z-index: 50;
  display: none;
}

.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
  display: block;
}

/* ---- Modal overlay ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  z-index: 200;
  display: none;
  place-items: center;
}

.modal-overlay.open { display: grid; }

.modal-box {
  background: white;
  border-radius: 12px;
  padding: 32px;
  max-width: 500px;
  width: 90%;
  position: relative; /* for close button */
}

.modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  cursor: pointer;
}

/* ---- Sticky sidebar ---- */
.page-layout {
  display: grid;
  grid-template-columns: 1fr 300px;
  gap: 32px;
  align-items: start;
}

.toc-sidebar {
  position: sticky;
  top: 80px; /* below fixed navbar */
  max-height: calc(100vh - 100px);
  overflow-y: auto;
}

5. Code Breakdown

position: relative on .icon-wrapper
This makes the icon wrapper the positioning context for the badge. Without it, the badge with position: absolute; top: -4px; right: -4px would position itself relative to the next positioned ancestor — possibly the entire page.
top: 100% on .dropdown-menu
100% of the parent's height. This places the dropdown exactly at the bottom edge of its parent button — regardless of how tall the button is. Much better than a hard-coded pixel value.
inset: 0 on .modal-overlay
Shorthand for top: 0; right: 0; bottom: 0; left: 0;. Combined with position: fixed, this makes the overlay cover the entire viewport precisely.
max-height: calc(100vh - 100px) on sidebar
calc() lets you mix units. Here: 100% of viewport height minus 100px (for the fixed header). This prevents the sticky sidebar from being taller than the screen, and overflow-y: auto adds a scrollbar if the TOC is long.
z-index: 100, 200, 50
Using layered z-index values: navbar (100) is above content, modal (200) is above navbar, dropdown (50) is above cards but below navbar. Using increments of 50+ gives room to add layers between.

6. Common Mistakes

  • Using position: absolute without a positioned parent. If no ancestor has position: relative/absolute/fixed/sticky, the element positions relative to the root — almost never what you want. Always add position: relative to the parent.
  • Forgetting body { padding-top: Xpx; } after a fixed navbar. Fixed elements are removed from flow — content scrolls under them. Add padding-top equal to the navbar height to prevent content from being hidden.
  • Using z-index without position. z-index only works on elements with a position other than static. Set position: relative at minimum.
  • Sticky not working. Common causes: a parent has overflow: hidden or overflow: auto (breaks sticky), the element doesn't have a top/bottom value set, or the parent height equals the element height.
  • Z-index arms race. When things aren't stacking right, beginners add higher and higher z-index values. Instead, understand stacking contexts and use structured z-index values.

7. Best Practices

  • Use positioning only when needed — prefer Flexbox/Grid for layout. Position is best for overlays, tooltips, badges.
  • Always pair position: absolute with position: relative on the parent.
  • Use structured z-index values — define layers in a comment or variable system: content=1, dropdowns=50, navbar=100, modals=200.
  • Use inset: 0 instead of writing all four offset properties for full-cover overlays.
  • Test sticky carefully — check no ancestor has overflow that breaks it.
  • Add padding-top to body when using a fixed navbar.

8. Practice Exercise

  1. Create a fixed navigation bar that stays at the top when you scroll. Add padding-top to body.
  2. Create an icon with a notification badge in the corner using relative + absolute.
  3. Create a card with an image — place a "Featured" badge in the top-left corner of the image using positioning.
  4. Create a simple dropdown — a button that shows a menu below it on hover using position: absolute; top: 100%;.
  5. Scroll down a long page and test position: sticky on a section heading.

9. Assignment

Build a page that uses all five position values correctly:

  • Fixed: A top navigation bar and a "Back to Top" button in the bottom-right corner
  • Relative: Used on three card wrappers as positioning context
  • Absolute: A "Sale" badge on one card and a heart icon in the corner of another
  • Sticky: A sidebar table of contents that sticks below the navbar
  • Static: All other page elements
  • Ensure correct z-index layering so fixed nav appears above all other content

10. Interview Questions

Q1: What are the five CSS position values?

Answer: static (default, in normal flow), relative (in flow, but can be offset from its normal position and creates context for absolute children), absolute (removed from flow, positioned relative to nearest positioned ancestor), fixed (removed from flow, positioned relative to viewport, does not scroll), sticky (in flow until a scroll threshold, then sticks).

Q2: What is a "positioned ancestor" and why does it matter?

Answer: A positioned ancestor is any ancestor element with a position value other than static. Absolutely positioned elements position themselves relative to their nearest positioned ancestor. Without one, they position relative to the viewport. That is why we add position: relative to parent containers when we need to anchor child elements.

Q3: What is the difference between position: fixed and position: sticky?

Answer: Fixed is always relative to the viewport and never moves when scrolling. Sticky stays in the normal flow until the user scrolls past a threshold (set by top/bottom), at which point it "sticks" and behaves like fixed. Sticky also stops sticking when its parent scrolls off screen.

Q4: Why doesn't z-index work on my element?

Answer: z-index only has effect on positioned elements (those with a position value other than static). Add position: relative to the element if you want z-index to take effect.

Q5: How do you overlay an element on top of another?

Answer: Give the parent position: relative, then give the overlay element position: absolute; inset: 0; (or top: 0; left: 0; right: 0; bottom: 0;) along with an appropriate z-index. Set a background colour (or transparent) and appropriate dimensions.

11. Additional Resources

  • MDN Web Docs — CSS position (search "MDN CSS position")
  • CSS-Tricks — Absolute Positioning Inside Relative Positioning (search this title)
  • MDN — Understanding z-index (search "MDN understanding z-index")
  • MDN — position: sticky (search "MDN sticky positioning")
  • Josh Comeau — What The Heck Is CSS Stacking Context (search this title)