A sticky element needs a non-auto inset on the sticky axis, the intended scroll container, and enough room inside its containing block to move. Flex or grid stretching can remove that room, while stacking contexts can make a correctly sticky element look hidden. Inspect computed styles and geometry before changing z-index.
Interactive CSS debugging lab
CSS position: sticky not working? Diagnose the root cause
Quick diagnosis
Interactive CSS debugging lab
Diagnose why position: sticky is not working
Check the computed inset, actual scroll container, available travel, flex or grid alignment, and paint order before changing unrelated CSS.
Missing inset
Add a non-auto inset on the sticky axis, such as top: 1rem.
Unexpected scroll container
Inspect ancestor overflow and verify which element actually scrolls.
No travel room
Compare the sticky element, containing block, and scroll range.
Flex or grid stretch
Verify item sizing before applying align-self: start.
Hidden behind another layer
Prove sticky behavior first, then inspect stacking contexts and z-index.
Interview focus
This CSS interview question tests whether you can explain CSS Sticky Not Working? 5 Causes and Fixes, connect it to production trade-offs, and handle common follow-up questions.
- CSS Sticky Not Working? 5 Causes and Fixes explanation without falling back to memorized definitions
- Position and Layout reasoning, edge cases, and production failure modes
- How you would answer the most likely CSS interview follow-up
Use this CSS interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
CSS sticky failure modes, proof, and fixes
Short answer
When position: sticky appears not to work, first prove four things: the element has a non-auto inset such as top: 0; you are scrolling the container sticky actually uses; that container has scroll range; and the sticky box has room to travel inside its containing block. If those checks pass but the element is covered, inspect paint order and stacking contexts. A larger z-index cannot repair missing scroll geometry.
Symptom | Likely cause | DevTools evidence | Smallest justified fix |
|---|---|---|---|
It behaves like relative positioning | Both insets on the sticky axis compute to auto | Computed | Set one axis-appropriate inset, such as |
It follows the wrong scroller | An ancestor establishes the nearest scroll container | Ancestor computed overflow and scroll range identify a different scroll owner | Keep the intended scroller and give it real range, or remove the accidental overflow rule |
It sticks only briefly or never visibly travels | The containing block is too short, or the sticky box consumes the available runway | Sticky height approaches parent height; parent end constrains the box immediately | Increase the containing block's useful height or reduce the sticky box's block size |
A flex/grid sidebar will not appear to move | Cross-axis stretching removes useful travel in this layout | Computed | Use |
It sticks but disappears behind content | Paint order or an ancestor stacking context covers it | The top coordinate is correct, but | Fix the owning stacking context, then use the smallest layer value required |
Five failure cases in the debugging lab
The lab above isolates one variable at a time: missing-inset, unexpected-scroll-container, no-travel-room, flex-grid-stretch, and sticks-but-hidden. Run the inspector before applying a fix. That baseline matters because several fixes can make the demo look different while leaving the original root cause unproven.
Inspect the ancestor chain in this order
- Confirm the target's computed
positionissticky; a more specific selector may have overridden it. - Check the inset for the axis you expect to stick. If both relevant insets are
auto, sticky behaves like relative positioning on that axis. - Walk ancestors and record computed
overflow-x/overflow-y. Do not assume the page viewport is the scroll owner. - Compare the sticky box with its containing block. Sticky is constrained when the opposite edge of that block is reached.
- In flex and grid layouts, inspect
align-items,align-self, and measured heights before changing alignment. - Only after the position changes correctly, inspect stacking-context owners and which element is painted at the sticky coordinates.
<li>For the actual scroll container, compare scrollHeight with clientHeight and verify its scrollTop changes.Broken and fixed example
This markup has an intentional inner scroller. The broken rule declares sticky but provides no threshold on the block axis.
<div class="results">
<aside class="filters">Filters</aside>
<main class="items">
<!-- enough results to make .results scroll -->
</main>
</div>
.results {
display: grid;
grid-template-columns: 12rem minmax(0, 1fr);
align-items: start;
max-block-size: 24rem;
overflow-y: auto;
}
/* Broken: no non-auto block-axis inset. */
.filters {
position: sticky;
}
/* Fixed for this measured layout. */
.filters {
position: sticky;
inset-block-start: 0;
}
The fix is specific to the evidence. overflow-y: auto is correct here because .results is deliberately the scroller and has a constrained block size plus overflowing content. Do not copy a blanket rule that every ancestor must use overflow: visible. Likewise, align-self: start is useful when stretching removed the runway; it is not required for every sticky element.
Sticky is not fixed
A sticky box remains in normal flow and is offset against a sticky view rectangle associated with its nearest relevant scrollport. It is also constrained by its containing block, so it stops sticking when that boundary reaches it. A fixed box is out of flow and normally uses the initial containing block. If the requirement is “stay visible for the entire viewport regardless of section boundaries,” that may be a fixed-position requirement rather than a broken sticky implementation. Review the CSS positioning model for relative, absolute, and fixed boxes before changing the behavior.
When z-index is relevantposition: sticky creates a stacking context, but that does not guarantee it will paint above a sibling in another ancestor context. If controlled scrolling shows the box holding the correct inset while content still covers it, inspect the parent contexts, paint order, and backgrounds. Fix ownership at the appropriate layer boundary instead of escalating to z-index: 999999. The z-index and stacking-context guide covers that failure separately. A transform on an ancestor can affect containing blocks or stacking, but it does not universally disable sticky positioning; prove the actual geometry and paint result.
Turn the diagnosis into assertions
- The intended scroll owner has
scrollHeight > clientHeight, and a test can change itsscrollTop. - Before the threshold, the sticky box moves with normal flow; after the threshold, its top remains within a small pixel tolerance of the resolved inset.
- The sticky box never crosses the ending edge imposed by its containing block.
- At the expected sticky coordinates,
document.elementFromPoint()resolves to the sticky box or one of its descendants when it should be visible. - Repeat at narrow and wide container sizes because content wrapping can change both scroll range and runway. Continue with responsive CSS constraints for that part of the test matrix.
Standards check
The terminology and axis behavior here follow the CSS Positioned Layout specification: sticky insets reference the nearest relevant scrollport, and the box remains constrained by its containing block. MDN's position reference also documents the non-auto inset requirement and why the nearest ancestor with a scrolling mechanism may not be the ancestor you expected to scroll.
Summary
- Start with computed
positionand a non-autoinset on the sticky axis. - Identify the actual scroll container from ancestor overflow and measured scroll range.
- Measure containing-block runway and flex/grid stretching instead of guessing.
- Use z-index only when scrolling proves the box is sticky but paint-order evidence shows it is covered.
- Test geometry at responsive widths and keep each fix tied to the failure it actually proves.
Use this as one explanation rep, then continue with the CSS interview questions cluster or a guided prep path.