CSS Positioning: static, relative, absolute, fixed, and sticky

The position property decides where an element actually lands on the page. Learn static, relative, absolute, fixed, and sticky positioning, how the top/right/bottom/left offsets work, and what 'positioning context' really means.

Published July 31, 20269 min readBy ACY Partner Indonesia
CSS positioning — the position property and its five values
300 × 250Ad Space AvailablePlace your ad here

Most of the time, elements land on the page exactly where the normal document flow puts them — one after another, top to bottom, left to right. That default is great, and you should lean on it. But every so often you need to break out of that flow: pin a banner to the top of the screen, drop a little badge in the corner of a card, or keep a sidebar heading visible as the user scrolls. That’s what the position property is for.

Positioning trips a lot of people up because the values sound similar but behave very differently, and because they all hinge on one idea — the positioning context — that nobody explains clearly. Once that clicks, the whole thing stops being mysterious. Let’s walk through all five values and the rules that tie them together.

The position property and its five values

Every element has a position, and it’s one of five values:

.element {
  position: static;   /* the default */
  /* position: relative; */
  /* position: absolute; */
  /* position: fixed; */
  /* position: sticky; */
}

The value you choose changes two things: whether the element stays in the normal flow, and what the top, right, bottom, and left offset properties do to it. Those four offsets are the steering wheel of positioning — but they only have an effect once you’ve moved away from static.

top/right/bottom/left do nothing on static elements

This is the number-one beginner confusion: you set top: 20px on an element, nothing happens, and you assume CSS is broken. It isn’t. The offset properties are simply ignored while position: static (the default). They only kick in once you switch to relative, absolute, fixed, or sticky. If your offsets aren’t working, check the position value first.

static — the default flow

position: static is what every element starts as. The element sits wherever the normal document flow places it, and top/left/etc. are ignored. You rarely write position: static yourself — it’s mostly useful for resetting an element back to normal after it inherited or was given another value.

.box {
  position: static; /* sits in normal flow; offsets ignored */
}

There’s nothing more to it. Static is the calm baseline everything else departs from.

relative — nudge from where it already is

position: relative keeps the element in the normal flow but lets you offset it from its own original spot. The space it originally occupied is still reserved, so neighboring elements don’t move to fill the gap — the element just visually shifts.

.box {
  position: relative;
  top: 10px;    /* move down 10px from where it was */
  left: 20px;   /* move right 20px from where it was */
}

Read those offsets carefully: top: 10px pushes the element down (away from the top), and left: 20px pushes it right (away from the left). The element moves relative to itself, leaving a ghost-shaped gap behind.

On its own, relative is only mildly useful for nudging things. Its real superpower is something else entirely: it creates a positioning context for absolutely-positioned children. Hold that thought — it’s the key to the next section.

absolute — pulled out of flow, placed against an ancestor

position: absolute is the big one. It removes the element from the normal flow entirely (neighbors close up as if it were never there) and positions it using the offsets — but relative to its nearest positioned ancestor, not to itself.

“Positioned ancestor” means the closest parent (or grandparent, etc.) that has a position other than static — usually relative. If no such ancestor exists, the element is positioned against the page itself (the initial containing block).

.card {
  position: relative;   /* becomes the positioning context */
}

.badge {
  position: absolute;
  top: 8px;
  right: 8px;           /* pin to the top-right corner of .card */
}
<div class="card">
  <span class="badge">New</span>
  <p>Card content goes here.</p>
</div>

Because .card is relative, the absolutely-positioned .badge measures its top and right from .card’s edges — landing it neatly in the top-right corner. This relative parent + absolute child pairing is one of the most useful patterns in all of CSS: badges, dropdown menus, tooltips, close buttons, image overlays. Learn it once and you’ll use it constantly.

The golden rule: relative parent, absolute child

When you want to place something precisely inside a box, give the box position: relative and the inner element position: absolute. The relative parent anchors the absolute child. Forget the relative on the parent and your “corner badge” will fly off to the corner of the whole page instead — that’s the classic bug, and now you know the fix.

If you set all four offsets at once on an absolute element, it stretches to fill its positioning context:

.overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;        /* covers the entire positioned ancestor */
}

That’s a tidy way to lay a semi-transparent overlay across a whole card or hero image.

fixed — pinned to the viewport

position: fixed also pulls the element out of the flow, but it pins it to the viewport — the visible browser window — rather than to any ancestor. The element then stays put even as the page scrolls.

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

That’s exactly how a floating “back to top” button, a cookie banner, or a sticky header that never moves is built. Because it’s measured against the viewport, scrolling has no effect — the element is glued to that spot on the screen.

Fixed on mobile, and the transform gotcha

Two things to watch with fixed. First, on mobile, fixed bars can overlap content or fight with the on-screen keyboard, so test on a real device and leave room with padding. Second — and this surprises everyone — if any ancestor has a transform, filter, or perspective set, your fixed element becomes fixed relative to that ancestor instead of the viewport. If a fixed element mysteriously scrolls away, look for a transform on a parent.

sticky — relative until it sticks

position: sticky is the clever hybrid, and the newest of the bunch. The element behaves like relative (staying in flow) until it scrolls to a threshold you set, at which point it “sticks” and behaves like fixed — but only while its container is still on screen.

.section-heading {
  position: sticky;
  top: 0;       /* sticks once it reaches the top of the viewport */
}

As you scroll, the heading scrolls normally until it hits the top edge, then it freezes there while the rest of that section scrolls past underneath. Once the section is gone, the heading scrolls away with it. This is how those “sticky” category headers in long lists, table headers, and section navigations work.

Two things are non-negotiable for sticky to work: you must specify a threshold (like top: 0), and the element needs a scrollable ancestor with room to scroll. A common silent failure is an ancestor with overflow: hidden, which quietly disables stickiness.

Quick comparison

Here’s the whole family side by side, which is the fastest way to keep them straight:

Value In normal flow? Offsets measured from Scrolls with page?
static Yes (offsets ignored) Yes
relative Yes (space kept) Its own original position Yes
absolute No Nearest positioned ancestor Yes (with that ancestor)
fixed No The viewport No (stays put)
sticky Yes, then pins Nearest scroll container Until the threshold, then sticks

If you remember just one row of context: absolute is measured against an ancestor, fixed against the viewport. That single distinction explains most of the “why is my element in the wrong place” headaches.

A practical example: a card with a corner badge

Let’s build the most common positioning pattern you’ll actually ship — a product card with a “Sale” badge pinned to its corner:

.product {
  position: relative;      /* the anchor */
  width: 240px;
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.product .badge {
  position: absolute;
  top: 12px;
  right: 12px;
  background: #00b8e6;
  color: #fff;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
}
<div class="product">
  <span class="badge">Sale</span>
  <h3>ACY Partner Indonesia Plan</h3>
  <p>Everything you need to get started.</p>
</div>

The card is relative so it becomes the reference frame; the badge is absolute and sits 12px from the top-right corner of that card, no matter how tall the card grows. Resize, add text, change the layout — the badge stays glued to its corner. That resilience is the whole point of the relative/absolute pair.

Best practices and common mistakes

A few habits will save you a lot of debugging:

  • Reach for flow and Flexbox/Grid first. Positioning is for overlaying and pinning, not for general layout. If you’re using absolute to build a whole page structure, step back — Flexbox and Grid are the right tools, and they’re far more robust.
  • Always set position: relative on the parent before placing an absolute child. This is the single most common positioning bug.
  • Don’t forget the offset on sticky. position: sticky without a top (or bottom/left/right) value does nothing at all.
  • Remember that absolute and fixed remove the element from flow. Surrounding content collapses as if the element vanished, which can cause overlaps. Reserve space with padding or margins on neighbors if needed.
  • Mind stacking order. When positioned elements overlap, which one wins is controlled by z-index, which only applies to positioned elements — that’s a whole topic of its own, and a natural next step from here.

Positioning also assumes you’re comfortable with how an element’s size is built up from content, padding, border, and margin. If any of that feels shaky, it’s worth revisiting the CSS box model before going further — every offset you set interacts with it.

Wrapping up

The position property is how you take an element out of the ordinary flow and place it deliberately. Here’s the whole picture in a few lines:

  • static is the default — normal flow, offsets ignored.
  • relative nudges an element from its own spot and creates a positioning context for absolute children.
  • absolute removes the element from flow and positions it against the nearest positioned ancestor (the classic relative parent, absolute child pattern).
  • fixed pins the element to the viewport, so it ignores scrolling.
  • sticky acts like relative until a scroll threshold, then pins like fixed within its container.

Get comfortable with the positioning context idea — the question “what is this measured against?” — and CSS positioning becomes one of the most predictable, useful tools in your kit. Next, when positioned elements start overlapping each other, you’ll want to control which one shows on top. That’s z-index, and it’s where we head next.

Tags:cssfrontendpositioninglayoutintermediate
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read