Container Scroll-State Queries
When to Use
Use
@container scroll-state()when you need to style elements based on whether their scroll container is stuck, snapped, or scrollable. UseIntersectionObserverwhen cross-browser support is required — scroll-state queries are Chrome 133+ only.
Decision
| If you need... | Use... | Why |
|---|---|---|
| Add shadow to sticky header when stuck | @container scroll-state(stuck: top) |
Pure CSS stuck detection |
| Style active carousel slide | @container scroll-state(snapped: x) |
No IntersectionObserver needed |
| Show scroll indicators when content overflows | @container scroll-state(scrollable: top) |
Detect scroll position |
| Cross-browser stuck detection | IntersectionObserver with threshold | Scroll-state queries are Chromium-only |
Pattern
/* Sticky header with shadow when stuck */
.header-wrapper {
container-type: scroll-state;
position: sticky;
top: 0;
}
@container scroll-state(stuck: top) {
.header {
box-shadow: 0 2px 8px oklch(0% 0 0 / 0.15);
border-bottom: 1px solid oklch(90% 0 0);
}
}
/* Active carousel slide styling */
.carousel {
container-type: scroll-state;
scroll-snap-type: x mandatory;
}
.slide {
opacity: 0.6;
transform: scale(0.95);
transition: opacity 0.3s, transform 0.3s;
}
@container scroll-state(snapped: x) {
.slide {
opacity: 1;
transform: scale(1);
}
}
/* Scroll shadow indicators */
.scrollable-container {
container-type: scroll-state;
}
@container scroll-state(scrollable: bottom) {
.scroll-indicator-bottom { opacity: 1; }
}
@container not scroll-state(scrollable: bottom) {
.scroll-indicator-bottom { opacity: 0; }
}
Browser support: Chrome 133+, Edge 133+. Not supported in Firefox or Safari. Use as progressive enhancement with sensible defaults.
Common Mistakes
- Wrong: Omitting
container-type: scroll-state→ Right: Without declaring the container type, queries won't match - Wrong: Confusing with size container queries → Right:
container-type: scroll-stateis separate fromcontainer-type: inline-size; you can combine:container-type: inline-size scroll-state - Wrong: Expecting scroll position percentage detection → Right: Scroll-state only detects binary states (stuck/not stuck, snapped/not snapped, scrollable/not scrollable)
See Also
- Container Queries (@container) — for size-based container queries
- CSS Scroll Snap — works with
snappedstate queries - Reference: Chrome: Scroll-State Container Queries