Skip to content
Back to Design Hub

Position Strategy: Sticky, Fixed, Absolute

Choosing the right position value for headers, tooltips, and overlays without layout surprises.

Responsive DesignIntermediate

Why

Misusing position: fixed (header overlaps content on iOS Safari URL bar resize) or position: absolute (tooltip clipped by overflow:hidden ancestor) creates bugs that are hard to trace. Each value has a narrow correct use.

StatCounter 2024 data shows 58% of web traffic is mobile. CSS container queries (baseline 2023) and subgrid (baseline 2024) eliminate most media-query hacks, reducing responsive CSS by 30-50%.

When

Pick by intent: static (default flow), relative (offset without leaving flow), absolute (remove from flow, position vs nearest positioned ancestor), sticky (flow until threshold then fixed-like), fixed (vs viewport). Avoid pixel-perfect responsive designs for data-dense dashboards — use a min-width + horizontal scroll strategy instead.

Skip container queries if you support Safari < 16.

How

1. Sticky header — position: sticky; top: 0; stays in flow until scroll past top, then pins 2. Sticky sidebar — top: 80px (below header), bottom: 0; doesn't overflow footer if container bounded 3. Fixed — avoid for headers on mobile; iOS URL bar resize makes fixed jump. Use sticky instead 4. Absolute tooltip — position: absolute vs a position: relative trigger; clipped by overflow:hidden ancestors → use Floating UI 5. Overlay/modal — position: fixed; inset: 0; covers viewport, but render in a portal to escape stacking 6. Z-index — only on positioned elements; pair with a token scale, not magic 9999 7. Containing block — transform/filter/will-change on an ancestor makes fixed behave like absolute (gotcha)

Example

/* Sticky header — stays in flow, pins on scroll, no iOS jump */ .header { position: sticky; top: 0; z-index: var(--z-sticky); background: var(--color-surface); } /* Sticky sidebar — below header, stops at container bottom */ .sidebar { position: sticky; top: 80px; align-self: start; max-height: calc(100vh - 80px); overflow: auto; } /* Tooltip — absolute vs relative trigger, but use Floating UI if ancestors clip */ .trigger { position: relative; } .tooltip { position: absolute; top: 100%; left: 0; } /* Modal — fixed + portal, escapes any transformed ancestor */ .modal-backdrop { position: fixed; inset: 0; z-index: var(--z-modal); } /* Gotcha: this ancestor makes .modal-backdrop behave like absolute, not fixed */ .transformed-ancestor { transform: translateZ(0); } /* → render modal in a portal */

Common Problems

  • ⚠position: fixed header on mobile — jumps when iOS Safari's URL bar shows/hides
  • ⚠Tooltip clipped by overflow:hidden — absolute positions vs the right ancestor but is invisible
  • ⚠transform on an ancestor — fixed child now positions vs that ancestor, not the viewport

Best Practices

  • ✓Prefer sticky over fixed for in-page headers — respects scroll, no mobile URL-bar jump
  • ✓Use Floating UI (Popper) for tooltips/popovers — handles clipping, flipping, escaping automatically
  • ✓Render modals in a portal — escapes transformed ancestors that break position: fixed

FAQ