CSS position: Five Values, One Live Demo

The position property chooses how an element is placed. static is normal flow. relative nudges it while keeping its space. absolute removes it from flow and places it against the nearest positioned ancestor. fixed pins it to the viewport. sticky scrolls normally until it reaches a threshold such as top: 0, then holds there.

The five values
ValueIn the flow?Placed against
staticStays in flowNothing, top/left have no effect
relativeStays in flow, its original space is keptIts own normal position
absoluteRemoved from flowNearest positioned ancestor's padding box
fixedRemoved from flowThe viewport, unless an ancestor has transform, filter or similar
stickyStays in flowNearest scrolling ancestor, within its parent

Updated

The demo below has one orange box inside a scrollable area. Switch its position value, move the offsets, scroll, and watch what it is measured from. A guide to containing blocks, a checklist for sticky that will not stick, and the inset shorthand follow.

Sticky not working: checklist

  1. You set a threshold: top, bottom, left or right. Without one, sticky behaves like relative.
  2. No ancestor between the element and the scroller has overflow: hidden, auto or scroll. Any of these becomes the scroller, often one that never scrolls. overflow: clip does not create a scroll container.
  3. The parent is taller than the sticky element. It only sticks while its parent is on screen, then leaves with it.
  4. In a flex or grid row, the item is not stretched to full height. Add align-self: start so it has room to move.
  5. The sticky element sits above the content scrolling under it. Give it a background and a z-index if content shows through.
  6. The scroll actually happens on the ancestor you think it does. Check which element has the scrollbar in DevTools.

Each value in detail

position values
ValueFlowWhat offsets doTypical use
staticStays in flowIgnoredThe default. Everything starts here.
relativeStays in flow, its original space is keptNudge it from where it would beSmall nudges, and as the anchor for absolute children.
absoluteRemoved from flowPlace it inside the containing blockBadges, overlays, dropdown menus, tooltips.
fixedRemoved from flowPlace it on the screenCookie bars, floating buttons, full-screen modals.
stickyStays in flowThreshold where it starts stickingTable headers, section titles, side menus.

What creates a containing block for absolute and fixed

Ancestors that capture positioned descendants (per MDN)
Ancestor hasabsolute children use it?fixed children use it?
position: relative, absolute, fixed or stickyYesNo
transform, perspective, rotate, scale or translate other than noneYesYes
filter or backdrop-filter other than noneYesYes
contain: layout, paint, strict or contentYesYes
will-change naming one of those properties, such as transform or filterYesYes
content-visibility: autoYesYes

Patterns you will use

Badge on a card corner

The card is the positioned ancestor. Negative offsets push the badge half outside the corner.

<div style="position: relative; max-width: 220px; padding: 16px; border: 1px solid #cbd5e1; border-radius: 12px">
  <strong>Inbox</strong><p style="margin: 4px 0 0">3 unread messages</p>
  <span style="position: absolute; top: -8px; right: -8px; min-width: 22px; height: 22px; display: grid; place-items: center; background: #dc2626; color: #fff; border-radius: 999px; font: 600 12px system-ui">3</span>
</div>

Sticky table header

The wrapper scrolls, so th with top: 0 sticks to it. The background stops rows showing through.

<div style="max-height: 160px; overflow: auto; border: 1px solid #cbd5e1">
  <table style="border-collapse: collapse; width: 100%; font: 14px system-ui">
    <thead><tr><th style="position: sticky; top: 0; background: #e2e8f0; text-align: left; padding: 6px">Order</th><th style="position: sticky; top: 0; background: #e2e8f0; text-align: left; padding: 6px">Status</th></tr></thead>
    <tbody>
      <tr><td style="padding: 6px">#1041</td><td>Shipped</td></tr><tr><td style="padding: 6px">#1042</td><td>Packing</td></tr>
      <tr><td style="padding: 6px">#1043</td><td>Paid</td></tr><tr><td style="padding: 6px">#1044</td><td>Shipped</td></tr>
      <tr><td style="padding: 6px">#1045</td><td>Refunded</td></tr><tr><td style="padding: 6px">#1046</td><td>Paid</td></tr>
      <tr><td style="padding: 6px">#1047</td><td>Packing</td></tr><tr><td style="padding: 6px">#1048</td><td>Shipped</td></tr>
    </tbody>
  </table>
</div>

inset shorthand

Same pattern as margin: one value for all sides, two for vertical and horizontal, four clockwise from the top.

.overlay   { position: absolute; inset: 0; }               /* cover the parent */
.toast     { position: fixed; inset: auto 16px 16px auto; } /* bottom-right corner */
.drawer    { position: fixed; inset-block: 0; inset-inline-start: 0; width: 320px; }
/* inset: 0 is the same as top: 0; right: 0; bottom: 0; left: 0; */
Value summary
ValueUse
staticThe default. Everything starts here.
relativeSmall nudges, and as the anchor for absolute children.
absoluteBadges, overlays, dropdown menus, tooltips.
fixedCookie bars, floating buttons, full-screen modals.
stickyTable headers, section titles, side menus.

The containing block, in plain terms

Every offset needs a reference rectangle. For static and relative elements it is the content box of the parent. For absolute it is the padding box of the closest positioned ancestor, so padding on the card does not push an absolute child inward.

For fixed it is normally the viewport. That changes when an ancestor gets transform, perspective, filter, backdrop-filter, contain: paint or a matching will-change value: the ancestor becomes the reference and the element scrolls with it. The demo on this page uses exactly that so its fixed box stays inside the demo frame.

Centring with absolute positioning

With a known size, inset: 0 plus margin: auto puts the box dead centre and keeps pixel edges sharp. With an unknown size, top: 50% and left: 50% followed by translate(-50%, -50%) works for any content.

Both remove the box from flow, so the parent will not grow around it. If the parent needs to size to the content, use grid or flex centring instead.

How it works

  1. Choose a position value for the orange box.
  2. Set top and left, and turn the positioned parent on or off to see which box absolute uses.
  3. Scroll inside the demo to compare fixed and sticky.
  4. Copy the CSS that matches what you see.

Tips

Frequently asked questions

What is the difference between absolute and relative?

relative shifts an element visually but leaves its original space in the layout, so nothing around it moves. absolute takes the element out of the layout entirely, neighbours close the gap, and top or left are measured from the nearest positioned ancestor rather than from where it would have been.

Why is position: sticky not working?

The usual causes are a missing top value, an ancestor with overflow hidden or auto, or a parent that is no taller than the sticky element. Work through the checklist on this page in order; the first two catch most cases.

What does absolute position relative to?

Its containing block: the padding box of the closest ancestor whose position is anything other than static. Ancestors with a transform, filter or certain contain values also qualify. If none exists, it is the initial containing block, which has the size of the viewport.

Why does my fixed header move with the page?

An ancestor has a transform, perspective, filter, backdrop-filter, will-change: transform or contain: paint. Any of these turns that ancestor into the containing block for fixed descendants, so the header is fixed to it instead of to the screen.

What is inset in CSS?

A shorthand for top, right, bottom and left, following the same one to four value pattern as margin. inset: 0 stretches a positioned element over its containing block. inset-inline and inset-block set the pairs for each axis.

Related: z-index and stacking contexts, How to centre a div, CSS transforms, CSS box model, Flexbox and grid cheat sheet.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap