CSS Position Relative
position: relative is the “nudge tool” of CSS layout. It keeps an element in the normal document
flow (so it still takes up space), but lets you offset it visually using top, right,
bottom, and left.
CSS position: relative meaning
When you set position: relative on an element:
-
It stays in the normal flow (like
position: static). -
You can apply offsets (
top,left, etc.) to move it visually. - The space it originally occupied does not move—so other elements behave as if it never moved.
- It can become a useful “anchor” for absolutely positioned children (we’ll do that soon).
.card {
position: relative;
top: 18px;
left: 18px;
}
.card {
position: relative;
top: -18px;
left: -18px;
}
.card {
position: relative;
left: 0px;
top: 0px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 12px;
}
.row {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
.stage {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
display: grid;
gap: 12px;
}
.label {
font-weight: 700;
}
.card {
border: 2px solid #111;
border-radius: 14px;
padding: 12px;
background: #f6f6f6;
box-shadow: 0 10px 0 #111;
}
.hint {
border: 2px dashed #111;
border-radius: 14px;
padding: 10px;
background: #fff;
opacity: 0.85;
}
Watch what happens when the card moves visually…This dashed box stays put. It’s the “original flow” area.I’m the cardWith
position: relative, I can be nudged without affecting other elements’ layout.Notice how this paragraph doesn’t “slide into” the card’s old spot. That spot still exists.
Offsets: top/right/bottom/left (how they work)
Offsets don’t “pull” other elements. They simply shift the painted box. Also:
-
top: 14pxmoves the element down (because you’re pushing the top edge away from where it would have been). -
left: 18pxmoves it right. -
bottom: 14pxmoves it up. -
right: 18pxmoves it left.
.chip {
position: relative;
top: 14px;
}
.chip {
position: relative;
bottom: 14px;
}
.chip {
position: relative;
left: 18px;
}
.chip {
position: relative;
right: 18px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 12px;
}
.stage {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
display: grid;
gap: 10px;
}
.line {
border: 2px dashed #111;
border-radius: 12px;
padding: 14px;
background: #fafafa;
}
.chip {
display: inline-block;
border: 2px solid #111;
border-radius: 999px;
padding: 6px 10px;
background: #f6f6f6;
font-weight: 700;
}
p {
margin: 0;
}
Text in the normal flow.
I moveText continues as if the chip never moved.
CSS position: relative vs static
position: static is the default for most elements. In static positioning:
- The element is in normal flow.
-
Offsets like
topandleftare ignored.
With position: relative, offsets are respected (but the element still stays in flow).
.box {
position: static;
top: 18px;
left: 18px;
}
.box {
position: relative;
top: 18px;
left: 18px;
}
.box {
position: relative;
top: 0px;
left: 0px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stage {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
display: grid;
gap: 10px;
}
.track {
border: 2px dashed #111;
border-radius: 14px;
padding: 16px;
background: #fafafa;
}
.box {
width: 220px;
border: 2px solid #111;
border-radius: 14px;
background: #f6f6f6;
padding: 12px;
box-shadow: 0 10px 0 #111;
}
small {
opacity: 0.8;
}
BoxTry the snippets: static ignores offsets, relative uses them.
Learn more about position: static; in the CSS Position Static Interactive Tutorial.
CSS position: relative vs absolute
These two are often compared because they both “unlock” offsets, but they behave very differently:
- relative: stays in the normal flow; offsets move it visually; its original space remains reserved.
- absolute: is removed from the normal flow; offsets position it relative to a containing block; it can overlap without reserving space.
This playground shows the “space reservation” difference immediately.
.tag {
position: relative;
top: 10px;
left: 10px;
}
.tag {
position: absolute;
top: 10px;
left: 10px;
}
.tag {
position: static;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stage {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
display: grid;
gap: 12px;
}
.card {
border: 2px solid #111;
border-radius: 16px;
background: #f6f6f6;
padding: 16px;
box-shadow: 0 10px 0 #111;
position: relative;
}
.tag {
display: inline-block;
border: 2px solid #111;
border-radius: 999px;
padding: 6px 10px;
background: #fff;
font-weight: 800;
}
p {
margin: 0;
}
.stack {
display: grid;
gap: 10px;
}
BadgeWhen the badge is relative, it still reserves its space.
When it is absolute, it no longer reserves space and can overlap.
Relative is often used to “enable” absolute children
One of the most common real-world uses of position: relative is not for nudging, but for creating a
containing block for position: absolute children.
In plain language: “Make this card the reference point for the badge inside it.”
.panel {
position: relative;
}
.badge {
position: absolute;
top: -10px;
right: -10px;
}
.panel {
position: static;
}
.badge {
position: absolute;
top: -10px;
right: -10px;
}
.panel {
position: relative;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stage {
display: grid;
gap: 12px;
}
.panel {
width: 360px;
border: 3px solid #111;
border-radius: 18px;
background: #fff;
padding: 18px;
min-height: 170px;
box-shadow: 0 12px 0 #111;
}
.badge {
border: 2px solid #111;
border-radius: 999px;
padding: 6px 10px;
background: #f6f6f6;
font-weight: 900;
}
p {
margin: 0;
}
.small {
opacity: 0.85;
}
NEWPanel content
Toggle snippets: when the panel isn’t relative, the badge “escapes” and positions to a different containing block.
Learn more about position: absolute; in the CSS Position Absolute Interactive Tutorial.
CSS position: relative to parent
This phrase is super common, but slightly misleading.
- When an element is relative, its offsets are calculated from where it would have been in the normal flow (its original position), not “from the parent’s top-left corner.”
- However, if the element is inside a parent, its “would-have-been” position is naturally influenced by that parent’s layout (padding, flex/grid rules, etc.).
So people say “relative to parent” because the parent affects layout, but the offset reference is still the element’s own normal position.
.parent {
padding: 10px;
}
.child {
position: relative;
left: 18px;
top: 10px;
}
.parent {
padding: 40px;
}
.child {
position: relative;
left: 18px;
top: 10px;
}
.parent {
padding: 10px;
}
.child {
position: relative;
left: 0px;
top: 0px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.parent {
border: 3px solid #111;
border-radius: 18px;
background: #fff;
min-height: 180px;
position: relative;
}
.child {
width: 240px;
border: 2px solid #111;
border-radius: 14px;
padding: 12px;
background: #f6f6f6;
box-shadow: 0 10px 0 #111;
}
.note {
border: 2px dashed #111;
border-radius: 14px;
padding: 10px;
background: #fafafa;
margin-top: 12px;
}
p {
margin: 0;
}
ChildI’m relative. My offsets start from where I would normally sit.
Change the parent padding in the snippets. The child’s “starting point” changes because layout changed.
CSS position: relative to another element
Pure position: relative is not a “target another element and align to it” feature.
If you want an element to visually relate to another element, you usually do one of these:
-
Use normal layout (flex/grid) to place them, then use
position: relativefor small nudges. - Wrap them in a shared container and position one relative to its normal position inside that container.
-
Use
position: absoluteinside a relative container when you truly need anchoring.
Nudge after layout
Here we lay out two cards with grid. Then we use position: relative to nudge the second card into a
playful overlap.
.card.is-2 {
position: relative;
top: -12px;
left: 12px;
}
.card.is-2 {
position: relative;
top: 0px;
left: 0px;
}
.card.is-2 {
position: relative;
top: -22px;
left: 22px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.card {
border: 2px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
box-shadow: 0 10px 0 #111;
}
.card h4 {
margin: 0 0 6px;
}
.card p {
margin: 0;
opacity: 0.85;
}
.card.is-1 {
background: #f6f6f6;
}
Card 1
Normal layout decides my position.
Card 2
Then relative offsets can add a small “designed” nudge.
CSS position: relative to sibling
Similar story: relative positioning doesn’t “attach” an element to its sibling.
But you can create sibling-aware effects by:
- Laying siblings out normally (flex/grid).
-
Nudging one sibling with
position: relativeto overlap or align aesthetically.
Watch how sibling B overlaps sibling A, while the flow spacing stays the same.
.b {
position: relative;
left: -20px;
}
.b {
position: relative;
left: 0px;
}
.b {
position: relative;
top: -14px;
left: -14px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.row {
display: flex;
gap: 12px;
align-items: stretch;
}
.box {
flex: 1;
border: 2px solid #111;
border-radius: 16px;
padding: 14px;
box-shadow: 0 10px 0 #111;
background: #fff;
}
.a {
background: #f6f6f6;
}
.box h4 {
margin: 0 0 6px;
}
.box p {
margin: 0;
opacity: 0.85;
}
Sibling A
I stay put in the normal flow.
Sibling B
I can overlap using relative offsets, but my original space still exists.
CSS position: relative to screen
position: relative is not relative to the screen. It’s relative to the element’s normal position in the flow.
If you want something “relative to the screen,” you usually mean one of these:
- position: fixed (sticks to the viewport as you scroll)
- position: sticky (sticks within a scroll container after crossing a threshold)
This playground compares relative to fixed so the difference feels obvious.
.helper {
position: relative;
top: 14px;
}
.helper {
position: fixed;
top: 14px;
right: 14px;
}
.helper {
position: sticky;
top:0;
}
.helper {
position: static;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 12px;
}
.scroller {
height: 220px;
overflow: auto;
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
}
.block {
border: 2px solid #111;
border-radius: 14px;
background: #f6f6f6;
padding: 12px;
box-shadow: 0 10px 0 #111;
margin-bottom: 12px;
}
.helper {
display: inline-block;
border: 2px solid #111;
border-radius: 999px;
padding: 6px 10px;
background: #fff;
font-weight: 900;
}
p {
margin: 0 0 10px;
}
Scroll inside this box.
HelperRelative moves me within flow. Fixed pins me to the viewport.
More content…
More content…
More content…
More content…
Relative positioning and z-index
A small but important detail: z-index only applies to positioned elements (or certain layout contexts).
Setting position: relative is a common way to make z-index “kick in.”
In other words: if you want two elements to overlap and control which is on top, relative positioning is often part of the recipe.
.one {
position: relative;
left: 18px;
z-index: 2;
}
.two {
position: relative;
left: -18px;
z-index: 2;
}
.one {
position: relative;
left: 18px;
z-index: 0;
}
.two {
position: relative;
left: -18px;
z-index: 0;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stack {
display: flex;
align-items: center;
}
.pill {
width: 220px;
border: 2px solid #111;
border-radius: 999px;
padding: 14px;
box-shadow: 0 10px 0 #111;
background: #fff;
font-weight: 900;
text-align: center;
}
.one {
background: #f6f6f6;
}
.two {
background: #fff;
}
OneTwo
Learn more about z-index; in the CSS Z-Index Interactive Tutorial.
Practical recipes you’ll actually use
Recipe: corner badge (relative parent, absolute child)
This is the classic combo: make the card position: relative, then absolutely position a badge inside
it.
.card {
position: relative;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
}
.card {
position: relative;
}
.badge {
position: absolute;
top: -10px;
right: -10px;
}
.card {
position: relative;
}
.badge {
position: absolute;
bottom: 12px;
left: 12px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.card {
border: 3px solid #111;
border-radius: 18px;
padding: 18px;
background: #fff;
box-shadow: 0 12px 0 #111;
min-height: 170px;
display: grid;
align-content: end;
gap: 8px;
}
.badge {
border: 2px solid #111;
border-radius: 999px;
padding: 6px 10px;
background: #f6f6f6;
font-weight: 900;
}
h4 {
margin: 0;
}
p {
margin: 0;
opacity: 0.85;
}
PROPricing Card
Relative positioning on the card makes the badge easy to anchor.
Recipe: tiny nudge without breaking layout
Sometimes you just want to visually align an icon or label with surrounding text. Relative positioning is perfect when you don’t want to disrupt line-height or flow.
.icon {
position: relative;
top: 2px;
}
.icon {
position: relative;
top: 0px;
}
.icon {
position: relative;
top: -2px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.line {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 16px;
box-shadow: 0 12px 0 #111;
}
.icon {
display: inline-grid;
place-items: center;
width: 26px;
height: 26px;
border: 2px solid #111;
border-radius: 8px;
background: #f6f6f6;
font-weight: 900;
margin-right: 8px;
}
p {
margin: 0;
font-size: 18px;
}
iTip: Relative offsets are great for tiny baseline nudges.
Common “relative to…” questions (answered clearly)
Is position: relative relative to parent?
Not in the “parent’s top-left corner” sense. It’s relative to the element’s normal position in the flow.
Is position: relative relative to sibling?
Not directly. But you can create sibling overlap and visual alignment by laying things out normally, then nudging one sibling.
Is position: relative relative to screen?
No. If you want screen/viewport anchoring, you’re looking for position: fixed or sometimes
position: sticky.
Why position: relative “isn’t working”
If position: relative seems to do nothing, check these first:
-
You forgot offsets.
position: relativealone doesn’t move anything. - You’re using it for screen positioning. Relative won’t pin to the viewport.
- You expected other elements to reflow. Relative doesn’t change layout space; it only shifts the painted box.
-
You’re trying to anchor a child, but the parent isn’t positioned. Use
position: relativeon the parent to contain an absolute child.
.notice {
position: relative;
}
.notice {
position: relative;
top: 12px;
left: 12px;
}
.notice {
position: relative;
bottom: 12px;
right: 12px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 12px;
}
.notice {
border: 3px solid #111;
border-radius: 16px;
background: #fff;
padding: 14px;
box-shadow: 0 12px 0 #111;
}
.notice p {
margin: 0;
opacity: 0.85;
}
Relative “not working” demoFirst snippet: relative alone moves nothing. Next snippets: add offsets and it jumps.
Summary
- position: relative keeps the element in the normal flow.
-
Offsets (
top,left, etc.) move it visually, but its original space stays reserved. - It’s commonly used to create a containing block for absolute children.
- It’s not “relative to the screen,” and it doesn’t directly “target” siblings—use layout + nudges or absolute positioning within a relative parent.
Conclusion
position: relative is a powerful tool for visual adjustments and anchoring, but it’s often
misunderstood. Remember: it’s relative to the element’s normal position, not the parent, siblings, or screen. Use it
for nudges, layering with z-index, and as a reference point for absolute children.
