← Chrome 149 reference

v149 · css · shipped

CSS scroll-state() Container Queries

Style elements based on their scroll container's live state — whether a sticky item is stuck, a snap target is currently snapped, or a scroll container has overflowed — without any JavaScript polling.

at a glance

Shipped inChrome 149 (desktop + Android)
StatusEnabled by default
FlagNone
Standards positionSpec in CSS Conditional Rules Level 5
SpecCSS Conditional Rules Level 5 §scroll-state
ChromeStatuschromestatus.com/feature/5072263730167808

why it exists

Before scroll-state(), reacting to sticky headers, snap targets, or scrollable overflow required JavaScript observers — IntersectionObserver for stickiness, MutationObserver hacks for snap, ResizeObserver for overflow. These all involve layout recalculation callbacks that race with painting. scroll-state() exposes three commonly-needed scroll signals as CSS custom container properties, letting the browser style them synchronously during its own paint pass with no script overhead.

Source: CSS Conditional Rules Level 5 §scroll-state motivation.

shape of the feature

Step 1: establish a scroll-state container

Add container-type: scroll-state to the element you want to query. You can combine it with a container-name for targeting.

.sticky-header {
  position: sticky;
  top: 0;
  container-type: scroll-state;
  container-name: sticky-nav;
}

Step 2: query with @container scroll-state()

Use the @container scroll-state() at-rule inside the container or on its descendants.

Supported scroll-state features

FeatureValuesMatches when…
stuck none | top | right | bottom | left | block-start | block-end | inline-start | inline-end The container has position: sticky and is visually shifted to stay inside its sticky view rectangle on that edge. none matches when not stuck on any edge.
snapped none | x | y | block | inline | both The container is a snap target that is (or would be) snapped to its scroll container on the given axis. none when not snapped.
scrollable none | top | right | bottom | left | block-start | block-end | inline-start | inline-end | x | y | block | inline | both The container is a scroll container with clipped content in that direction that the user can scroll to. Does not match for overflow: hidden containers.
Source: CSS Conditional Rules Level 5 §scroll-state features.

example: style a sticky header when stuck

Show a box-shadow only when the header is actually pinned to the top — no JavaScript.

.page-header {
  position: sticky;
  top: 0;
  container-type: scroll-state;
  background: white;
  transition: box-shadow 0.2s;
}

@container scroll-state(stuck: top) {
  .page-header {
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
  }
}

example: highlight the currently snapped carousel item

.carousel-item {
  container-type: scroll-state;
  opacity: 0.6;
  transition: opacity 0.2s;
}

@container scroll-state(snapped: x) {
  .carousel-item {
    opacity: 1;
  }
}

example: show a "more content below" indicator

.article {
  overflow-y: auto;
  max-height: 400px;
  container-type: scroll-state;
  container-name: article-scroller;
}

.scroll-hint {
  display: none;
}

@container article-scroller scroll-state(scrollable: bottom) {
  .scroll-hint {
    display: block; /* visible only when there's more to scroll down */
  }
}

browser support

Chrome149+ (enabled by default)
Edge149+ (tracking Chromium)
FirefoxNo signal yet
SafariNo signal yet
Source: chromestatus.com/feature/5072263730167808, May 2026.

see also