17 CSS Sticky Elements07 / 17
CSS Sticky Section Headers List
The iOS-contacts pattern: alphabetical group headers that pin to the top of the list, then get pushed out by the next arriving header — that satisfying shove is not an animation, it's the containing block doing its job. Each header is sticky inside its own <section>, so its leash ends exactly where the next group begins. A directory app shell with a letter rail, all CSS, zero scroll listeners.
Published
The code
<section class="cst-07" aria-label="Sticky section headers list demo">
<div class="cst-07__stage">
<header class="cst-07__bar">
<h1>Directory</h1>
<label class="cst-07__search"><span class="cst-07__vh">Search people</span><svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="9" cy="9" r="6" fill="none" stroke="currentColor" stroke-width="2"/><path d="m13.5 13.5 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input type="search" placeholder="Search 23 people…"></label>
</header>
<div class="cst-07__body">
<div class="cst-07__list" id="cst07-list" tabindex="0">
<section class="cst-07__group" id="cst07-A"><h3>A</h3><ul>
<li><i style="--h:280">AB</i><div><strong>Amara Bailey</strong><span>Product design · Sydney</span></div><a href="#cst07-A" aria-label="Call Amara Bailey">+61 2 5550 114</a></li>
<li><i style="--h:180">AO</i><div><strong>Arthur Okonkwo</strong><span>Platform eng · London</span></div><a href="#cst07-A" aria-label="Call Arthur Okonkwo">+44 20 5550 883</a></li>
<li><i style="--h:60">AS</i><div><strong>Astrid Solheim</strong><span>Research · remote</span></div><a href="#cst07-A" aria-label="Call Astrid Solheim">+1 604 555 0192</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-B"><h3>B</h3><ul>
<li><i style="--h:330">BC</i><div><strong>Bea Castellanos</strong><span>Brand · Austin</span></div><a href="#cst07-B" aria-label="Call Bea Castellanos">+1 512 555 0147</a></li>
<li><i style="--h:220">BN</i><div><strong>Bodhi Ngata</strong><span>Mobile eng · Auckland</span></div><a href="#cst07-B" aria-label="Call Bodhi Ngata">+64 9 555 0173</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-C"><h3>C</h3><ul>
<li><i style="--h:120">CD</i><div><strong>Callum Doyle</strong><span>Support lead · Dublin</span></div><a href="#cst07-C" aria-label="Call Callum Doyle">+353 1 555 0128</a></li>
<li><i style="--h:20">CF</i><div><strong>Cleo Fontaine</strong><span>Content · Montréal</span></div><a href="#cst07-C" aria-label="Call Cleo Fontaine">+1 514 555 0136</a></li>
<li><i style="--h:260">CW</i><div><strong>Casey Wu</strong><span>Data science · Seattle</span></div><a href="#cst07-C" aria-label="Call Casey Wu">+1 206 555 0161</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-H"><h3>H</h3><ul>
<li><i style="--h:200">HK</i><div><strong>Hana Kobayashi</strong><span>Design systems · Vancouver</span></div><a href="#cst07-H" aria-label="Call Hana Kobayashi">+1 604 555 0175</a></li>
<li><i style="--h:300">HP</i><div><strong>Hugh Prescott</strong><span>Finance · Edinburgh</span></div><a href="#cst07-H" aria-label="Call Hugh Prescott">+44 131 555 0119</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-J"><h3>J</h3><ul>
<li><i style="--h:90">JM</i><div><strong>Jonas Meyer</strong><span>Security · Berlin</span></div><a href="#cst07-J" aria-label="Call Jonas Meyer">+49 30 5550 0142</a></li>
<li><i style="--h:350">JT</i><div><strong>Jaya Thakur</strong><span>PM, payments · Toronto</span></div><a href="#cst07-J" aria-label="Call Jaya Thakur">+1 416 555 0187</a></li>
<li><i style="--h:150">JW</i><div><strong>Jesse Whitehorse</strong><span>QA · Phoenix</span></div><a href="#cst07-J" aria-label="Call Jesse Whitehorse">+1 602 555 0158</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-M"><h3>M</h3><ul>
<li><i style="--h:240">MA</i><div><strong>Miriam Abebe</strong><span>Engineering manager · NYC</span></div><a href="#cst07-M" aria-label="Call Miriam Abebe">+1 212 555 0139</a></li>
<li><i style="--h:40">MF</i><div><strong>Marco Ferrari</strong><span>iOS · Melbourne</span></div><a href="#cst07-M" aria-label="Call Marco Ferrari">+61 3 5550 168</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-R"><h3>R</h3><ul>
<li><i style="--h:310">RC</i><div><strong>Rhiannon Carey</strong><span>Legal · Cardiff</span></div><a href="#cst07-R" aria-label="Call Rhiannon Carey">+44 29 5550 122</a></li>
<li><i style="--h:170">RS</i><div><strong>Rafael Santos</strong><span>SRE · Lisbon</span></div><a href="#cst07-R" aria-label="Call Rafael Santos">+351 21 555 0176</a></li>
<li><i style="--h:70">RV</i><div><strong>Ruby Vance</strong><span>Marketing · Chicago</span></div><a href="#cst07-R" aria-label="Call Ruby Vance">+1 312 555 0114</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-S"><h3>S</h3><ul>
<li><i style="--h:130">SB</i><div><strong>Saoirse Byrne</strong><span>Customer research · Galway</span></div><a href="#cst07-S" aria-label="Call Saoirse Byrne">+353 91 555 0151</a></li>
<li><i style="--h:290">SO</i><div><strong>Sam Osei</strong><span>Growth · London</span></div><a href="#cst07-S" aria-label="Call Sam Osei">+44 20 5550 199</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-T"><h3>T</h3><ul>
<li><i style="--h:210">TH</i><div><strong>Tove Hansen</strong><span>Ops · Copenhagen</span></div><a href="#cst07-T" aria-label="Call Tove Hansen">+45 33 555 0184</a></li>
<li><i style="--h:10">TN</i><div><strong>Tāne Ngata</strong><span>Partnerships · Wellington</span></div><a href="#cst07-T" aria-label="Call Tāne Ngata">+64 4 555 0129</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-W"><h3>W</h3><ul>
<li><i style="--h:100">WF</i><div><strong>Willa Fitzgerald</strong><span>People · Denver</span></div><a href="#cst07-W" aria-label="Call Willa Fitzgerald">+1 303 555 0146</a></li>
<li><i style="--h:250">WT</i><div><strong>Wes Tanaka</strong><span>Video · Los Angeles</span></div><a href="#cst07-W" aria-label="Call Wes Tanaka">+1 213 555 0171</a></li>
</ul></section>
<p class="cst-07__end">23 people · headers above are <code>position: sticky; top: 0</code>, each caged by its own section</p>
</div>
<nav class="cst-07__rail" aria-label="Jump to letter">
<a href="#cst07-A">A</a><a href="#cst07-B">B</a><a href="#cst07-C">C</a><a href="#cst07-H">H</a><a href="#cst07-J">J</a><a href="#cst07-M">M</a><a href="#cst07-R">R</a><a href="#cst07-S">S</a><a href="#cst07-T">T</a><a href="#cst07-W">W</a>
</nav>
</div>
</div>
</section><section class="cst-07" aria-label="Sticky section headers list demo">
<div class="cst-07__stage">
<header class="cst-07__bar">
<h1>Directory</h1>
<label class="cst-07__search"><span class="cst-07__vh">Search people</span><svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="9" cy="9" r="6" fill="none" stroke="currentColor" stroke-width="2"/><path d="m13.5 13.5 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input type="search" placeholder="Search 23 people…"></label>
</header>
<div class="cst-07__body">
<div class="cst-07__list" id="cst07-list" tabindex="0">
<section class="cst-07__group" id="cst07-A"><h3>A</h3><ul>
<li><i style="--h:280">AB</i><div><strong>Amara Bailey</strong><span>Product design · Sydney</span></div><a href="#cst07-A" aria-label="Call Amara Bailey">+61 2 5550 114</a></li>
<li><i style="--h:180">AO</i><div><strong>Arthur Okonkwo</strong><span>Platform eng · London</span></div><a href="#cst07-A" aria-label="Call Arthur Okonkwo">+44 20 5550 883</a></li>
<li><i style="--h:60">AS</i><div><strong>Astrid Solheim</strong><span>Research · remote</span></div><a href="#cst07-A" aria-label="Call Astrid Solheim">+1 604 555 0192</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-B"><h3>B</h3><ul>
<li><i style="--h:330">BC</i><div><strong>Bea Castellanos</strong><span>Brand · Austin</span></div><a href="#cst07-B" aria-label="Call Bea Castellanos">+1 512 555 0147</a></li>
<li><i style="--h:220">BN</i><div><strong>Bodhi Ngata</strong><span>Mobile eng · Auckland</span></div><a href="#cst07-B" aria-label="Call Bodhi Ngata">+64 9 555 0173</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-C"><h3>C</h3><ul>
<li><i style="--h:120">CD</i><div><strong>Callum Doyle</strong><span>Support lead · Dublin</span></div><a href="#cst07-C" aria-label="Call Callum Doyle">+353 1 555 0128</a></li>
<li><i style="--h:20">CF</i><div><strong>Cleo Fontaine</strong><span>Content · Montréal</span></div><a href="#cst07-C" aria-label="Call Cleo Fontaine">+1 514 555 0136</a></li>
<li><i style="--h:260">CW</i><div><strong>Casey Wu</strong><span>Data science · Seattle</span></div><a href="#cst07-C" aria-label="Call Casey Wu">+1 206 555 0161</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-H"><h3>H</h3><ul>
<li><i style="--h:200">HK</i><div><strong>Hana Kobayashi</strong><span>Design systems · Vancouver</span></div><a href="#cst07-H" aria-label="Call Hana Kobayashi">+1 604 555 0175</a></li>
<li><i style="--h:300">HP</i><div><strong>Hugh Prescott</strong><span>Finance · Edinburgh</span></div><a href="#cst07-H" aria-label="Call Hugh Prescott">+44 131 555 0119</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-J"><h3>J</h3><ul>
<li><i style="--h:90">JM</i><div><strong>Jonas Meyer</strong><span>Security · Berlin</span></div><a href="#cst07-J" aria-label="Call Jonas Meyer">+49 30 5550 0142</a></li>
<li><i style="--h:350">JT</i><div><strong>Jaya Thakur</strong><span>PM, payments · Toronto</span></div><a href="#cst07-J" aria-label="Call Jaya Thakur">+1 416 555 0187</a></li>
<li><i style="--h:150">JW</i><div><strong>Jesse Whitehorse</strong><span>QA · Phoenix</span></div><a href="#cst07-J" aria-label="Call Jesse Whitehorse">+1 602 555 0158</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-M"><h3>M</h3><ul>
<li><i style="--h:240">MA</i><div><strong>Miriam Abebe</strong><span>Engineering manager · NYC</span></div><a href="#cst07-M" aria-label="Call Miriam Abebe">+1 212 555 0139</a></li>
<li><i style="--h:40">MF</i><div><strong>Marco Ferrari</strong><span>iOS · Melbourne</span></div><a href="#cst07-M" aria-label="Call Marco Ferrari">+61 3 5550 168</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-R"><h3>R</h3><ul>
<li><i style="--h:310">RC</i><div><strong>Rhiannon Carey</strong><span>Legal · Cardiff</span></div><a href="#cst07-R" aria-label="Call Rhiannon Carey">+44 29 5550 122</a></li>
<li><i style="--h:170">RS</i><div><strong>Rafael Santos</strong><span>SRE · Lisbon</span></div><a href="#cst07-R" aria-label="Call Rafael Santos">+351 21 555 0176</a></li>
<li><i style="--h:70">RV</i><div><strong>Ruby Vance</strong><span>Marketing · Chicago</span></div><a href="#cst07-R" aria-label="Call Ruby Vance">+1 312 555 0114</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-S"><h3>S</h3><ul>
<li><i style="--h:130">SB</i><div><strong>Saoirse Byrne</strong><span>Customer research · Galway</span></div><a href="#cst07-S" aria-label="Call Saoirse Byrne">+353 91 555 0151</a></li>
<li><i style="--h:290">SO</i><div><strong>Sam Osei</strong><span>Growth · London</span></div><a href="#cst07-S" aria-label="Call Sam Osei">+44 20 5550 199</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-T"><h3>T</h3><ul>
<li><i style="--h:210">TH</i><div><strong>Tove Hansen</strong><span>Ops · Copenhagen</span></div><a href="#cst07-T" aria-label="Call Tove Hansen">+45 33 555 0184</a></li>
<li><i style="--h:10">TN</i><div><strong>Tāne Ngata</strong><span>Partnerships · Wellington</span></div><a href="#cst07-T" aria-label="Call Tāne Ngata">+64 4 555 0129</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-W"><h3>W</h3><ul>
<li><i style="--h:100">WF</i><div><strong>Willa Fitzgerald</strong><span>People · Denver</span></div><a href="#cst07-W" aria-label="Call Willa Fitzgerald">+1 303 555 0146</a></li>
<li><i style="--h:250">WT</i><div><strong>Wes Tanaka</strong><span>Video · Los Angeles</span></div><a href="#cst07-W" aria-label="Call Wes Tanaka">+1 213 555 0171</a></li>
</ul></section>
<p class="cst-07__end">23 people · headers above are <code>position: sticky; top: 0</code>, each caged by its own section</p>
</div>
<nav class="cst-07__rail" aria-label="Jump to letter">
<a href="#cst07-A">A</a><a href="#cst07-B">B</a><a href="#cst07-C">C</a><a href="#cst07-H">H</a><a href="#cst07-J">J</a><a href="#cst07-M">M</a><a href="#cst07-R">R</a><a href="#cst07-S">S</a><a href="#cst07-T">T</a><a href="#cst07-W">W</a>
</nav>
</div>
</div>
</section>.cst-07,
.cst-07 *,
.cst-07 *::before,
.cst-07 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cst-07 {
--bg: oklch(0.97 0.005 260);
--pane: oklch(1 0 0);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.53 0.012 260);
--line: oklch(0.915 0.006 260);
--acc: oklch(0.58 0.17 260);
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);
}
.cst-07__stage {
width: 100%;
container: cst07/inline-size;
height: 100vh;
height: 100svh;
display: flex;
flex-direction: column;
background: var(--bg);
}
.cst-07__bar {
display: flex;
align-items: center;
gap: 18px;
padding: 16px clamp(16px,3.5cqi,32px) 14px;
}
.cst-07__bar h1 {
font-size: clamp(22px,3.4cqi,30px);
letter-spacing: -.03em;
font-weight: 800;
}
.cst-07__search {
display: flex;
align-items: center;
gap: 9px;
flex: 0 1 340px;
margin-left: auto;
min-height: 42px;
padding: 0 14px;
border-radius: 12px;
background: var(--pane);
border: 1.5px solid var(--line);
color: var(--mut);
transition: border-color .2s,box-shadow .2s;
}
.cst-07__search:focus-within {
border-color: var(--acc);
box-shadow: 0 0 0 3px oklch(0.58 0.17 260/.15);
color: var(--ink);
}
.cst-07__search input {
flex: 1;
min-width: 0;
border: none;
background: none;
font: inherit;
font-size: 13.5px;
color: var(--ink);
outline: none;
}
.cst-07__search input::placeholder {
color: oklch(0.66 0.01 260);
}
.cst-07__vh {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.cst-07__body {
flex: 1;
min-height: 0;
display: flex;
gap: 4px;
padding: 0 clamp(10px,2.5cqi,26px) clamp(12px,2.5cqi,22px);
}
.cst-07__list {
flex: 1;
min-width: 0;
overflow-y: auto;
border-radius: 18px;
background: var(--pane);
border: 1px solid var(--line);
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cst-07__list:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cst-07__group h3 {
position: sticky;
top: -1px;
z-index: 2;
display: flex;
align-items: center;
min-height: 34px;
padding: 1px 18px 0;
font-size: 12.5px;
font-weight: 800;
letter-spacing: .1em;
color: var(--acc);
background: color-mix(in oklch,var(--pane) 82%,transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.cst-07__group {
scroll-margin-top: 0;
}
.cst-07__group ul {
list-style: none;
}
.cst-07__group li {
display: flex;
align-items: center;
gap: 13px;
min-height: 60px;
padding: 8px 18px;
border-bottom: 1px solid color-mix(in oklch,var(--line) 55%,transparent);
transition: background .15s;
}
.cst-07__group li:hover {
background: oklch(0.975 0.005 260);
}
.cst-07__group li:last-child {
border-bottom: none;
}
.cst-07__group i {
flex: none;
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-style: normal;
font-size: 12.5px;
font-weight: 800;
color: oklch(0.99 0 0);
background: linear-gradient(140deg,oklch(0.68 0.14 var(--h,260)),oklch(0.5 0.15 calc(var(--h,260) + 40)));
}
.cst-07__group strong {
display: block;
font-size: 14.5px;
letter-spacing: -.005em;
}
.cst-07__group span {
font-size: 12px;
color: var(--mut);
}
.cst-07__group li a {
margin-left: auto;
flex: none;
display: grid;
place-items: center;
min-height: 36px;
padding: 0 12px;
border-radius: 9px;
font-family: ui-monospace,Menlo,monospace;
font-size: 11.5px;
font-weight: 600;
color: var(--mut);
text-decoration: none;
transition: color .2s,background .2s;
}
.cst-07__group li a:hover,
.cst-07__group li a:focus-visible {
color: var(--acc);
background: oklch(0.93 0.02 260/.6);
}
.cst-07__group li a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
}
.cst-07__end {
padding: 18px;
font-size: 12px;
color: var(--mut);
text-align: center;
}
.cst-07__end code {
font-family: ui-monospace,Menlo,monospace;
font-size: .92em;
background: oklch(0.94 0.008 260);
padding: 1px 5px;
border-radius: 4px;
}
.cst-07__rail {
flex: none;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1px;
padding: 6px 2px;
}
.cst-07__rail a {
display: grid;
place-items: center;
width: 26px;
min-height: 26px;
border-radius: 7px;
font-size: 10.5px;
font-weight: 800;
color: var(--mut);
text-decoration: none;
transition: color .15s,background .15s,scale .15s;
}
.cst-07__rail a:hover,
.cst-07__rail a:focus-visible {
color: oklch(0.99 0 0);
background: var(--acc);
scale: 1.15;
}
.cst-07__rail a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
@container cst07 (width < 480px) {
.cst-07__bar {
flex-wrap: wrap;
gap: 10px;
}
.cst-07__search {
flex-basis: 100%;
}
.cst-07__group li a {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.cst-07__list {
scroll-behavior: auto;
}
.cst-07 * {
transition-duration: .01ms !important;
scale: none !important;
}
}.cst-07,
.cst-07 *,
.cst-07 *::before,
.cst-07 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cst-07 {
--bg: oklch(0.97 0.005 260);
--pane: oklch(1 0 0);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.53 0.012 260);
--line: oklch(0.915 0.006 260);
--acc: oklch(0.58 0.17 260);
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);
}
.cst-07__stage {
width: 100%;
container: cst07/inline-size;
height: 100vh;
height: 100svh;
display: flex;
flex-direction: column;
background: var(--bg);
}
.cst-07__bar {
display: flex;
align-items: center;
gap: 18px;
padding: 16px clamp(16px,3.5cqi,32px) 14px;
}
.cst-07__bar h1 {
font-size: clamp(22px,3.4cqi,30px);
letter-spacing: -.03em;
font-weight: 800;
}
.cst-07__search {
display: flex;
align-items: center;
gap: 9px;
flex: 0 1 340px;
margin-left: auto;
min-height: 42px;
padding: 0 14px;
border-radius: 12px;
background: var(--pane);
border: 1.5px solid var(--line);
color: var(--mut);
transition: border-color .2s,box-shadow .2s;
}
.cst-07__search:focus-within {
border-color: var(--acc);
box-shadow: 0 0 0 3px oklch(0.58 0.17 260/.15);
color: var(--ink);
}
.cst-07__search input {
flex: 1;
min-width: 0;
border: none;
background: none;
font: inherit;
font-size: 13.5px;
color: var(--ink);
outline: none;
}
.cst-07__search input::placeholder {
color: oklch(0.66 0.01 260);
}
.cst-07__vh {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.cst-07__body {
flex: 1;
min-height: 0;
display: flex;
gap: 4px;
padding: 0 clamp(10px,2.5cqi,26px) clamp(12px,2.5cqi,22px);
}
.cst-07__list {
flex: 1;
min-width: 0;
overflow-y: auto;
border-radius: 18px;
background: var(--pane);
border: 1px solid var(--line);
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cst-07__list:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cst-07__group h3 {
position: sticky;
top: -1px;
z-index: 2;
display: flex;
align-items: center;
min-height: 34px;
padding: 1px 18px 0;
font-size: 12.5px;
font-weight: 800;
letter-spacing: .1em;
color: var(--acc);
background: color-mix(in oklch,var(--pane) 82%,transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.cst-07__group {
scroll-margin-top: 0;
}
.cst-07__group ul {
list-style: none;
}
.cst-07__group li {
display: flex;
align-items: center;
gap: 13px;
min-height: 60px;
padding: 8px 18px;
border-bottom: 1px solid color-mix(in oklch,var(--line) 55%,transparent);
transition: background .15s;
}
.cst-07__group li:hover {
background: oklch(0.975 0.005 260);
}
.cst-07__group li:last-child {
border-bottom: none;
}
.cst-07__group i {
flex: none;
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-style: normal;
font-size: 12.5px;
font-weight: 800;
color: oklch(0.99 0 0);
background: linear-gradient(140deg,oklch(0.68 0.14 var(--h,260)),oklch(0.5 0.15 calc(var(--h,260) + 40)));
}
.cst-07__group strong {
display: block;
font-size: 14.5px;
letter-spacing: -.005em;
}
.cst-07__group span {
font-size: 12px;
color: var(--mut);
}
.cst-07__group li a {
margin-left: auto;
flex: none;
display: grid;
place-items: center;
min-height: 36px;
padding: 0 12px;
border-radius: 9px;
font-family: ui-monospace,Menlo,monospace;
font-size: 11.5px;
font-weight: 600;
color: var(--mut);
text-decoration: none;
transition: color .2s,background .2s;
}
.cst-07__group li a:hover,
.cst-07__group li a:focus-visible {
color: var(--acc);
background: oklch(0.93 0.02 260/.6);
}
.cst-07__group li a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
}
.cst-07__end {
padding: 18px;
font-size: 12px;
color: var(--mut);
text-align: center;
}
.cst-07__end code {
font-family: ui-monospace,Menlo,monospace;
font-size: .92em;
background: oklch(0.94 0.008 260);
padding: 1px 5px;
border-radius: 4px;
}
.cst-07__rail {
flex: none;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1px;
padding: 6px 2px;
}
.cst-07__rail a {
display: grid;
place-items: center;
width: 26px;
min-height: 26px;
border-radius: 7px;
font-size: 10.5px;
font-weight: 800;
color: var(--mut);
text-decoration: none;
transition: color .15s,background .15s,scale .15s;
}
.cst-07__rail a:hover,
.cst-07__rail a:focus-visible {
color: oklch(0.99 0 0);
background: var(--acc);
scale: 1.15;
}
.cst-07__rail a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
@container cst07 (width < 480px) {
.cst-07__bar {
flex-wrap: wrap;
gap: 10px;
}
.cst-07__search {
flex-basis: 100%;
}
.cst-07__group li a {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.cst-07__list {
scroll-behavior: auto;
}
.cst-07 * {
transition-duration: .01ms !important;
scale: none !important;
}
}Here's a working CSS Sticky Element 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 Sticky Section Headers List
Source: https://codefronts.com/layouts/css-sticky-elements/css-sticky-section-headers-list/
The iOS-contacts pattern: alphabetical group headers that pin to the top of the list, then get pushed out by the next arriving header — that satisfying shove is not an animation, it's the containing block doing its job. Each header is sticky inside its own <section>, so its leash ends exactly where the next group begins. A directory app shell with a letter rail, all CSS, zero scroll listeners.
## HTML
```html
<section class="cst-07" aria-label="Sticky section headers list demo">
<div class="cst-07__stage">
<header class="cst-07__bar">
<h1>Directory</h1>
<label class="cst-07__search"><span class="cst-07__vh">Search people</span><svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="9" cy="9" r="6" fill="none" stroke="currentColor" stroke-width="2"/><path d="m13.5 13.5 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input type="search" placeholder="Search 23 people…"></label>
</header>
<div class="cst-07__body">
<div class="cst-07__list" id="cst07-list" tabindex="0">
<section class="cst-07__group" id="cst07-A"><h3>A</h3><ul>
<li><i style="--h:280">AB</i><div><strong>Amara Bailey</strong><span>Product design · Sydney</span></div><a href="#cst07-A" aria-label="Call Amara Bailey">+61 2 5550 114</a></li>
<li><i style="--h:180">AO</i><div><strong>Arthur Okonkwo</strong><span>Platform eng · London</span></div><a href="#cst07-A" aria-label="Call Arthur Okonkwo">+44 20 5550 883</a></li>
<li><i style="--h:60">AS</i><div><strong>Astrid Solheim</strong><span>Research · remote</span></div><a href="#cst07-A" aria-label="Call Astrid Solheim">+1 604 555 0192</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-B"><h3>B</h3><ul>
<li><i style="--h:330">BC</i><div><strong>Bea Castellanos</strong><span>Brand · Austin</span></div><a href="#cst07-B" aria-label="Call Bea Castellanos">+1 512 555 0147</a></li>
<li><i style="--h:220">BN</i><div><strong>Bodhi Ngata</strong><span>Mobile eng · Auckland</span></div><a href="#cst07-B" aria-label="Call Bodhi Ngata">+64 9 555 0173</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-C"><h3>C</h3><ul>
<li><i style="--h:120">CD</i><div><strong>Callum Doyle</strong><span>Support lead · Dublin</span></div><a href="#cst07-C" aria-label="Call Callum Doyle">+353 1 555 0128</a></li>
<li><i style="--h:20">CF</i><div><strong>Cleo Fontaine</strong><span>Content · Montréal</span></div><a href="#cst07-C" aria-label="Call Cleo Fontaine">+1 514 555 0136</a></li>
<li><i style="--h:260">CW</i><div><strong>Casey Wu</strong><span>Data science · Seattle</span></div><a href="#cst07-C" aria-label="Call Casey Wu">+1 206 555 0161</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-H"><h3>H</h3><ul>
<li><i style="--h:200">HK</i><div><strong>Hana Kobayashi</strong><span>Design systems · Vancouver</span></div><a href="#cst07-H" aria-label="Call Hana Kobayashi">+1 604 555 0175</a></li>
<li><i style="--h:300">HP</i><div><strong>Hugh Prescott</strong><span>Finance · Edinburgh</span></div><a href="#cst07-H" aria-label="Call Hugh Prescott">+44 131 555 0119</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-J"><h3>J</h3><ul>
<li><i style="--h:90">JM</i><div><strong>Jonas Meyer</strong><span>Security · Berlin</span></div><a href="#cst07-J" aria-label="Call Jonas Meyer">+49 30 5550 0142</a></li>
<li><i style="--h:350">JT</i><div><strong>Jaya Thakur</strong><span>PM, payments · Toronto</span></div><a href="#cst07-J" aria-label="Call Jaya Thakur">+1 416 555 0187</a></li>
<li><i style="--h:150">JW</i><div><strong>Jesse Whitehorse</strong><span>QA · Phoenix</span></div><a href="#cst07-J" aria-label="Call Jesse Whitehorse">+1 602 555 0158</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-M"><h3>M</h3><ul>
<li><i style="--h:240">MA</i><div><strong>Miriam Abebe</strong><span>Engineering manager · NYC</span></div><a href="#cst07-M" aria-label="Call Miriam Abebe">+1 212 555 0139</a></li>
<li><i style="--h:40">MF</i><div><strong>Marco Ferrari</strong><span>iOS · Melbourne</span></div><a href="#cst07-M" aria-label="Call Marco Ferrari">+61 3 5550 168</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-R"><h3>R</h3><ul>
<li><i style="--h:310">RC</i><div><strong>Rhiannon Carey</strong><span>Legal · Cardiff</span></div><a href="#cst07-R" aria-label="Call Rhiannon Carey">+44 29 5550 122</a></li>
<li><i style="--h:170">RS</i><div><strong>Rafael Santos</strong><span>SRE · Lisbon</span></div><a href="#cst07-R" aria-label="Call Rafael Santos">+351 21 555 0176</a></li>
<li><i style="--h:70">RV</i><div><strong>Ruby Vance</strong><span>Marketing · Chicago</span></div><a href="#cst07-R" aria-label="Call Ruby Vance">+1 312 555 0114</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-S"><h3>S</h3><ul>
<li><i style="--h:130">SB</i><div><strong>Saoirse Byrne</strong><span>Customer research · Galway</span></div><a href="#cst07-S" aria-label="Call Saoirse Byrne">+353 91 555 0151</a></li>
<li><i style="--h:290">SO</i><div><strong>Sam Osei</strong><span>Growth · London</span></div><a href="#cst07-S" aria-label="Call Sam Osei">+44 20 5550 199</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-T"><h3>T</h3><ul>
<li><i style="--h:210">TH</i><div><strong>Tove Hansen</strong><span>Ops · Copenhagen</span></div><a href="#cst07-T" aria-label="Call Tove Hansen">+45 33 555 0184</a></li>
<li><i style="--h:10">TN</i><div><strong>Tāne Ngata</strong><span>Partnerships · Wellington</span></div><a href="#cst07-T" aria-label="Call Tāne Ngata">+64 4 555 0129</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-W"><h3>W</h3><ul>
<li><i style="--h:100">WF</i><div><strong>Willa Fitzgerald</strong><span>People · Denver</span></div><a href="#cst07-W" aria-label="Call Willa Fitzgerald">+1 303 555 0146</a></li>
<li><i style="--h:250">WT</i><div><strong>Wes Tanaka</strong><span>Video · Los Angeles</span></div><a href="#cst07-W" aria-label="Call Wes Tanaka">+1 213 555 0171</a></li>
</ul></section>
<p class="cst-07__end">23 people · headers above are <code>position: sticky; top: 0</code>, each caged by its own section</p>
</div>
<nav class="cst-07__rail" aria-label="Jump to letter">
<a href="#cst07-A">A</a><a href="#cst07-B">B</a><a href="#cst07-C">C</a><a href="#cst07-H">H</a><a href="#cst07-J">J</a><a href="#cst07-M">M</a><a href="#cst07-R">R</a><a href="#cst07-S">S</a><a href="#cst07-T">T</a><a href="#cst07-W">W</a>
</nav>
</div>
</div>
</section>
```
## CSS
```css
.cst-07,
.cst-07 *,
.cst-07 *::before,
.cst-07 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cst-07 {
--bg: oklch(0.97 0.005 260);
--pane: oklch(1 0 0);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.53 0.012 260);
--line: oklch(0.915 0.006 260);
--acc: oklch(0.58 0.17 260);
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);
}
.cst-07__stage {
width: 100%;
container: cst07/inline-size;
height: 100vh;
height: 100svh;
display: flex;
flex-direction: column;
background: var(--bg);
}
.cst-07__bar {
display: flex;
align-items: center;
gap: 18px;
padding: 16px clamp(16px,3.5cqi,32px) 14px;
}
.cst-07__bar h1 {
font-size: clamp(22px,3.4cqi,30px);
letter-spacing: -.03em;
font-weight: 800;
}
.cst-07__search {
display: flex;
align-items: center;
gap: 9px;
flex: 0 1 340px;
margin-left: auto;
min-height: 42px;
padding: 0 14px;
border-radius: 12px;
background: var(--pane);
border: 1.5px solid var(--line);
color: var(--mut);
transition: border-color .2s,box-shadow .2s;
}
.cst-07__search:focus-within {
border-color: var(--acc);
box-shadow: 0 0 0 3px oklch(0.58 0.17 260/.15);
color: var(--ink);
}
.cst-07__search input {
flex: 1;
min-width: 0;
border: none;
background: none;
font: inherit;
font-size: 13.5px;
color: var(--ink);
outline: none;
}
.cst-07__search input::placeholder {
color: oklch(0.66 0.01 260);
}
.cst-07__vh {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.cst-07__body {
flex: 1;
min-height: 0;
display: flex;
gap: 4px;
padding: 0 clamp(10px,2.5cqi,26px) clamp(12px,2.5cqi,22px);
}
.cst-07__list {
flex: 1;
min-width: 0;
overflow-y: auto;
border-radius: 18px;
background: var(--pane);
border: 1px solid var(--line);
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cst-07__list:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cst-07__group h3 {
position: sticky;
top: -1px;
z-index: 2;
display: flex;
align-items: center;
min-height: 34px;
padding: 1px 18px 0;
font-size: 12.5px;
font-weight: 800;
letter-spacing: .1em;
color: var(--acc);
background: color-mix(in oklch,var(--pane) 82%,transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.cst-07__group {
scroll-margin-top: 0;
}
.cst-07__group ul {
list-style: none;
}
.cst-07__group li {
display: flex;
align-items: center;
gap: 13px;
min-height: 60px;
padding: 8px 18px;
border-bottom: 1px solid color-mix(in oklch,var(--line) 55%,transparent);
transition: background .15s;
}
.cst-07__group li:hover {
background: oklch(0.975 0.005 260);
}
.cst-07__group li:last-child {
border-bottom: none;
}
.cst-07__group i {
flex: none;
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-style: normal;
font-size: 12.5px;
font-weight: 800;
color: oklch(0.99 0 0);
background: linear-gradient(140deg,oklch(0.68 0.14 var(--h,260)),oklch(0.5 0.15 calc(var(--h,260) + 40)));
}
.cst-07__group strong {
display: block;
font-size: 14.5px;
letter-spacing: -.005em;
}
.cst-07__group span {
font-size: 12px;
color: var(--mut);
}
.cst-07__group li a {
margin-left: auto;
flex: none;
display: grid;
place-items: center;
min-height: 36px;
padding: 0 12px;
border-radius: 9px;
font-family: ui-monospace,Menlo,monospace;
font-size: 11.5px;
font-weight: 600;
color: var(--mut);
text-decoration: none;
transition: color .2s,background .2s;
}
.cst-07__group li a:hover,
.cst-07__group li a:focus-visible {
color: var(--acc);
background: oklch(0.93 0.02 260/.6);
}
.cst-07__group li a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
}
.cst-07__end {
padding: 18px;
font-size: 12px;
color: var(--mut);
text-align: center;
}
.cst-07__end code {
font-family: ui-monospace,Menlo,monospace;
font-size: .92em;
background: oklch(0.94 0.008 260);
padding: 1px 5px;
border-radius: 4px;
}
.cst-07__rail {
flex: none;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1px;
padding: 6px 2px;
}
.cst-07__rail a {
display: grid;
place-items: center;
width: 26px;
min-height: 26px;
border-radius: 7px;
font-size: 10.5px;
font-weight: 800;
color: var(--mut);
text-decoration: none;
transition: color .15s,background .15s,scale .15s;
}
.cst-07__rail a:hover,
.cst-07__rail a:focus-visible {
color: oklch(0.99 0 0);
background: var(--acc);
scale: 1.15;
}
.cst-07__rail a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
@container cst07 (width < 480px) {
.cst-07__bar {
flex-wrap: wrap;
gap: 10px;
}
.cst-07__search {
flex-basis: 100%;
}
.cst-07__group li a {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.cst-07__list {
scroll-behavior: auto;
}
.cst-07 * {
transition-duration: .01ms !important;
scale: none !important;
}
}
```Here's a working CSS Sticky Element 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 Sticky Section Headers List
Source: https://codefronts.com/layouts/css-sticky-elements/css-sticky-section-headers-list/
The iOS-contacts pattern: alphabetical group headers that pin to the top of the list, then get pushed out by the next arriving header — that satisfying shove is not an animation, it's the containing block doing its job. Each header is sticky inside its own <section>, so its leash ends exactly where the next group begins. A directory app shell with a letter rail, all CSS, zero scroll listeners.
## HTML
```html
<section class="cst-07" aria-label="Sticky section headers list demo">
<div class="cst-07__stage">
<header class="cst-07__bar">
<h1>Directory</h1>
<label class="cst-07__search"><span class="cst-07__vh">Search people</span><svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true"><circle cx="9" cy="9" r="6" fill="none" stroke="currentColor" stroke-width="2"/><path d="m13.5 13.5 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input type="search" placeholder="Search 23 people…"></label>
</header>
<div class="cst-07__body">
<div class="cst-07__list" id="cst07-list" tabindex="0">
<section class="cst-07__group" id="cst07-A"><h3>A</h3><ul>
<li><i style="--h:280">AB</i><div><strong>Amara Bailey</strong><span>Product design · Sydney</span></div><a href="#cst07-A" aria-label="Call Amara Bailey">+61 2 5550 114</a></li>
<li><i style="--h:180">AO</i><div><strong>Arthur Okonkwo</strong><span>Platform eng · London</span></div><a href="#cst07-A" aria-label="Call Arthur Okonkwo">+44 20 5550 883</a></li>
<li><i style="--h:60">AS</i><div><strong>Astrid Solheim</strong><span>Research · remote</span></div><a href="#cst07-A" aria-label="Call Astrid Solheim">+1 604 555 0192</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-B"><h3>B</h3><ul>
<li><i style="--h:330">BC</i><div><strong>Bea Castellanos</strong><span>Brand · Austin</span></div><a href="#cst07-B" aria-label="Call Bea Castellanos">+1 512 555 0147</a></li>
<li><i style="--h:220">BN</i><div><strong>Bodhi Ngata</strong><span>Mobile eng · Auckland</span></div><a href="#cst07-B" aria-label="Call Bodhi Ngata">+64 9 555 0173</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-C"><h3>C</h3><ul>
<li><i style="--h:120">CD</i><div><strong>Callum Doyle</strong><span>Support lead · Dublin</span></div><a href="#cst07-C" aria-label="Call Callum Doyle">+353 1 555 0128</a></li>
<li><i style="--h:20">CF</i><div><strong>Cleo Fontaine</strong><span>Content · Montréal</span></div><a href="#cst07-C" aria-label="Call Cleo Fontaine">+1 514 555 0136</a></li>
<li><i style="--h:260">CW</i><div><strong>Casey Wu</strong><span>Data science · Seattle</span></div><a href="#cst07-C" aria-label="Call Casey Wu">+1 206 555 0161</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-H"><h3>H</h3><ul>
<li><i style="--h:200">HK</i><div><strong>Hana Kobayashi</strong><span>Design systems · Vancouver</span></div><a href="#cst07-H" aria-label="Call Hana Kobayashi">+1 604 555 0175</a></li>
<li><i style="--h:300">HP</i><div><strong>Hugh Prescott</strong><span>Finance · Edinburgh</span></div><a href="#cst07-H" aria-label="Call Hugh Prescott">+44 131 555 0119</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-J"><h3>J</h3><ul>
<li><i style="--h:90">JM</i><div><strong>Jonas Meyer</strong><span>Security · Berlin</span></div><a href="#cst07-J" aria-label="Call Jonas Meyer">+49 30 5550 0142</a></li>
<li><i style="--h:350">JT</i><div><strong>Jaya Thakur</strong><span>PM, payments · Toronto</span></div><a href="#cst07-J" aria-label="Call Jaya Thakur">+1 416 555 0187</a></li>
<li><i style="--h:150">JW</i><div><strong>Jesse Whitehorse</strong><span>QA · Phoenix</span></div><a href="#cst07-J" aria-label="Call Jesse Whitehorse">+1 602 555 0158</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-M"><h3>M</h3><ul>
<li><i style="--h:240">MA</i><div><strong>Miriam Abebe</strong><span>Engineering manager · NYC</span></div><a href="#cst07-M" aria-label="Call Miriam Abebe">+1 212 555 0139</a></li>
<li><i style="--h:40">MF</i><div><strong>Marco Ferrari</strong><span>iOS · Melbourne</span></div><a href="#cst07-M" aria-label="Call Marco Ferrari">+61 3 5550 168</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-R"><h3>R</h3><ul>
<li><i style="--h:310">RC</i><div><strong>Rhiannon Carey</strong><span>Legal · Cardiff</span></div><a href="#cst07-R" aria-label="Call Rhiannon Carey">+44 29 5550 122</a></li>
<li><i style="--h:170">RS</i><div><strong>Rafael Santos</strong><span>SRE · Lisbon</span></div><a href="#cst07-R" aria-label="Call Rafael Santos">+351 21 555 0176</a></li>
<li><i style="--h:70">RV</i><div><strong>Ruby Vance</strong><span>Marketing · Chicago</span></div><a href="#cst07-R" aria-label="Call Ruby Vance">+1 312 555 0114</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-S"><h3>S</h3><ul>
<li><i style="--h:130">SB</i><div><strong>Saoirse Byrne</strong><span>Customer research · Galway</span></div><a href="#cst07-S" aria-label="Call Saoirse Byrne">+353 91 555 0151</a></li>
<li><i style="--h:290">SO</i><div><strong>Sam Osei</strong><span>Growth · London</span></div><a href="#cst07-S" aria-label="Call Sam Osei">+44 20 5550 199</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-T"><h3>T</h3><ul>
<li><i style="--h:210">TH</i><div><strong>Tove Hansen</strong><span>Ops · Copenhagen</span></div><a href="#cst07-T" aria-label="Call Tove Hansen">+45 33 555 0184</a></li>
<li><i style="--h:10">TN</i><div><strong>Tāne Ngata</strong><span>Partnerships · Wellington</span></div><a href="#cst07-T" aria-label="Call Tāne Ngata">+64 4 555 0129</a></li>
</ul></section>
<section class="cst-07__group" id="cst07-W"><h3>W</h3><ul>
<li><i style="--h:100">WF</i><div><strong>Willa Fitzgerald</strong><span>People · Denver</span></div><a href="#cst07-W" aria-label="Call Willa Fitzgerald">+1 303 555 0146</a></li>
<li><i style="--h:250">WT</i><div><strong>Wes Tanaka</strong><span>Video · Los Angeles</span></div><a href="#cst07-W" aria-label="Call Wes Tanaka">+1 213 555 0171</a></li>
</ul></section>
<p class="cst-07__end">23 people · headers above are <code>position: sticky; top: 0</code>, each caged by its own section</p>
</div>
<nav class="cst-07__rail" aria-label="Jump to letter">
<a href="#cst07-A">A</a><a href="#cst07-B">B</a><a href="#cst07-C">C</a><a href="#cst07-H">H</a><a href="#cst07-J">J</a><a href="#cst07-M">M</a><a href="#cst07-R">R</a><a href="#cst07-S">S</a><a href="#cst07-T">T</a><a href="#cst07-W">W</a>
</nav>
</div>
</div>
</section>
```
## CSS
```css
.cst-07,
.cst-07 *,
.cst-07 *::before,
.cst-07 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cst-07 {
--bg: oklch(0.97 0.005 260);
--pane: oklch(1 0 0);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.53 0.012 260);
--line: oklch(0.915 0.006 260);
--acc: oklch(0.58 0.17 260);
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);
}
.cst-07__stage {
width: 100%;
container: cst07/inline-size;
height: 100vh;
height: 100svh;
display: flex;
flex-direction: column;
background: var(--bg);
}
.cst-07__bar {
display: flex;
align-items: center;
gap: 18px;
padding: 16px clamp(16px,3.5cqi,32px) 14px;
}
.cst-07__bar h1 {
font-size: clamp(22px,3.4cqi,30px);
letter-spacing: -.03em;
font-weight: 800;
}
.cst-07__search {
display: flex;
align-items: center;
gap: 9px;
flex: 0 1 340px;
margin-left: auto;
min-height: 42px;
padding: 0 14px;
border-radius: 12px;
background: var(--pane);
border: 1.5px solid var(--line);
color: var(--mut);
transition: border-color .2s,box-shadow .2s;
}
.cst-07__search:focus-within {
border-color: var(--acc);
box-shadow: 0 0 0 3px oklch(0.58 0.17 260/.15);
color: var(--ink);
}
.cst-07__search input {
flex: 1;
min-width: 0;
border: none;
background: none;
font: inherit;
font-size: 13.5px;
color: var(--ink);
outline: none;
}
.cst-07__search input::placeholder {
color: oklch(0.66 0.01 260);
}
.cst-07__vh {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.cst-07__body {
flex: 1;
min-height: 0;
display: flex;
gap: 4px;
padding: 0 clamp(10px,2.5cqi,26px) clamp(12px,2.5cqi,22px);
}
.cst-07__list {
flex: 1;
min-width: 0;
overflow-y: auto;
border-radius: 18px;
background: var(--pane);
border: 1px solid var(--line);
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cst-07__list:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cst-07__group h3 {
position: sticky;
top: -1px;
z-index: 2;
display: flex;
align-items: center;
min-height: 34px;
padding: 1px 18px 0;
font-size: 12.5px;
font-weight: 800;
letter-spacing: .1em;
color: var(--acc);
background: color-mix(in oklch,var(--pane) 82%,transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.cst-07__group {
scroll-margin-top: 0;
}
.cst-07__group ul {
list-style: none;
}
.cst-07__group li {
display: flex;
align-items: center;
gap: 13px;
min-height: 60px;
padding: 8px 18px;
border-bottom: 1px solid color-mix(in oklch,var(--line) 55%,transparent);
transition: background .15s;
}
.cst-07__group li:hover {
background: oklch(0.975 0.005 260);
}
.cst-07__group li:last-child {
border-bottom: none;
}
.cst-07__group i {
flex: none;
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-style: normal;
font-size: 12.5px;
font-weight: 800;
color: oklch(0.99 0 0);
background: linear-gradient(140deg,oklch(0.68 0.14 var(--h,260)),oklch(0.5 0.15 calc(var(--h,260) + 40)));
}
.cst-07__group strong {
display: block;
font-size: 14.5px;
letter-spacing: -.005em;
}
.cst-07__group span {
font-size: 12px;
color: var(--mut);
}
.cst-07__group li a {
margin-left: auto;
flex: none;
display: grid;
place-items: center;
min-height: 36px;
padding: 0 12px;
border-radius: 9px;
font-family: ui-monospace,Menlo,monospace;
font-size: 11.5px;
font-weight: 600;
color: var(--mut);
text-decoration: none;
transition: color .2s,background .2s;
}
.cst-07__group li a:hover,
.cst-07__group li a:focus-visible {
color: var(--acc);
background: oklch(0.93 0.02 260/.6);
}
.cst-07__group li a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
}
.cst-07__end {
padding: 18px;
font-size: 12px;
color: var(--mut);
text-align: center;
}
.cst-07__end code {
font-family: ui-monospace,Menlo,monospace;
font-size: .92em;
background: oklch(0.94 0.008 260);
padding: 1px 5px;
border-radius: 4px;
}
.cst-07__rail {
flex: none;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1px;
padding: 6px 2px;
}
.cst-07__rail a {
display: grid;
place-items: center;
width: 26px;
min-height: 26px;
border-radius: 7px;
font-size: 10.5px;
font-weight: 800;
color: var(--mut);
text-decoration: none;
transition: color .15s,background .15s,scale .15s;
}
.cst-07__rail a:hover,
.cst-07__rail a:focus-visible {
color: oklch(0.99 0 0);
background: var(--acc);
scale: 1.15;
}
.cst-07__rail a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
@container cst07 (width < 480px) {
.cst-07__bar {
flex-wrap: wrap;
gap: 10px;
}
.cst-07__search {
flex-basis: 100%;
}
.cst-07__group li a {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.cst-07__list {
scroll-behavior: auto;
}
.cst-07 * {
transition-duration: .01ms !important;
scale: none !important;
}
}
```How this works
The entire effect is one declaration placed at the right depth in the DOM. Because a sticky element can never leave its parent, giving each group its own wrapper makes every header's territory end where the next group starts:
<section class='group'> <!-- the containing block = the leash -->
<h3>A</h3> <!-- position:sticky; top:0 -->
<ul>…contacts…</ul>
</section>
.group h3{ position:sticky; top:0; }Walk the physics: header A pins at top:0 while its section scrolls beneath. When section B's top edge reaches the pinned header, A has run out of parent — the containing block's bottom edge drags it up and off as B's header slides into the exact same slot. The 'push' is two independent stickies obeying their own cages; they never know about each other. This is the same one-line mechanism as a sticky page header, just scoped one level deeper.
Two production details: the list scrolls in its own pane (overflow-y:auto on the list, not the page) so the app bar above stays put without being sticky itself — and the headers use a translucent color-mix() background with blur so rows dim as they slide under, keeping the letter legible without a hard band. The letter rail on the right is a plain column of anchor links living outside the scroller, so it never moves at all.
Make it yours
- Header depth: move the sticky from
h3to a taller header block with counts ('A · 3 people') — the push-away works identically at any height. - Timeline flavor: swap letters for dates ('March 2026') and contacts for events — this exact structure is every activity-feed and changelog sidebar.
- Pin below an app bar: if the scroller is the whole page with a sticky header above, the letters need
top:var(--headerH)— inside a dedicated scroll pane like this demo,top:0is correct. - Sticky rail too: make the letter rail
position:sticky; top:12pxinside the scroller instead of outside it if you want it to participate in the page scroll on mobile.
Gotchas — read before shipping
- The wrapper IS the feature: putting all headers and rows in one flat parent gives every header the same containing block — they'll stack on top of each other instead of pushing (that stacking is demo 08, a different pattern).
- A 1px gap can flash between a header and the row beneath during fast scroll on some GPUs — the classic fix is
top:-1pxplus 1px extra top padding. - Sticky headers need a background: they pin OVER rows, and a transparent header becomes unreadable soup. Translucent + backdrop-filter is the premium version.
- Don't add
z-indexper header hoping to control the push — DOM order already paints later headers above earlier ones at the handoff moment.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
Bounded sticky is the original 2017 use case and works everywhere, including iOS Safari momentum scroll. Floor comes from oklch()/color-mix() cosmetics; backdrop-filter needs -webkit- for Safari ≤ 17.