15 CSS Aspect Ratio Demos05 / 15
CSS Aspect Ratio Vertical Story Card 9 16
A Stories/Shorts rail where the ratio runs the other direction: each card declares a height (clamped to the viewport) and aspect-ratio: 9/16 derives the width. That reversal — ratio solves for whichever axis you leave auto — is the most under-taught fact about the property. Scroll-snap paddles the rail, segment bars and conic-gradient avatar rings sell the Stories grammar, edge masks fade the overflow, and every card is a real focusable link.
Published
The code
<section class="car-05" aria-label="Vertical 9:16 story cards demo">
<div class="car-05__stage" id="car05-top">
<header class="car-05__bar">
<a class="car-05__brand" href="#car05-top"><i aria-hidden="true"></i>Shortloop</a>
<p class="car-05__hint"><code>height: clamp(340px, 56svh, 540px)</code> · <code>aspect-ratio: 9/16</code> — width is derived</p>
<a class="car-05__all" href="#car05-top">See all</a>
</header>
<div class="car-05__railwrap">
<ul class="car-05__rail" aria-label="Stories">
<li><a class="car-05__story car-05__story--new" href="#car05-top"><span class="car-05__plus" aria-hidden="true">+</span><b>Your story</b><small>9:16, like all the others</small></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>mira.k</b><time>2h</time></span><span class="car-05__cap">Fitting the fold on every phone since 2026</span><span class="car-05__eye" aria-hidden="true">▶ 12.4k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>june.ro</b><time>4h</time></span><span class="car-05__cap">The width of this card is height × 0.5625</span><span class="car-05__eye" aria-hidden="true">▶ 8.1k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>theo.v</b><time>6h</time></span><span class="car-05__cap">flex:none is load-bearing here</span><span class="car-05__eye" aria-hidden="true">▶ 5.7k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i class="is-seen"></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>amara</b><time>9h</time></span><span class="car-05__cap">Scroll-snap lands every card on the gutter</span><span class="car-05__eye" aria-hidden="true">▶ 21k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>sol.ie</b><time>11h</time></span><span class="car-05__cap">Edge fade is mask-image, not a gradient div</span><span class="car-05__eye" aria-hidden="true">▶ 3.3k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>nadia</b><time>14h</time></span><span class="car-05__cap">Tab through — every card is a real link</span><span class="car-05__eye" aria-hidden="true">▶ 9.8k</span></a></li>
</ul>
</div>
<footer class="car-05__foot">Rotate or resize the pane: card heights re-clamp to <code>56svh</code> and every width re-derives from <code>aspect-ratio: 9/16</code>. A demo from the CodeFronts aspect-ratio collection.</footer>
</div>
</section><section class="car-05" aria-label="Vertical 9:16 story cards demo">
<div class="car-05__stage" id="car05-top">
<header class="car-05__bar">
<a class="car-05__brand" href="#car05-top"><i aria-hidden="true"></i>Shortloop</a>
<p class="car-05__hint"><code>height: clamp(340px, 56svh, 540px)</code> · <code>aspect-ratio: 9/16</code> — width is derived</p>
<a class="car-05__all" href="#car05-top">See all</a>
</header>
<div class="car-05__railwrap">
<ul class="car-05__rail" aria-label="Stories">
<li><a class="car-05__story car-05__story--new" href="#car05-top"><span class="car-05__plus" aria-hidden="true">+</span><b>Your story</b><small>9:16, like all the others</small></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>mira.k</b><time>2h</time></span><span class="car-05__cap">Fitting the fold on every phone since 2026</span><span class="car-05__eye" aria-hidden="true">▶ 12.4k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>june.ro</b><time>4h</time></span><span class="car-05__cap">The width of this card is height × 0.5625</span><span class="car-05__eye" aria-hidden="true">▶ 8.1k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>theo.v</b><time>6h</time></span><span class="car-05__cap">flex:none is load-bearing here</span><span class="car-05__eye" aria-hidden="true">▶ 5.7k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i class="is-seen"></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>amara</b><time>9h</time></span><span class="car-05__cap">Scroll-snap lands every card on the gutter</span><span class="car-05__eye" aria-hidden="true">▶ 21k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>sol.ie</b><time>11h</time></span><span class="car-05__cap">Edge fade is mask-image, not a gradient div</span><span class="car-05__eye" aria-hidden="true">▶ 3.3k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>nadia</b><time>14h</time></span><span class="car-05__cap">Tab through — every card is a real link</span><span class="car-05__eye" aria-hidden="true">▶ 9.8k</span></a></li>
</ul>
</div>
<footer class="car-05__foot">Rotate or resize the pane: card heights re-clamp to <code>56svh</code> and every width re-derives from <code>aspect-ratio: 9/16</code>. A demo from the CodeFronts aspect-ratio collection.</footer>
</div>
</section>.car-05,
.car-05 *,
.car-05 *::before,
.car-05 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-05 {
--bg: oklch(0.15 0.02 300);
--ink: oklch(0.96 0.005 300);
--mut: oklch(0.68 0.02 300);
--line: oklch(0.3 0.03 300);
--acc: oklch(0.7 0.19 340);
--acc2: oklch(0.78 0.15 60);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.car-05__stage {
width: 100%;
container: car05/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: flex;
flex-direction: column;
background: radial-gradient(120cqi 80cqi at 80% -20%,oklch(0.28 0.06 340/.5),transparent 60%),var(--bg);
}
.car-05__bar {
display: flex;
align-items: center;
gap: 16px;
padding: 18px clamp(16px,4cqi,40px) 10px;
flex-wrap: wrap;
}
.car-05__brand {
display: flex;
align-items: center;
gap: 9px;
font-size: 17px;
font-weight: 800;
letter-spacing: -.02em;
color: inherit;
text-decoration: none;
}
.car-05__brand i {
width: 13px;
height: 22px;
border-radius: 4px;
background: linear-gradient(160deg,var(--acc),var(--acc2));
}
.car-05__hint {
font-size: 11.5px;
color: var(--mut);
margin-inline: auto;
}
.car-05__hint code,
.car-05__foot code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .95em;
background: oklch(0.24 0.03 300);
padding: 2px 6px;
border-radius: 5px;
color: oklch(0.85 0.08 340);
}
.car-05__all {
display: grid;
place-items: center;
min-height: 44px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
color: var(--ink);
text-decoration: none;
border: 1px solid var(--line);
transition: border-color .2s,background .2s;
}
.car-05__all:hover,
.car-05__all:focus-visible {
border-color: var(--acc);
background: oklch(0.22 0.03 320);
}
.car-05__all:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.car-05__railwrap {
flex: 1;
display: grid;
align-content: center;
min-height: 0;
}
.car-05__rail {
list-style: none;
display: flex;
gap: 14px;
overflow-x: auto;
padding: 18px clamp(16px,4cqi,40px) 22px;
scroll-snap-type: x mandatory;
scroll-padding-inline: clamp(16px,4cqi,40px);
-webkit-mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
scrollbar-width: thin;
scrollbar-color: oklch(0.4 0.04 320) transparent;
}
.car-05__rail::-webkit-scrollbar {
height: 8px;
}
.car-05__rail::-webkit-scrollbar-thumb {
background: oklch(0.4 0.04 320);
border-radius: 99px;
}
.car-05__story {
position: relative;
flex: none;
height: clamp(340px,56svh,540px);
aspect-ratio: 9/16;
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 14px;
border-radius: 20px;
overflow: hidden;
text-decoration: none;
color: oklch(0.98 0 0);
isolation: isolate;
background-color: oklch(0.3 0.05 320);
background-image: linear-gradient(to bottom,oklch(0.2 0.03 320/.05) 40%,oklch(0.12 0.03 320/.82)),var(--img);
background-size: cover;
background-position: center;
transition: translate .3s cubic-bezier(.2,.7,.2,1),box-shadow .3s;
}
.car-05__story:hover,
.car-05__story:focus-visible {
translate: 0 -6px;
box-shadow: 0 22px 50px oklch(0.05 0.03 320/.6);
}
.car-05__story:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.car-05__segs {
position: absolute;
left: 12px;
right: 12px;
top: 10px;
display: flex;
gap: 5px;
}
.car-05__segs i {
flex: 1;
height: 3px;
border-radius: 99px;
background: oklch(0.95 0.01 320/.35);
}
.car-05__segs i.is-seen {
background: oklch(0.98 0 0/.95);
}
.car-05__head {
display: flex;
align-items: center;
gap: 8px;
position: absolute;
left: 12px;
right: 12px;
top: 22px;
}
.car-05__head img {
width: 34px;
height: auto;
aspect-ratio: 1;
border-radius: 50%;
object-fit: cover;
border: 2px solid transparent;
background: linear-gradient(oklch(0.2 0.03 320),oklch(0.2 0.03 320)) padding-box,conic-gradient(var(--acc),var(--acc2),var(--acc)) border-box;
}
.car-05__head b {
font-size: 13px;
font-weight: 700;
text-shadow: 0 1px 8px oklch(0.1 0.03 320/.8);
}
.car-05__head time {
font-size: 11px;
color: oklch(0.85 0.02 320/.85);
}
.car-05__cap {
font-size: 13.5px;
line-height: 1.4;
font-weight: 600;
text-shadow: 0 1px 10px oklch(0.1 0.03 320/.9);
text-wrap: pretty;
}
.car-05__eye {
margin-top: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
color: oklch(0.9 0.02 320/.9);
}
.car-05__story--new {
align-items: center;
justify-content: center;
gap: 6px;
background: oklch(0.2 0.03 320/.55);
border: 1.5px dashed oklch(0.5 0.05 320);
}
.car-05__plus {
display: grid;
place-items: center;
width: 52px;
height: 52px;
border-radius: 50%;
font-size: 26px;
font-weight: 600;
background: linear-gradient(160deg,var(--acc),var(--acc2));
color: oklch(0.15 0.03 320);
transition: scale .25s cubic-bezier(.34,1.56,.64,1);
}
.car-05__story--new:hover .car-05__plus,
.car-05__story--new:focus-visible .car-05__plus {
scale: 1.1;
}
.car-05__story--new b {
font-size: 13.5px;
}
.car-05__story--new small {
font-size: 11px;
color: var(--mut);
}
.car-05__foot {
padding: 0 clamp(16px,4cqi,40px) 22px;
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
}
@media (prefers-reduced-motion: reduce) {
.car-05 * {
transition-duration: .01ms !important;
}
}.car-05,
.car-05 *,
.car-05 *::before,
.car-05 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-05 {
--bg: oklch(0.15 0.02 300);
--ink: oklch(0.96 0.005 300);
--mut: oklch(0.68 0.02 300);
--line: oklch(0.3 0.03 300);
--acc: oklch(0.7 0.19 340);
--acc2: oklch(0.78 0.15 60);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.car-05__stage {
width: 100%;
container: car05/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: flex;
flex-direction: column;
background: radial-gradient(120cqi 80cqi at 80% -20%,oklch(0.28 0.06 340/.5),transparent 60%),var(--bg);
}
.car-05__bar {
display: flex;
align-items: center;
gap: 16px;
padding: 18px clamp(16px,4cqi,40px) 10px;
flex-wrap: wrap;
}
.car-05__brand {
display: flex;
align-items: center;
gap: 9px;
font-size: 17px;
font-weight: 800;
letter-spacing: -.02em;
color: inherit;
text-decoration: none;
}
.car-05__brand i {
width: 13px;
height: 22px;
border-radius: 4px;
background: linear-gradient(160deg,var(--acc),var(--acc2));
}
.car-05__hint {
font-size: 11.5px;
color: var(--mut);
margin-inline: auto;
}
.car-05__hint code,
.car-05__foot code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .95em;
background: oklch(0.24 0.03 300);
padding: 2px 6px;
border-radius: 5px;
color: oklch(0.85 0.08 340);
}
.car-05__all {
display: grid;
place-items: center;
min-height: 44px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
color: var(--ink);
text-decoration: none;
border: 1px solid var(--line);
transition: border-color .2s,background .2s;
}
.car-05__all:hover,
.car-05__all:focus-visible {
border-color: var(--acc);
background: oklch(0.22 0.03 320);
}
.car-05__all:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.car-05__railwrap {
flex: 1;
display: grid;
align-content: center;
min-height: 0;
}
.car-05__rail {
list-style: none;
display: flex;
gap: 14px;
overflow-x: auto;
padding: 18px clamp(16px,4cqi,40px) 22px;
scroll-snap-type: x mandatory;
scroll-padding-inline: clamp(16px,4cqi,40px);
-webkit-mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
scrollbar-width: thin;
scrollbar-color: oklch(0.4 0.04 320) transparent;
}
.car-05__rail::-webkit-scrollbar {
height: 8px;
}
.car-05__rail::-webkit-scrollbar-thumb {
background: oklch(0.4 0.04 320);
border-radius: 99px;
}
.car-05__story {
position: relative;
flex: none;
height: clamp(340px,56svh,540px);
aspect-ratio: 9/16;
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 14px;
border-radius: 20px;
overflow: hidden;
text-decoration: none;
color: oklch(0.98 0 0);
isolation: isolate;
background-color: oklch(0.3 0.05 320);
background-image: linear-gradient(to bottom,oklch(0.2 0.03 320/.05) 40%,oklch(0.12 0.03 320/.82)),var(--img);
background-size: cover;
background-position: center;
transition: translate .3s cubic-bezier(.2,.7,.2,1),box-shadow .3s;
}
.car-05__story:hover,
.car-05__story:focus-visible {
translate: 0 -6px;
box-shadow: 0 22px 50px oklch(0.05 0.03 320/.6);
}
.car-05__story:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.car-05__segs {
position: absolute;
left: 12px;
right: 12px;
top: 10px;
display: flex;
gap: 5px;
}
.car-05__segs i {
flex: 1;
height: 3px;
border-radius: 99px;
background: oklch(0.95 0.01 320/.35);
}
.car-05__segs i.is-seen {
background: oklch(0.98 0 0/.95);
}
.car-05__head {
display: flex;
align-items: center;
gap: 8px;
position: absolute;
left: 12px;
right: 12px;
top: 22px;
}
.car-05__head img {
width: 34px;
height: auto;
aspect-ratio: 1;
border-radius: 50%;
object-fit: cover;
border: 2px solid transparent;
background: linear-gradient(oklch(0.2 0.03 320),oklch(0.2 0.03 320)) padding-box,conic-gradient(var(--acc),var(--acc2),var(--acc)) border-box;
}
.car-05__head b {
font-size: 13px;
font-weight: 700;
text-shadow: 0 1px 8px oklch(0.1 0.03 320/.8);
}
.car-05__head time {
font-size: 11px;
color: oklch(0.85 0.02 320/.85);
}
.car-05__cap {
font-size: 13.5px;
line-height: 1.4;
font-weight: 600;
text-shadow: 0 1px 10px oklch(0.1 0.03 320/.9);
text-wrap: pretty;
}
.car-05__eye {
margin-top: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
color: oklch(0.9 0.02 320/.9);
}
.car-05__story--new {
align-items: center;
justify-content: center;
gap: 6px;
background: oklch(0.2 0.03 320/.55);
border: 1.5px dashed oklch(0.5 0.05 320);
}
.car-05__plus {
display: grid;
place-items: center;
width: 52px;
height: 52px;
border-radius: 50%;
font-size: 26px;
font-weight: 600;
background: linear-gradient(160deg,var(--acc),var(--acc2));
color: oklch(0.15 0.03 320);
transition: scale .25s cubic-bezier(.34,1.56,.64,1);
}
.car-05__story--new:hover .car-05__plus,
.car-05__story--new:focus-visible .car-05__plus {
scale: 1.1;
}
.car-05__story--new b {
font-size: 13.5px;
}
.car-05__story--new small {
font-size: 11px;
color: var(--mut);
}
.car-05__foot {
padding: 0 clamp(16px,4cqi,40px) 22px;
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
}
@media (prefers-reduced-motion: reduce) {
.car-05 * {
transition-duration: .01ms !important;
}
}Here's a working CSS Aspect Ratio Demo 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: CSS Aspect Ratio Vertical Story Card 9 16
Source: https://codefronts.com/layouts/css-aspect-ratio/css-aspect-ratio-vertical-story-card-9-16/
A Stories/Shorts rail where the ratio runs the other direction: each card declares a height (clamped to the viewport) and aspect-ratio: 9/16 derives the width. That reversal — ratio solves for whichever axis you leave auto — is the most under-taught fact about the property. Scroll-snap paddles the rail, segment bars and conic-gradient avatar rings sell the Stories grammar, edge masks fade the overflow, and every card is a real focusable link.
## HTML
```html
<section class="car-05" aria-label="Vertical 9:16 story cards demo">
<div class="car-05__stage" id="car05-top">
<header class="car-05__bar">
<a class="car-05__brand" href="#car05-top"><i aria-hidden="true"></i>Shortloop</a>
<p class="car-05__hint"><code>height: clamp(340px, 56svh, 540px)</code> · <code>aspect-ratio: 9/16</code> — width is derived</p>
<a class="car-05__all" href="#car05-top">See all</a>
</header>
<div class="car-05__railwrap">
<ul class="car-05__rail" aria-label="Stories">
<li><a class="car-05__story car-05__story--new" href="#car05-top"><span class="car-05__plus" aria-hidden="true">+</span><b>Your story</b><small>9:16, like all the others</small></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>mira.k</b><time>2h</time></span><span class="car-05__cap">Fitting the fold on every phone since 2026</span><span class="car-05__eye" aria-hidden="true">▶ 12.4k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>june.ro</b><time>4h</time></span><span class="car-05__cap">The width of this card is height × 0.5625</span><span class="car-05__eye" aria-hidden="true">▶ 8.1k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>theo.v</b><time>6h</time></span><span class="car-05__cap">flex:none is load-bearing here</span><span class="car-05__eye" aria-hidden="true">▶ 5.7k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i class="is-seen"></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>amara</b><time>9h</time></span><span class="car-05__cap">Scroll-snap lands every card on the gutter</span><span class="car-05__eye" aria-hidden="true">▶ 21k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>sol.ie</b><time>11h</time></span><span class="car-05__cap">Edge fade is mask-image, not a gradient div</span><span class="car-05__eye" aria-hidden="true">▶ 3.3k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>nadia</b><time>14h</time></span><span class="car-05__cap">Tab through — every card is a real link</span><span class="car-05__eye" aria-hidden="true">▶ 9.8k</span></a></li>
</ul>
</div>
<footer class="car-05__foot">Rotate or resize the pane: card heights re-clamp to <code>56svh</code> and every width re-derives from <code>aspect-ratio: 9/16</code>. A demo from the CodeFronts aspect-ratio collection.</footer>
</div>
</section>
```
## CSS
```css
.car-05,
.car-05 *,
.car-05 *::before,
.car-05 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-05 {
--bg: oklch(0.15 0.02 300);
--ink: oklch(0.96 0.005 300);
--mut: oklch(0.68 0.02 300);
--line: oklch(0.3 0.03 300);
--acc: oklch(0.7 0.19 340);
--acc2: oklch(0.78 0.15 60);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.car-05__stage {
width: 100%;
container: car05/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: flex;
flex-direction: column;
background: radial-gradient(120cqi 80cqi at 80% -20%,oklch(0.28 0.06 340/.5),transparent 60%),var(--bg);
}
.car-05__bar {
display: flex;
align-items: center;
gap: 16px;
padding: 18px clamp(16px,4cqi,40px) 10px;
flex-wrap: wrap;
}
.car-05__brand {
display: flex;
align-items: center;
gap: 9px;
font-size: 17px;
font-weight: 800;
letter-spacing: -.02em;
color: inherit;
text-decoration: none;
}
.car-05__brand i {
width: 13px;
height: 22px;
border-radius: 4px;
background: linear-gradient(160deg,var(--acc),var(--acc2));
}
.car-05__hint {
font-size: 11.5px;
color: var(--mut);
margin-inline: auto;
}
.car-05__hint code,
.car-05__foot code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .95em;
background: oklch(0.24 0.03 300);
padding: 2px 6px;
border-radius: 5px;
color: oklch(0.85 0.08 340);
}
.car-05__all {
display: grid;
place-items: center;
min-height: 44px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
color: var(--ink);
text-decoration: none;
border: 1px solid var(--line);
transition: border-color .2s,background .2s;
}
.car-05__all:hover,
.car-05__all:focus-visible {
border-color: var(--acc);
background: oklch(0.22 0.03 320);
}
.car-05__all:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.car-05__railwrap {
flex: 1;
display: grid;
align-content: center;
min-height: 0;
}
.car-05__rail {
list-style: none;
display: flex;
gap: 14px;
overflow-x: auto;
padding: 18px clamp(16px,4cqi,40px) 22px;
scroll-snap-type: x mandatory;
scroll-padding-inline: clamp(16px,4cqi,40px);
-webkit-mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
scrollbar-width: thin;
scrollbar-color: oklch(0.4 0.04 320) transparent;
}
.car-05__rail::-webkit-scrollbar {
height: 8px;
}
.car-05__rail::-webkit-scrollbar-thumb {
background: oklch(0.4 0.04 320);
border-radius: 99px;
}
.car-05__story {
position: relative;
flex: none;
height: clamp(340px,56svh,540px);
aspect-ratio: 9/16;
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 14px;
border-radius: 20px;
overflow: hidden;
text-decoration: none;
color: oklch(0.98 0 0);
isolation: isolate;
background-color: oklch(0.3 0.05 320);
background-image: linear-gradient(to bottom,oklch(0.2 0.03 320/.05) 40%,oklch(0.12 0.03 320/.82)),var(--img);
background-size: cover;
background-position: center;
transition: translate .3s cubic-bezier(.2,.7,.2,1),box-shadow .3s;
}
.car-05__story:hover,
.car-05__story:focus-visible {
translate: 0 -6px;
box-shadow: 0 22px 50px oklch(0.05 0.03 320/.6);
}
.car-05__story:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.car-05__segs {
position: absolute;
left: 12px;
right: 12px;
top: 10px;
display: flex;
gap: 5px;
}
.car-05__segs i {
flex: 1;
height: 3px;
border-radius: 99px;
background: oklch(0.95 0.01 320/.35);
}
.car-05__segs i.is-seen {
background: oklch(0.98 0 0/.95);
}
.car-05__head {
display: flex;
align-items: center;
gap: 8px;
position: absolute;
left: 12px;
right: 12px;
top: 22px;
}
.car-05__head img {
width: 34px;
height: auto;
aspect-ratio: 1;
border-radius: 50%;
object-fit: cover;
border: 2px solid transparent;
background: linear-gradient(oklch(0.2 0.03 320),oklch(0.2 0.03 320)) padding-box,conic-gradient(var(--acc),var(--acc2),var(--acc)) border-box;
}
.car-05__head b {
font-size: 13px;
font-weight: 700;
text-shadow: 0 1px 8px oklch(0.1 0.03 320/.8);
}
.car-05__head time {
font-size: 11px;
color: oklch(0.85 0.02 320/.85);
}
.car-05__cap {
font-size: 13.5px;
line-height: 1.4;
font-weight: 600;
text-shadow: 0 1px 10px oklch(0.1 0.03 320/.9);
text-wrap: pretty;
}
.car-05__eye {
margin-top: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
color: oklch(0.9 0.02 320/.9);
}
.car-05__story--new {
align-items: center;
justify-content: center;
gap: 6px;
background: oklch(0.2 0.03 320/.55);
border: 1.5px dashed oklch(0.5 0.05 320);
}
.car-05__plus {
display: grid;
place-items: center;
width: 52px;
height: 52px;
border-radius: 50%;
font-size: 26px;
font-weight: 600;
background: linear-gradient(160deg,var(--acc),var(--acc2));
color: oklch(0.15 0.03 320);
transition: scale .25s cubic-bezier(.34,1.56,.64,1);
}
.car-05__story--new:hover .car-05__plus,
.car-05__story--new:focus-visible .car-05__plus {
scale: 1.1;
}
.car-05__story--new b {
font-size: 13.5px;
}
.car-05__story--new small {
font-size: 11px;
color: var(--mut);
}
.car-05__foot {
padding: 0 clamp(16px,4cqi,40px) 22px;
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
}
@media (prefers-reduced-motion: reduce) {
.car-05 * {
transition-duration: .01ms !important;
}
}
```Here's a working CSS Aspect Ratio Demo 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: CSS Aspect Ratio Vertical Story Card 9 16
Source: https://codefronts.com/layouts/css-aspect-ratio/css-aspect-ratio-vertical-story-card-9-16/
A Stories/Shorts rail where the ratio runs the other direction: each card declares a height (clamped to the viewport) and aspect-ratio: 9/16 derives the width. That reversal — ratio solves for whichever axis you leave auto — is the most under-taught fact about the property. Scroll-snap paddles the rail, segment bars and conic-gradient avatar rings sell the Stories grammar, edge masks fade the overflow, and every card is a real focusable link.
## HTML
```html
<section class="car-05" aria-label="Vertical 9:16 story cards demo">
<div class="car-05__stage" id="car05-top">
<header class="car-05__bar">
<a class="car-05__brand" href="#car05-top"><i aria-hidden="true"></i>Shortloop</a>
<p class="car-05__hint"><code>height: clamp(340px, 56svh, 540px)</code> · <code>aspect-ratio: 9/16</code> — width is derived</p>
<a class="car-05__all" href="#car05-top">See all</a>
</header>
<div class="car-05__railwrap">
<ul class="car-05__rail" aria-label="Stories">
<li><a class="car-05__story car-05__story--new" href="#car05-top"><span class="car-05__plus" aria-hidden="true">+</span><b>Your story</b><small>9:16, like all the others</small></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1529139574466-a303027c1d8b?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>mira.k</b><time>2h</time></span><span class="car-05__cap">Fitting the fold on every phone since 2026</span><span class="car-05__eye" aria-hidden="true">▶ 12.4k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>june.ro</b><time>4h</time></span><span class="car-05__cap">The width of this card is height × 0.5625</span><span class="car-05__eye" aria-hidden="true">▶ 8.1k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>theo.v</b><time>6h</time></span><span class="car-05__cap">flex:none is load-bearing here</span><span class="car-05__eye" aria-hidden="true">▶ 5.7k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i class="is-seen"></i><i class="is-seen"></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>amara</b><time>9h</time></span><span class="car-05__cap">Scroll-snap lands every card on the gutter</span><span class="car-05__eye" aria-hidden="true">▶ 21k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i></i><i></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>sol.ie</b><time>11h</time></span><span class="car-05__cap">Edge fade is mask-image, not a gradient div</span><span class="car-05__eye" aria-hidden="true">▶ 3.3k</span></a></li>
<li><a class="car-05__story" href="#car05-top" style="--img:url('https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=700&h=1244&auto=format&fit=crop')"><span class="car-05__segs" aria-hidden="true"><i class="is-seen"></i><i></i></span><span class="car-05__head"><img src="https://images.unsplash.com/photo-1509631179647-0177331693ae?q=80&w=96&h=96&auto=format&fit=crop" width="96" height="96" loading="lazy" alt=""><b>nadia</b><time>14h</time></span><span class="car-05__cap">Tab through — every card is a real link</span><span class="car-05__eye" aria-hidden="true">▶ 9.8k</span></a></li>
</ul>
</div>
<footer class="car-05__foot">Rotate or resize the pane: card heights re-clamp to <code>56svh</code> and every width re-derives from <code>aspect-ratio: 9/16</code>. A demo from the CodeFronts aspect-ratio collection.</footer>
</div>
</section>
```
## CSS
```css
.car-05,
.car-05 *,
.car-05 *::before,
.car-05 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-05 {
--bg: oklch(0.15 0.02 300);
--ink: oklch(0.96 0.005 300);
--mut: oklch(0.68 0.02 300);
--line: oklch(0.3 0.03 300);
--acc: oklch(0.7 0.19 340);
--acc2: oklch(0.78 0.15 60);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.car-05__stage {
width: 100%;
container: car05/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: flex;
flex-direction: column;
background: radial-gradient(120cqi 80cqi at 80% -20%,oklch(0.28 0.06 340/.5),transparent 60%),var(--bg);
}
.car-05__bar {
display: flex;
align-items: center;
gap: 16px;
padding: 18px clamp(16px,4cqi,40px) 10px;
flex-wrap: wrap;
}
.car-05__brand {
display: flex;
align-items: center;
gap: 9px;
font-size: 17px;
font-weight: 800;
letter-spacing: -.02em;
color: inherit;
text-decoration: none;
}
.car-05__brand i {
width: 13px;
height: 22px;
border-radius: 4px;
background: linear-gradient(160deg,var(--acc),var(--acc2));
}
.car-05__hint {
font-size: 11.5px;
color: var(--mut);
margin-inline: auto;
}
.car-05__hint code,
.car-05__foot code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .95em;
background: oklch(0.24 0.03 300);
padding: 2px 6px;
border-radius: 5px;
color: oklch(0.85 0.08 340);
}
.car-05__all {
display: grid;
place-items: center;
min-height: 44px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
color: var(--ink);
text-decoration: none;
border: 1px solid var(--line);
transition: border-color .2s,background .2s;
}
.car-05__all:hover,
.car-05__all:focus-visible {
border-color: var(--acc);
background: oklch(0.22 0.03 320);
}
.car-05__all:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.car-05__railwrap {
flex: 1;
display: grid;
align-content: center;
min-height: 0;
}
.car-05__rail {
list-style: none;
display: flex;
gap: 14px;
overflow-x: auto;
padding: 18px clamp(16px,4cqi,40px) 22px;
scroll-snap-type: x mandatory;
scroll-padding-inline: clamp(16px,4cqi,40px);
-webkit-mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
mask-image: linear-gradient(to right,transparent,black 26px,black calc(100% - 26px),transparent);
scrollbar-width: thin;
scrollbar-color: oklch(0.4 0.04 320) transparent;
}
.car-05__rail::-webkit-scrollbar {
height: 8px;
}
.car-05__rail::-webkit-scrollbar-thumb {
background: oklch(0.4 0.04 320);
border-radius: 99px;
}
.car-05__story {
position: relative;
flex: none;
height: clamp(340px,56svh,540px);
aspect-ratio: 9/16;
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 14px;
border-radius: 20px;
overflow: hidden;
text-decoration: none;
color: oklch(0.98 0 0);
isolation: isolate;
background-color: oklch(0.3 0.05 320);
background-image: linear-gradient(to bottom,oklch(0.2 0.03 320/.05) 40%,oklch(0.12 0.03 320/.82)),var(--img);
background-size: cover;
background-position: center;
transition: translate .3s cubic-bezier(.2,.7,.2,1),box-shadow .3s;
}
.car-05__story:hover,
.car-05__story:focus-visible {
translate: 0 -6px;
box-shadow: 0 22px 50px oklch(0.05 0.03 320/.6);
}
.car-05__story:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.car-05__segs {
position: absolute;
left: 12px;
right: 12px;
top: 10px;
display: flex;
gap: 5px;
}
.car-05__segs i {
flex: 1;
height: 3px;
border-radius: 99px;
background: oklch(0.95 0.01 320/.35);
}
.car-05__segs i.is-seen {
background: oklch(0.98 0 0/.95);
}
.car-05__head {
display: flex;
align-items: center;
gap: 8px;
position: absolute;
left: 12px;
right: 12px;
top: 22px;
}
.car-05__head img {
width: 34px;
height: auto;
aspect-ratio: 1;
border-radius: 50%;
object-fit: cover;
border: 2px solid transparent;
background: linear-gradient(oklch(0.2 0.03 320),oklch(0.2 0.03 320)) padding-box,conic-gradient(var(--acc),var(--acc2),var(--acc)) border-box;
}
.car-05__head b {
font-size: 13px;
font-weight: 700;
text-shadow: 0 1px 8px oklch(0.1 0.03 320/.8);
}
.car-05__head time {
font-size: 11px;
color: oklch(0.85 0.02 320/.85);
}
.car-05__cap {
font-size: 13.5px;
line-height: 1.4;
font-weight: 600;
text-shadow: 0 1px 10px oklch(0.1 0.03 320/.9);
text-wrap: pretty;
}
.car-05__eye {
margin-top: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
color: oklch(0.9 0.02 320/.9);
}
.car-05__story--new {
align-items: center;
justify-content: center;
gap: 6px;
background: oklch(0.2 0.03 320/.55);
border: 1.5px dashed oklch(0.5 0.05 320);
}
.car-05__plus {
display: grid;
place-items: center;
width: 52px;
height: 52px;
border-radius: 50%;
font-size: 26px;
font-weight: 600;
background: linear-gradient(160deg,var(--acc),var(--acc2));
color: oklch(0.15 0.03 320);
transition: scale .25s cubic-bezier(.34,1.56,.64,1);
}
.car-05__story--new:hover .car-05__plus,
.car-05__story--new:focus-visible .car-05__plus {
scale: 1.1;
}
.car-05__story--new b {
font-size: 13.5px;
}
.car-05__story--new small {
font-size: 11px;
color: var(--mut);
}
.car-05__foot {
padding: 0 clamp(16px,4cqi,40px) 22px;
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
}
@media (prefers-reduced-motion: reduce) {
.car-05 * {
transition-duration: .01ms !important;
}
}
```How this works
Every previous demo let width lead and height follow. A story rail inverts it: the rail's height is the constraint (it must fit the fold), so the cards fix height and let the ratio return the width:
.rail { display:flex; gap:14px; overflow-x:auto;
scroll-snap-type:x mandatory; scroll-padding-inline:20px; }
.story{ flex:none;
height:clamp(340px, 56svh, 540px); /* the fixed axis */
aspect-ratio:9 / 16; /* width ≈ height × 0.5625 */
scroll-snap-align:start; }aspect-ratio is not 'width ÷ height' as a formula you owe the browser — it's a relationship the browser uses to fill in whichever axis is auto. Here each flex item has a definite height and automatic width, so width resolves to height × 9⁄16. The clamp() means tall desktop panes get 540px cards and a phone in landscape gets 340px ones — and the widths silently re-derive. No media queries, no card-width table to maintain.
The rest is the Stories dialect done properly. The rail is a scroll-snap container (x mandatory + scroll-padding-inline so snapped cards respect the page gutter), with mask-image fading the clipped edges instead of a hard cut. Each card layers photo → scrim → chrome inside its own stacking context; the avatar ring is a conic-gradient border and the 'watched' segments are a 3-column grid of 3px bars, exactly how the real apps draw them. Cards are anchor elements, so the whole rail is keyboard-traversable — Tab walks the stories, focus-visible re-uses the hover treatment, and snap keeps the focused card in view.
Make it yours
- Card scale: the
clamp(340px, 56svh, 540px)height is the single size dial. Compact 'tray' rails use ~200px; immersive pickers use72svh. - Other verticals: 9:16 is Stories/Shorts/Reels;
3/4reads as Pinterest,2/3as movie posters (see demo 11). One number swaps the genre. - Snap feel:
mandatoryalways lands on a card edge — switch toproximityif users should be able to park mid-rail. Addscroll-behavior:smoothfor paddle buttons. - Live rings: the unwatched ring is a conic-gradient. Animate its hue with a registered @property for the 'live' pulse, or gray it out via a .watched class.
Gotchas — read before shipping
- In a flex row, aspect-ratio only derives width while the item's width is auto —
flex:none(no grow, no shrink, auto basis) is load-bearing.flex:1or a percentage basis overrides the ratio. - Use svh, not vh, in the height clamp: on iOS the URL bar makes 100vh taller than the visible fold, and vh-sized cards get their bottom chrome clipped.
- mask-image on the rail also fades the scrollbar if the scrollbar is inside the masked box — style the scrollbar (as here) or move the mask to a wrapper.
- overflow-x:auto creates a focusless scroll container in some browsers; keeping the cards themselves focusable anchors (as here) is what makes the rail keyboard-accessible.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
aspect-ratio + scroll-snap are 2021-universal; svh is Chrome 108 / Safari 15.4 / Firefox 101 (vh fallback declared first); unprefixed mask-image is Chrome 120 / Safari 15.4 / Firefox 53 (-webkit- kept for older Chromium). Floor reflects oklch()/color-mix tokens.