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.
| Value | In the flow? | Placed against |
|---|---|---|
| static | Stays in flow | Nothing, top/left have no effect |
| relative | Stays in flow, its original space is kept | Its own normal position |
| absolute | Removed from flow | Nearest positioned ancestor's padding box |
| fixed | Removed from flow | The viewport, unless an ancestor has transform, filter or similar |
| sticky | Stays in flow | Nearest 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.
| Value | Flow | What offsets do | Typical use |
|---|---|---|---|
| static | Stays in flow | Ignored | The default. Everything starts here. |
| relative | Stays in flow, its original space is kept | Nudge it from where it would be | Small nudges, and as the anchor for absolute children. |
| absolute | Removed from flow | Place it inside the containing block | Badges, overlays, dropdown menus, tooltips. |
| fixed | Removed from flow | Place it on the screen | Cookie bars, floating buttons, full-screen modals. |
| sticky | Stays in flow | Threshold where it starts sticking | Table headers, section titles, side menus. |
| Ancestor has | absolute children use it? | fixed children use it? |
|---|---|---|
| position: relative, absolute, fixed or sticky | Yes | No |
| transform, perspective, rotate, scale or translate other than none | Yes | Yes |
| filter or backdrop-filter other than none | Yes | Yes |
| contain: layout, paint, strict or content | Yes | Yes |
| will-change naming one of those properties, such as transform or filter | Yes | Yes |
| content-visibility: auto | Yes | Yes |
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>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>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 | Use |
|---|---|
| static | The default. Everything starts here. |
| relative | Small nudges, and as the anchor for absolute children. |
| absolute | Badges, overlays, dropdown menus, tooltips. |
| fixed | Cookie bars, floating buttons, full-screen modals. |
| sticky | Table headers, section titles, side menus. |
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.
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.
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.
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.
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.
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.
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.
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.