Sticky headers have become common across documentation websites, publishing platforms, dashboards, and web applications. They allow important controls or section titles to remain visible while users move through long pages.
The basic implementation is familiar: apply position: sticky and define an offset. The browser handles the positioning without requiring JavaScript.
However, developers often want an additional behavior. A header may need a shadow or stronger background only after it actually becomes stuck at the top of its scrolling area.
Traditionally, detecting that condition has required JavaScript, often using scroll listeners or IntersectionObserver.
CSS scroll-state container queries offer another approach, allowing styles to react directly to certain browser-managed scrolling conditions. The feature first became available in Chrome 133 in 2025 and remains worth exploring as browser support evolves.
The Difference Between Being Sticky and Being Stuck
Consider a heading using the following CSS:
.section-heading {
position: sticky;
top: 0;
}The heading participates in normal document flow until scrolling brings it to the specified boundary. At that point, the browser constrains its position.
The CSS selector system has traditionally provided no direct way to ask whether that transition has occurred.
A developer may want a normal heading to remain visually quiet while it is part of the document, then acquire a background and shadow only when it becomes stuck.
Applying those styles permanently would be simple, but it would not express the actual design requirement.
Introducing scroll-state()
Scroll-state queries extend the container-query model beyond dimensions.
Instead of asking how wide a container is, the browser can evaluate conditions related to its scrolling state.
The principal query families include stuck, snapped, and scrollable. They address different questions: whether an element is constrained by sticky positioning, whether it is snapped into place, and whether further scrolling is possible in a particular direction.
Some implementations and specification developments also address the direction in which a container was most recently scrolled. Developers should verify support for each descriptor independently.
Our example focuses on the stuck condition because it provides an immediately useful application for article navigation and documentation layouts.
Building a Sticky Section Header
We’ll start with a section heading containing a title and a short label:
<section class="article-section">
<header class="section-anchor">
<div class="section-anchor__surface">
<strong>Performance</strong>
<span>Section 3 of 6</span>
</div>
</header>
<div class="article-section__content">
<p>Article content goes here.</p>
<!-- Additional paragraphs -->
</div>
</section>The section must contain enough content for scrolling to demonstrate the sticky behavior. The layout must also avoid unintended overflow containers that change the sticky positioning reference.
Next, define the container and its normal appearance:
.section-anchor {
position: sticky;
top: 0;
z-index: 10;
container-type: scroll-state;
container-name: section-anchor;
}
.section-anchor__surface {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 0.9rem 1.2rem;
background: white;
border-bottom: 1px solid #dce4ed;
}The container-type declaration makes the sticky header a query container for scroll-state conditions.
We can now define what happens when the header reaches the top boundary:
@container section-anchor
scroll-state(stuck: top) {
.section-anchor__surface {
background: #f4f8fe;
box-shadow:
0 5px 16px rgb(15 42 75 / 0.12);
}
}When the sticky header is actually constrained to the top of its scrolling container, the browser applies the additional styles.
Notice that the query styles a descendant element rather than the query container itself. This follows an important rule of container queries: a query container cannot directly restyle itself through its own container query.
Why This Can Be Simpler Than JavaScript
A JavaScript implementation might use an IntersectionObserver sentinel to determine when the header becomes stuck, then add or remove a CSS class.
This approach can work well and remains useful for broader browser compatibility.
However, it introduces additional elements, observation logic, and application state solely to change presentation.
With scroll-state queries, the browser already knows whether sticky positioning has reached its constraint. CSS can respond without duplicating that state in JavaScript.
This also reduces the risk of implementing inefficient scroll handlers that perform unnecessary layout measurements or DOM updates during scrolling.
Another Use Case: Scrollable Content Indicators
Scroll-state queries can also detect whether additional scrolling is possible in a particular direction.
For example, a horizontally scrollable component may contain more content beyond its visible right edge.
A scrollable query can help adapt a descendant visual indicator depending on whether the container can still be scrolled toward a particular boundary.
This is useful for horizontally scrolling navigation, data tables, or collections of cards where users may not immediately recognize that more content exists.
It is important to distinguish scrollability from scrolling direction. A container that can scroll farther right is not necessarily being scrolled to the right at that moment.
Browser Compatibility and Progressive Enhancement
Scroll-state queries are a comparatively recent CSS capability. Chrome introduced the initial implementation in version 133, but support should not be assumed across every browser and operating-system version.
The good news is that our example works as a normal sticky header even when the scroll-state query is unsupported.
The browser can ignore the unsupported conditional styling while retaining position: sticky and the basic presentation.
For critical interfaces, developers should test the actual supported browser matrix and provide a JavaScript fallback only when the changing visual state is essential to usability.
Keep performance claims measured. Replacing a JavaScript observer with CSS does not automatically prove a meaningful improvement in every application.
Conclusion
CSS scroll-state queries extend declarative styling into an area previously handled primarily through JavaScript.
For sticky section headers and scrolling indicators, they can express the relationship between interface state and visual appearance directly in CSS.
Their practical value lies in reducing unnecessary coordination code while allowing the browser to manage conditions it already understands.
As with other emerging CSS features, progressive enhancement remains the appropriate starting point: preserve functional layouts first, then add state-dependent styling where supported.






