32 CSS Timelines21 / 32
Zigzag Alternating Timeline — Container Query Responsive
The classic center-spine zigzag — cards alternating left and right of a vertical rail with arrow notches pointing at their markers — made truly portable: it reshapes to a single left-rail column when its CONTAINER drops below 560px, not the viewport. Drop it in a sidebar, a modal or a split view and it lays itself out correctly with zero configuration.
Published Updated
The code
<section class="tls-21" aria-label="Zigzag container query timeline demo">
<div class="tls-21__frame">
<h3 class="tls-21__title">Company milestones</h3>
<div class="tls-21__container">
<ol class="tls-21__grid" role="list">
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2022">2022</time>
<h4 class="tls-21__h">Founded in a garage</h4>
<p class="tls-21__p">Two founders, one whiteboard, no name for six weeks.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2023">2023</time>
<h4 class="tls-21__h">Seed round</h4>
<p class="tls-21__p">$2.4M to hire the first five engineers and a very good office dog.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2024">2024</time>
<h4 class="tls-21__h">First enterprise customer</h4>
<p class="tls-21__p">A 3,000-seat rollout that rewrote our definition of 'edge case'.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2025">2025</time>
<h4 class="tls-21__h">Series A</h4>
<p class="tls-21__p">$18M led by our earliest angel. The dog got a title.</p>
</div>
</li>
</ol>
</div>
</div>
</section><section class="tls-21" aria-label="Zigzag container query timeline demo">
<div class="tls-21__frame">
<h3 class="tls-21__title">Company milestones</h3>
<div class="tls-21__container">
<ol class="tls-21__grid" role="list">
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2022">2022</time>
<h4 class="tls-21__h">Founded in a garage</h4>
<p class="tls-21__p">Two founders, one whiteboard, no name for six weeks.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2023">2023</time>
<h4 class="tls-21__h">Seed round</h4>
<p class="tls-21__p">$2.4M to hire the first five engineers and a very good office dog.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2024">2024</time>
<h4 class="tls-21__h">First enterprise customer</h4>
<p class="tls-21__p">A 3,000-seat rollout that rewrote our definition of 'edge case'.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2025">2025</time>
<h4 class="tls-21__h">Series A</h4>
<p class="tls-21__p">$18M led by our earliest angel. The dog got a title.</p>
</div>
</li>
</ol>
</div>
</div>
</section>.tls-21,
.tls-21 *,
.tls-21 *::before,
.tls-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tls-21 {
min-height: 100vh;
width: 100%;
--tl-accent: #0e7490;
--tl-line: #d3e3e8;
--tl-card: #ffffff;
--tl-bg: #f2f7f9;
--tl-ink: #164252;
--tl-mut: #5e7d8a;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--tl-bg);
padding: 64px 24px;
display: grid;
place-items: center;
}
.tls-21__frame {
width: min(760px,100%);
}
.tls-21__title {
font-size: clamp(22px,3.5vw,28px);
font-weight: 800;
color: var(--tl-ink);
letter-spacing: -.02em;
text-align: center;
margin-bottom: 36px;
}
.tls-21__container {
container-type: inline-size;
}
.tls-21__grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 48px 1fr;
}
.tls-21__item {
position: relative;
padding-bottom: 28px;
}
.tls-21__item:nth-child(odd) {
grid-column: 1;
}
.tls-21__item:nth-child(even) {
grid-column: 3;
}
.tls-21__item::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: var(--tl-line);
}
.tls-21__item:nth-child(odd)::before {
right: -25px;
}
.tls-21__item:nth-child(even)::before {
left: -25px;
}
.tls-21__item:last-child::before {
bottom: auto;
height: 28px;
}
.tls-21__item::after {
content: '';
position: absolute;
top: 22px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--tl-accent);
border: 3px solid var(--tl-bg);
z-index: 1;
}
.tls-21__item:nth-child(odd)::after {
right: -31px;
}
.tls-21__item:nth-child(even)::after {
left: -31px;
}
.tls-21__card {
position: relative;
background: var(--tl-card);
border: 1px solid var(--tl-line);
border-radius: 14px;
padding: 18px 20px;
box-shadow: 0 2px 10px -6px rgba(20,66,82,.15);
}
.tls-21__card::after {
content: '';
position: absolute;
top: 22px;
width: 12px;
height: 12px;
background: var(--tl-card);
border: 1px solid var(--tl-line);
rotate: 45deg;
}
.tls-21__item:nth-child(odd) .tls-21__card::after {
right: -7px;
border-left: none;
border-bottom: none;
}
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
border-right: none;
border-top: none;
}
.tls-21__date {
font-size: 12px;
font-weight: 800;
letter-spacing: .1em;
color: var(--tl-accent);
}
.tls-21__h {
font-size: 16.5px;
font-weight: 750;
color: var(--tl-ink);
margin: 5px 0 5px;
letter-spacing: -.01em;
}
.tls-21__p {
font-size: 13.5px;
line-height: 1.55;
color: var(--tl-mut);
text-wrap: pretty;
}
@container (max-width: 560px) {
.tls-21__grid {
grid-template-columns: 24px 1fr;
}
.tls-21__item:nth-child(odd),
.tls-21__item:nth-child(even) {
grid-column: 2;
}
.tls-21__item:nth-child(odd)::before,
.tls-21__item:nth-child(even)::before {
left: -13px;
right: auto;
}
.tls-21__item:nth-child(odd)::after,
.tls-21__item:nth-child(even)::after {
left: -19px;
right: auto;
}
.tls-21__item:nth-child(odd) .tls-21__card::after,
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
right: auto;
border: 1px solid var(--tl-line);
border-right: none;
border-top: none;
}
}.tls-21,
.tls-21 *,
.tls-21 *::before,
.tls-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tls-21 {
min-height: 100vh;
width: 100%;
--tl-accent: #0e7490;
--tl-line: #d3e3e8;
--tl-card: #ffffff;
--tl-bg: #f2f7f9;
--tl-ink: #164252;
--tl-mut: #5e7d8a;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--tl-bg);
padding: 64px 24px;
display: grid;
place-items: center;
}
.tls-21__frame {
width: min(760px,100%);
}
.tls-21__title {
font-size: clamp(22px,3.5vw,28px);
font-weight: 800;
color: var(--tl-ink);
letter-spacing: -.02em;
text-align: center;
margin-bottom: 36px;
}
.tls-21__container {
container-type: inline-size;
}
.tls-21__grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 48px 1fr;
}
.tls-21__item {
position: relative;
padding-bottom: 28px;
}
.tls-21__item:nth-child(odd) {
grid-column: 1;
}
.tls-21__item:nth-child(even) {
grid-column: 3;
}
.tls-21__item::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: var(--tl-line);
}
.tls-21__item:nth-child(odd)::before {
right: -25px;
}
.tls-21__item:nth-child(even)::before {
left: -25px;
}
.tls-21__item:last-child::before {
bottom: auto;
height: 28px;
}
.tls-21__item::after {
content: '';
position: absolute;
top: 22px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--tl-accent);
border: 3px solid var(--tl-bg);
z-index: 1;
}
.tls-21__item:nth-child(odd)::after {
right: -31px;
}
.tls-21__item:nth-child(even)::after {
left: -31px;
}
.tls-21__card {
position: relative;
background: var(--tl-card);
border: 1px solid var(--tl-line);
border-radius: 14px;
padding: 18px 20px;
box-shadow: 0 2px 10px -6px rgba(20,66,82,.15);
}
.tls-21__card::after {
content: '';
position: absolute;
top: 22px;
width: 12px;
height: 12px;
background: var(--tl-card);
border: 1px solid var(--tl-line);
rotate: 45deg;
}
.tls-21__item:nth-child(odd) .tls-21__card::after {
right: -7px;
border-left: none;
border-bottom: none;
}
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
border-right: none;
border-top: none;
}
.tls-21__date {
font-size: 12px;
font-weight: 800;
letter-spacing: .1em;
color: var(--tl-accent);
}
.tls-21__h {
font-size: 16.5px;
font-weight: 750;
color: var(--tl-ink);
margin: 5px 0 5px;
letter-spacing: -.01em;
}
.tls-21__p {
font-size: 13.5px;
line-height: 1.55;
color: var(--tl-mut);
text-wrap: pretty;
}
@container (max-width: 560px) {
.tls-21__grid {
grid-template-columns: 24px 1fr;
}
.tls-21__item:nth-child(odd),
.tls-21__item:nth-child(even) {
grid-column: 2;
}
.tls-21__item:nth-child(odd)::before,
.tls-21__item:nth-child(even)::before {
left: -13px;
right: auto;
}
.tls-21__item:nth-child(odd)::after,
.tls-21__item:nth-child(even)::after {
left: -19px;
right: auto;
}
.tls-21__item:nth-child(odd) .tls-21__card::after,
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
right: auto;
border: 1px solid var(--tl-line);
border-right: none;
border-top: none;
}
}/* No JavaScript — container-type:inline-size on the wrapper + one @container (max-width:560px) block flips the zigzag to a single left-rail column based on the CONTAINER's width, not the viewport. */
/* No JavaScript — container-type:inline-size on the wrapper + one @container (max-width:560px) block flips the zigzag to a single left-rail column based on the CONTAINER's width, not the viewport. */Here's a working CSS Timeline from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Zigzag Alternating Timeline — Container Query Responsive
Source: https://codefronts.com/layouts/css-timelines/vertical-centered/
The classic center-spine zigzag — cards alternating left and right of a vertical rail with arrow notches pointing at their markers — made truly portable: it reshapes to a single left-rail column when its CONTAINER drops below 560px, not the viewport. Drop it in a sidebar, a modal or a split view and it lays itself out correctly with zero configuration.
## HTML
```html
<section class="tls-21" aria-label="Zigzag container query timeline demo">
<div class="tls-21__frame">
<h3 class="tls-21__title">Company milestones</h3>
<div class="tls-21__container">
<ol class="tls-21__grid" role="list">
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2022">2022</time>
<h4 class="tls-21__h">Founded in a garage</h4>
<p class="tls-21__p">Two founders, one whiteboard, no name for six weeks.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2023">2023</time>
<h4 class="tls-21__h">Seed round</h4>
<p class="tls-21__p">$2.4M to hire the first five engineers and a very good office dog.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2024">2024</time>
<h4 class="tls-21__h">First enterprise customer</h4>
<p class="tls-21__p">A 3,000-seat rollout that rewrote our definition of 'edge case'.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2025">2025</time>
<h4 class="tls-21__h">Series A</h4>
<p class="tls-21__p">$18M led by our earliest angel. The dog got a title.</p>
</div>
</li>
</ol>
</div>
</div>
</section>
```
## CSS
```css
.tls-21,
.tls-21 *,
.tls-21 *::before,
.tls-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tls-21 {
min-height: 100vh;
width: 100%;
--tl-accent: #0e7490;
--tl-line: #d3e3e8;
--tl-card: #ffffff;
--tl-bg: #f2f7f9;
--tl-ink: #164252;
--tl-mut: #5e7d8a;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--tl-bg);
padding: 64px 24px;
display: grid;
place-items: center;
}
.tls-21__frame {
width: min(760px,100%);
}
.tls-21__title {
font-size: clamp(22px,3.5vw,28px);
font-weight: 800;
color: var(--tl-ink);
letter-spacing: -.02em;
text-align: center;
margin-bottom: 36px;
}
.tls-21__container {
container-type: inline-size;
}
.tls-21__grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 48px 1fr;
}
.tls-21__item {
position: relative;
padding-bottom: 28px;
}
.tls-21__item:nth-child(odd) {
grid-column: 1;
}
.tls-21__item:nth-child(even) {
grid-column: 3;
}
.tls-21__item::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: var(--tl-line);
}
.tls-21__item:nth-child(odd)::before {
right: -25px;
}
.tls-21__item:nth-child(even)::before {
left: -25px;
}
.tls-21__item:last-child::before {
bottom: auto;
height: 28px;
}
.tls-21__item::after {
content: '';
position: absolute;
top: 22px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--tl-accent);
border: 3px solid var(--tl-bg);
z-index: 1;
}
.tls-21__item:nth-child(odd)::after {
right: -31px;
}
.tls-21__item:nth-child(even)::after {
left: -31px;
}
.tls-21__card {
position: relative;
background: var(--tl-card);
border: 1px solid var(--tl-line);
border-radius: 14px;
padding: 18px 20px;
box-shadow: 0 2px 10px -6px rgba(20,66,82,.15);
}
.tls-21__card::after {
content: '';
position: absolute;
top: 22px;
width: 12px;
height: 12px;
background: var(--tl-card);
border: 1px solid var(--tl-line);
rotate: 45deg;
}
.tls-21__item:nth-child(odd) .tls-21__card::after {
right: -7px;
border-left: none;
border-bottom: none;
}
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
border-right: none;
border-top: none;
}
.tls-21__date {
font-size: 12px;
font-weight: 800;
letter-spacing: .1em;
color: var(--tl-accent);
}
.tls-21__h {
font-size: 16.5px;
font-weight: 750;
color: var(--tl-ink);
margin: 5px 0 5px;
letter-spacing: -.01em;
}
.tls-21__p {
font-size: 13.5px;
line-height: 1.55;
color: var(--tl-mut);
text-wrap: pretty;
}
@container (max-width: 560px) {
.tls-21__grid {
grid-template-columns: 24px 1fr;
}
.tls-21__item:nth-child(odd),
.tls-21__item:nth-child(even) {
grid-column: 2;
}
.tls-21__item:nth-child(odd)::before,
.tls-21__item:nth-child(even)::before {
left: -13px;
right: auto;
}
.tls-21__item:nth-child(odd)::after,
.tls-21__item:nth-child(even)::after {
left: -19px;
right: auto;
}
.tls-21__item:nth-child(odd) .tls-21__card::after,
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
right: auto;
border: 1px solid var(--tl-line);
border-right: none;
border-top: none;
}
}
```
## JavaScript
```js
/* No JavaScript — container-type:inline-size on the wrapper + one @container (max-width:560px) block flips the zigzag to a single left-rail column based on the CONTAINER's width, not the viewport. */
```Here's a working CSS Timeline from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Zigzag Alternating Timeline — Container Query Responsive
Source: https://codefronts.com/layouts/css-timelines/vertical-centered/
The classic center-spine zigzag — cards alternating left and right of a vertical rail with arrow notches pointing at their markers — made truly portable: it reshapes to a single left-rail column when its CONTAINER drops below 560px, not the viewport. Drop it in a sidebar, a modal or a split view and it lays itself out correctly with zero configuration.
## HTML
```html
<section class="tls-21" aria-label="Zigzag container query timeline demo">
<div class="tls-21__frame">
<h3 class="tls-21__title">Company milestones</h3>
<div class="tls-21__container">
<ol class="tls-21__grid" role="list">
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2022">2022</time>
<h4 class="tls-21__h">Founded in a garage</h4>
<p class="tls-21__p">Two founders, one whiteboard, no name for six weeks.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2023">2023</time>
<h4 class="tls-21__h">Seed round</h4>
<p class="tls-21__p">$2.4M to hire the first five engineers and a very good office dog.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2024">2024</time>
<h4 class="tls-21__h">First enterprise customer</h4>
<p class="tls-21__p">A 3,000-seat rollout that rewrote our definition of 'edge case'.</p>
</div>
</li>
<li class="tls-21__item">
<div class="tls-21__card">
<time class="tls-21__date" datetime="2025">2025</time>
<h4 class="tls-21__h">Series A</h4>
<p class="tls-21__p">$18M led by our earliest angel. The dog got a title.</p>
</div>
</li>
</ol>
</div>
</div>
</section>
```
## CSS
```css
.tls-21,
.tls-21 *,
.tls-21 *::before,
.tls-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tls-21 {
min-height: 100vh;
width: 100%;
--tl-accent: #0e7490;
--tl-line: #d3e3e8;
--tl-card: #ffffff;
--tl-bg: #f2f7f9;
--tl-ink: #164252;
--tl-mut: #5e7d8a;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--tl-bg);
padding: 64px 24px;
display: grid;
place-items: center;
}
.tls-21__frame {
width: min(760px,100%);
}
.tls-21__title {
font-size: clamp(22px,3.5vw,28px);
font-weight: 800;
color: var(--tl-ink);
letter-spacing: -.02em;
text-align: center;
margin-bottom: 36px;
}
.tls-21__container {
container-type: inline-size;
}
.tls-21__grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 48px 1fr;
}
.tls-21__item {
position: relative;
padding-bottom: 28px;
}
.tls-21__item:nth-child(odd) {
grid-column: 1;
}
.tls-21__item:nth-child(even) {
grid-column: 3;
}
.tls-21__item::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: var(--tl-line);
}
.tls-21__item:nth-child(odd)::before {
right: -25px;
}
.tls-21__item:nth-child(even)::before {
left: -25px;
}
.tls-21__item:last-child::before {
bottom: auto;
height: 28px;
}
.tls-21__item::after {
content: '';
position: absolute;
top: 22px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--tl-accent);
border: 3px solid var(--tl-bg);
z-index: 1;
}
.tls-21__item:nth-child(odd)::after {
right: -31px;
}
.tls-21__item:nth-child(even)::after {
left: -31px;
}
.tls-21__card {
position: relative;
background: var(--tl-card);
border: 1px solid var(--tl-line);
border-radius: 14px;
padding: 18px 20px;
box-shadow: 0 2px 10px -6px rgba(20,66,82,.15);
}
.tls-21__card::after {
content: '';
position: absolute;
top: 22px;
width: 12px;
height: 12px;
background: var(--tl-card);
border: 1px solid var(--tl-line);
rotate: 45deg;
}
.tls-21__item:nth-child(odd) .tls-21__card::after {
right: -7px;
border-left: none;
border-bottom: none;
}
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
border-right: none;
border-top: none;
}
.tls-21__date {
font-size: 12px;
font-weight: 800;
letter-spacing: .1em;
color: var(--tl-accent);
}
.tls-21__h {
font-size: 16.5px;
font-weight: 750;
color: var(--tl-ink);
margin: 5px 0 5px;
letter-spacing: -.01em;
}
.tls-21__p {
font-size: 13.5px;
line-height: 1.55;
color: var(--tl-mut);
text-wrap: pretty;
}
@container (max-width: 560px) {
.tls-21__grid {
grid-template-columns: 24px 1fr;
}
.tls-21__item:nth-child(odd),
.tls-21__item:nth-child(even) {
grid-column: 2;
}
.tls-21__item:nth-child(odd)::before,
.tls-21__item:nth-child(even)::before {
left: -13px;
right: auto;
}
.tls-21__item:nth-child(odd)::after,
.tls-21__item:nth-child(even)::after {
left: -19px;
right: auto;
}
.tls-21__item:nth-child(odd) .tls-21__card::after,
.tls-21__item:nth-child(even) .tls-21__card::after {
left: -7px;
right: auto;
border: 1px solid var(--tl-line);
border-right: none;
border-top: none;
}
}
```
## JavaScript
```js
/* No JavaScript — container-type:inline-size on the wrapper + one @container (max-width:560px) block flips the zigzag to a single left-rail column based on the CONTAINER's width, not the viewport. */
```How this works
The wrapper declares container-type: inline-size, turning it into a query container. The zigzag is one grid — grid-template-columns: 1fr 48px 1fr — where every card explicitly places itself: odd items grid-column: 1 with right-pointing notches, even items grid-column: 3 with left-pointing ones, and the spine occupies the middle track as a per-item ::before. Rows pack with grid-auto-flow: dense-free simple placement because each item also sets its own grid-row via nth-child arithmetic-free flow — items simply occupy successive rows and the empty opposite cell is the whitespace that makes zigzags breathe.
The responsive flip is a single @container (max-width: 560px) block: columns become 24px 1fr, every card snaps to column 2, notches all point left, markers slide onto the rail. Because the query watches the container, the SAME component is two-column in a 900px article body and one-column in a 320px sidebar — on the same page, at the same viewport width. That is the thing media queries fundamentally cannot do, and the reason component libraries have moved to container queries.
The notch is a rotated square (::after, 12×12, rotate:45deg) inheriting the card's background and border, clipped so only the outward corner shows — cheaper and crisper than border-triangle hacks and it follows theme tokens automatically.
Techniques used: container-type:inline-size + @container width query · 1fr auto 1fr center-spine grid with explicit per-side placement · rotated-square card notches inheriting card chrome · per-item spine segments (no measured full-height line) · design tokens for one-line theming · time[datetime] semantic dates
Make it yours
- Breakpoint: the 560px in @container — set it to where CARDS (not the page) start feeling cramped, usually 480–640px.
- Spine width: the middle 48px track; widen it and the markers auto-center because they're placed with justify-self.
- Force single-column always: delete the two grid-column rules for odd/even and keep the @container block's layout as the base.
- Test it honestly: resize the demo's PARENT (browser devtools → select wrapper → drag) — resizing the window only tests it if the wrapper tracks the viewport.
Gotchas — read before shipping
- A query container cannot size itself from its contents in the queried axis — inline-size containment is fine for full-width wrappers, but don't put container-type on something that must shrink-wrap.
- @container rules must target DESCENDANTS of the container, never the container itself — a container cannot query its own size.
- Keep the spine per-item: a single absolutely-positioned full-height line needs the container's height, which changes when the layout flips, and it will desync.
- Name containers (
container-name: tl) in real projects — anonymous queries resolve to the NEAREST ancestor container, which may not be the one you meant once the component nests.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by @container, text-wrap as this demo is written. The core technique itself goes back further — Chrome 105+.
Size container queries are Baseline 2023 — safe for production. Without support the layout stays two-column zigzag (still correct, just not container-aware).