v153 · single-axis scroll containers

Sticky, per axis

A sticky element sticks to its nearest scrolling ancestor — one ancestor, both axes. A data grid wants two: the header should follow the sheet's vertical scroll while staying with the columns as they move sideways. That needs an element that scrolls on one axis and truly does not on the other.

Scroll the sheet, watch the header

header inside overflow: scroll clip

Not measured yet.

header with no scroll container between it and the sheet

Not measured yet.

Drag the slider or press the button.

The right-hand lane is the reference, not a second feature: with nothing scrollable between the header and the sheet, sticky behaves the way everyone expects. The left-hand lane asks for the same thing while keeping a horizontal scroller in between — which only works once the vertical axis can be clipped rather than hidden.

What the grid needs

Sticky positioning is resolved against the nearest ancestor scroll container, and an element is a scroll container per axis in name only — the nearest one is a single element. Put a horizontal scroller between the header and the vertical sheet and the header's vertical stickiness is captured by that scroller, which never scrolls vertically, so it pins against an offset that never changes and slides away with the sheet.

The usual escape is to build the frozen header out of a second, synchronised element positioned outside the scroller — two DOM trees, a scroll listener, and a rounding error at every zoom level. Clipping the vertical axis removes the scroll container from that axis, so one sticky element answers the sheet vertically and the strip horizontally.

see also