v133 · css
Sticky Column
A horizontally-scrollable data table with a sticky first column. When the column is actually stuck to the left edge — i.e. the table has scrolled — @container scroll-state(stuck: left) fires and adds a drop-shadow and accent border automatically. No JavaScript IntersectionObserver, no scroll listeners.
Feature detection: checking…
Scroll the table right — first column detects
stuck: left
container-type: scroll-state on wrapper
| Country | Capital | Population | Area (km²) | GDP (USD bn) | Currency | Language | Time zone |
|---|---|---|---|---|---|---|---|
| Germany | Berlin | 83.2 M | 357,114 | $4,082 | Euro | German | UTC+1/+2 |
| France | Paris | 67.8 M | 551,695 | $2,778 | Euro | French | UTC+1/+2 |
| Japan | Tokyo | 125.7 M | 377,975 | $4,231 | Yen | Japanese | UTC+9 |
| Brazil | Brasília | 214.3 M | 8,515,767 | $1,920 | Real | Portuguese | UTC-3 |
| Australia | Canberra | 25.9 M | 7,692,024 | $1,688 | AUD | English | UTC+8/+11 |
| India | New Delhi | 1,428 M | 3,287,263 | $3,730 | Rupee | Hindi/English | UTC+5:30 |
| Canada | Ottawa | 38.2 M | 9,984,670 | $2,139 | CAD | English/French | UTC-3.5/−8 |
| South Korea | Seoul | 51.7 M | 100,210 | $1,709 | Won | Korean | UTC+9 |
Pre-133 vs Chrome 133+
Pre-Chrome 133
To detect a stuck sticky column, authors had to set up an
IntersectionObserver watching a 1px sentinel element just before the sticky column, toggle a class when it left the viewport, and remove it on un-stick. Each table needed its own observer, and the sentinel had to be carefully positioned to avoid false positives.
Chrome 133+
Set
container-type: scroll-state on the scrollable table wrapper. Write @container scroll-state(stuck: left) for the sticky cells. The browser fires the query automatically when the cell sticks to the left edge — shadow, border, and font-weight all update in CSS, with zero JavaScript.
/* 1. Mark the scroll container */
.data-table {
container-type: scroll-state;
overflow-x: auto;
}
/* 2. Sticky first column as usual */
.col-name {
position: sticky;
left: 0;
z-index: 2;
}
/* 3. Style the stuck state — fires when column hits the left edge */
@container scroll-state(stuck: left) {
.col-name {
background: var(--bg-stone);
border-right: 3px solid var(--text-black);
box-shadow: 3px 0 8px -2px rgba(0, 0, 0, 0.18);
font-weight: 700;
}
}
/*
Other stuck: values:
stuck: top — sticky header hits the top of the viewport
stuck: right — sticky right column hits the right edge
stuck: bottom — sticky footer hits the bottom
All use the same container-type: scroll-state pattern.
*/