v153 · enabled by default · css · scrolling
Single-axis scroll containers
An element that scrolls in one axis only — created by combining a scrollable value (scroll, auto, hidden) on one axis with overflow: clip on the other, as in overflow: scroll clip. Because the clipped axis is not a scroll container at all, position: sticky descendants resolve their constraint for that axis against the next ancestor that does scroll there — so horizontally scrolling tables can keep vertically sticky headers without JavaScript.
The milestone=153 listing files this feature as “Enabled by default” for desktop, Android, and WebView (verified 2026-07-29; the listing is authoritative per gendn invariant #2). Two implementation facts qualify that, and are recorded rather than smoothed over:
- At Chromium trunk (fetched 2026-07-29) the runtime feature
SingleAxisScrollContainersis still statusexperimentalin runtime_enabled_features.json5, and the change that flips it on by default — CL 8127263, “Enable SingleAxisScrollContainers by default” — is still NEW (unmerged), last updated 2026-07-22. The feature detail record is also stale (status “Proposed”). For local testing ahead of the default, launch Chrome with--enable-blink-features=SingleAxisScrollContainers. - Scroll-snap interaction with single-axis containers is a separate follow-on feature (
SingleAxisScrollContainersForScrollSnap, which depends on the base flag and has no enabled status anywhere at trunk — CL 7951323 added it, CL 7944227 implementing it is still open). Do not assume snap points behave per-axis yet; csswg-drafts #14018 is still clarifying that behavior.
at a glance
| What it is | A scroll container scrollable in only one axis, plus the spec machinery that makes position: sticky resolve its constraint per axis against the nearest ancestor scrollable in that axis |
|---|---|
| How you create one | A scrollable value on one axis with clip on the other: overflow: scroll clip, overflow: auto clip, or longhands such as overflow-x: auto; overflow-y: clip |
Why clip and not hidden | hidden is a scrollable value — the axis stays a (programmatically scrollable) scroll container, so sticky still binds to it in both axes. clip is non-scrollable: the axis is not a scroll container at all |
| Milestone listing | Chrome 153 — Enabled by default (listing, verified 2026-07-29); desktop, Android, WebView per the detail record |
| Runtime feature | SingleAxisScrollContainers — status experimental at trunk (see the ship-status note); enable locally with --enable-blink-features=SingleAxisScrollContainers |
| Spec | CSS Overflow 3 §3.1 (scrollable/non-scrollable values, single-axis definition) + CSS Position 3 §3.4 (per-axis sticky resolution); csswg-drafts #12289 umbrella issue |
| WPT | Yes — css/css-overflow/single-axis-* (6 files) and css/css-position/sticky/position-sticky-overflow-clip-container.html; see web platform tests |
| ChromeStatus | 5067363861004288 — Single-axis scroll containers (Blink component Blink>Layout) |
reference routes
overflow: scroll clip — combination semantics
The two-keyword grammar, per-axis computed-value rules, what the disabled axis does to scroll offsets and programmatic scrolling.
position: sticky per-axis resolution
How sticky constraints resolve against the nearest scroll container with a matching scrollable axis — the split-axis table pattern.
why it exists
CSS sticky positioning historically resolved against a single nearest scroll container for both axes. That breaks one of the most common layout patterns on the web: a wide table or data grid that scrolls horizontally inside a vertically scrolling page. The intent thread states the motivation plainly: the header should remain visible as the page scrolls vertically, while horizontally sticky content should stay attached to the inner scroller — but because the inner horizontal scroller became the sticky reference for both axes, “vertical sticking becomes ineffective”. Authors worked around it with duplicated headers, DOM restructuring, or JavaScript scroll synchronization.
The missing primitive was an element scrollable in one axis only. overflow-x: auto; overflow-y: hidden looks like it should do that, but hidden is a scrollable value: the y-axis remains a scroll container (programmatic scrolling still works), so the element is a dual-axis scroll container and sticky binds to it in both axes. Pairing a scrollable value with clip — a non-scrollable value that forbids scrolling entirely — produces a true single-axis scroll container, and the spec’s sticky rules then route each axis’s constraint to the nearest ancestor that actually scrolls in that axis.
how it works
CSS Overflow 3 classifies the overflow values and defines what each combination produces. Verbatim from §3.1:
“The
scroll,auto, andhiddenvalues are known as the scrollable values ofoverflow. They cause the box to be a scroll container and the affected axis to be a scrollable axis. […] Thevisibleandclipvalues are known as the non-scrollable values. However, if the other axis specifies a scrollable value, a specified value ofvisiblecomputes toauto, enabling scrolling in its axis. If neither axis computes to a scrollable value, the box is not a scroll container. If only one axis computes to a scrollable value (i.e. the other axis isclip), the box is a single-axis scroll container.”
CSS Overflow 3 §3 then names the container types: a single-axis scroll container is “a scroll container that is scrollable in only one axis”, a dual-axis scroll container is scrollable in both, and the x/y/block/inline-axis scroll container terms mean “scrollable in the specified axis, regardless of whether it can also scroll in the other axis”.
On the sticky side, CSS Position 3 §3.4 now resolves the constraint per axis — verbatim: “For a sticky positioned box, the inset properties represent insets from the respective edges of the scrollport of the nearest scroll container with a matching scrollable axis, defining the sticky view rectangle used to constrain the box’s position. (The sticky view rectangle can be constructed from edges of two different scrollports, if the nearest scroll container is a single-axis scroll container.)”
| Axis values (x / y) | Result | Sticky consequence |
|---|---|---|
scroll / clip (or auto / clip) | Single-axis scroll container (x only) | Vertical sticky constraints skip this element; they resolve against the next ancestor scrollable in y |
scroll / hidden | Dual-axis scroll container (hidden is scrollable — programmatic scrolling still works) | Binds sticky in both axes — the old behavior that broke split-axis layouts |
scroll / visible | visible computes to auto → dual-axis scroll container | Binds sticky in both axes (unchanged long-standing rule) |
clip / clip | Not a scroll container | No sticky constraint from this element in either axis |
scroll APIs on the disabled axis
A single-axis scroll container still measures its full overflow, but the clipped axis holds no scroll offset and cannot be given one — established by the upstream WPT assertions:
scrollWidth/scrollHeightstill report the full content size (300×300 in the test fixture) — clipping does not shrink measured overflow.scrollLefton the x-clipped axis reads0and stays0:scrollTo()/scrollBy()calls that target the clipped axis do not move it, while the scrollable axis responds normally (scrollTopadvances to 50, then 70 in the test).- Dynamically changing an axis from a scrollable value to
clipclamps that axis back to scroll offset 0 and repaints (single-axis-overflow-scroll-to-clip.html: “A scroller with overflow: hidden has one of its axes set to overflow: clip. It repaints with the axis set to overflow: clip at a scroll offset of 0.”). scrollIntoView()scrolls the scrollable axis only (single-axis-scroll-into-view.html, plus an RTL variant).
examples
The canonical pattern — a wide data grid that scrolls horizontally inside a vertically scrolling page, keeping its header sticky against the page:
.grid-viewport {
/* scroll horizontally, but do NOT become a scroll container vertically */
overflow-x: auto;
overflow-y: clip; /* = overflow: auto clip */
}
.grid-header {
position: sticky;
top: 0; /* constrains against the nearest y-axis scroll
container — the page, not .grid-viewport */
}
.grid-first-col {
position: sticky;
left: 0; /* constrains against .grid-viewport, the nearest
x-axis scroll container */
}
Before this feature the same markup needed overflow-y: hidden — which silently kept the element a dual-axis scroll container and broke the header’s vertical stickiness — or JavaScript scroll synchronization.
Ensuring a clipped axis truly stays in place (the second half of the listing summary):
.no-vertical-drift {
overflow-y: clip; /* no scrolling in y through ANY mechanism —
not wheel, not touch, not scrollTo() */
overflow-x: scroll;
}
The Chrome Platform Showcase route for this feature returned 404 at check time (2026-07-29), so no live demo is embedded — the snippets above are the reference examples.
Source: pattern per the Intent to Prototype motivation and the listing summary; semantics per CSS Overflow 3 §3.1web platform tests
Upstream WPT coverage exists in two directories (fetched 2026-07-29):
css/css-overflow/single-axis-overflow-scroll-to-clip.html(+ RTL ref) — dynamic scroll-to-clip transitions clamp the axis to offset 0css/css-overflow/single-axis-overflow-clip-rtl.html— clipped-axis behavior in RTLcss/css-overflow/single-axis-scroll-apis-programmatic.html— scroll metrics and programmatic scrolls on the disabled axiscss/css-overflow/single-axis-scroll-apis-dynamic.html— the same under dynamic style changescss/css-overflow/single-axis-scroll-into-view.html(+ RTL) — scrollIntoView on single-axis containerscss/css-position/sticky/position-sticky-overflow-clip-container.html— “sticky elements do not consider overflow:clip containers as possible scroll ancestor”
browser compatibility
Interim table. No BCD key records this behavior — BCD css/properties/overflow.json has multiple_keywords (generic two-keyword overflow syntax: Chrome 68, Firefox 61, Safari 13.1) and a clip value key, but neither records single-axis scroll-container behavior or per-axis sticky resolution, and there is no dedicated web-features entry (webstatus query, 2026-07-29). Treating “multiple keywords supported” as support for this feature would be wrong; rows below are compiled from the linked primary sources.
| Browser | Support | Evidence |
|---|---|---|
| Chrome | Enabled by default 153 (per the listing); runtime flag SingleAxisScrollContainers experimental at trunk, enable-by-default CL pending — see the ship-status note | milestone=153 listing; flag record; CL 8127263 |
| Edge | Not separately reported | Chromium-based; no separate position on the ChromeStatus record |
| Firefox | No signal; no public implementation recorded | mozilla/standards-positions #1418 (open, no position label, 2026-07-29) |
| Safari | No signal; no public implementation recorded | WebKit/standards-positions #680 (open, no position label, 2026-07-29) |
security and privacy
- No new data exposure — this is a layout/scrolling behavior change; it reads nothing, stores nothing, and sends nothing over the network.
- No permission or policy surface — no Permissions-Policy directive, no enterprise policy, no user prompt is involved.
- Fingerprinting — per-axis sticky resolution and clipped-axis scroll clamping are observable through layout and scroll-offset reads (
scrollTop/getBoundingClientRect()), but they reveal only the browser engine and version family, which layout behavior already reveals. No new fingerprinting vector was identified in the sources read. - Usability consideration —
clipforbids scrolling through any mechanism on that axis, including keyboard and assistive-technology scrolling; content clipped on that axis must not be essential-or-inaccessible. This is an authoring hazard ofoverflow: clipgenerally, not specific to this feature.
specifications
| Document | Status |
|---|---|
| CSS Overflow Module Level 3 — §3.1 overflow properties (scrollable/non-scrollable values, single-axis rule) and §3 (scroll container type definitions) | Editor’s Draft; the definitions quoted on this page are current as of 2026-07-29 |
| CSS Positioned Layout Level 3 §3.4 — sticky positioning (per-axis resolution, sticky view rectangle) | Editor’s Draft, current as of 2026-07-29 |
| csswg-drafts PR #13903 — update “scroll container” mentions to refer to scrollable axes | Merged 2026-06-30; the chromestatus record’s spec link |
| csswg-drafts #12289 — allow scrollable overflow to be clipped in off-axis | Open — the umbrella issue for the feature |
| csswg-drafts #865 — sticky inside overflow:hidden|auto parents | Open (2017) — the long-standing motivation; cited as the initial public proposal in the intent |
see also
- Chrome Platform Status — Single-axis scroll containers (API record)
- blink-dev — Intent to Prototype: CSS sticky positioning in single-axis scroll containers
- Chromium tracking bug 440038212 (and 481019005 from the intent)
- CL 7697174 — track clip and scrollable overflow combinations (merged); CL 7702333 — enforce scroll axis constraints in single-axis containers (merged)
- MDN — overflow (covers the base property; no single-axis or per-axis-sticky coverage at check time, 2026-07-29 — hence this page)
- MDN — position (sticky basics)