Header remaining visible while scrolling.
Scroll down inside this box. The header above uses position: sticky with top: 0, so it stays pinned to the top of this scroll container instead of scrolling away with the text.
It only sticks within its own scrolling ancestor — once that ancestor ends, the header scrolls away too.
A third paragraph adds enough height to make scrolling necessary.
A fourth paragraph, for good measure.
And a fifth, so the sticky behaviour is unmistakable.
<div class="sh-demo">
<div class="sh-demo__header sh-demo__header--left sh-demo__header--shadow" style="background:#A8E6C1;">Chapter 2</div>
<p>Scroll down inside this box. The header above uses <code>position: sticky</code> with <code>top: 0</code>, so it stays pinned to the top of this scroll container instead of scrolling away with the text.</p>
<p>It only sticks within its own scrolling ancestor — once that ancestor ends, the header scrolls away too.</p>
<p>A third paragraph adds enough height to make scrolling necessary.</p>
<p>A fourth paragraph, for good measure.</p>
<p>And a fifth, so the sticky behaviour is unmistakable.</p>
</div>
<style>
.sh-demo { height: 160px; overflow-y: auto; border: 1px solid #24143022; font: 13px/1.6 system-ui, sans-serif; color: #241430; }
.sh-demo__header { position: sticky; top: 0; z-index: 2; padding: 10px 14px; font-weight: 700; }
.sh-demo__header--center { text-align: center; }
.sh-demo__header--shadow { box-shadow: 0 4px 10px rgba(36,20,48,.2); }
.sh-demo p { margin: 0; padding: 10px 14px 0; color: #241430cc; }
</style><div class="h-40 overflow-y-auto border border-[#24143022] font-sans text-[13px] leading-relaxed text-[#241430]">
<div class="sticky top-0 z-10 px-3.5 py-2.5 font-bold sh-tw__header sh-tw__header--left sh-tw__header--shadow" style="background:#A8E6C1;">Chapter 2</div>
<p class="m-0 px-3.5 pt-2.5 text-[#241430]/80">Scroll down inside this box. The header above uses <code>position: sticky</code> with <code>top: 0</code>, so it stays pinned to the top of this scroll container instead of scrolling away with the text.</p>
<p class="m-0 px-3.5 pt-2.5 text-[#241430]/80">It only sticks within its own scrolling ancestor — once that ancestor ends, the header scrolls away too.</p>
<p class="m-0 px-3.5 pt-2.5 text-[#241430]/80">A third paragraph adds enough height to make scrolling necessary.</p>
<p class="m-0 px-3.5 pt-2.5 text-[#241430]/80">A fourth paragraph, for good measure.</p>
<p class="m-0 px-3.5 pt-2.5 text-[#241430]/80">And a fifth, so the sticky behaviour is unmistakable.</p>
</div>
<style>
.sh-tw__header--center { text-align: center; }
.sh-tw__header--shadow { box-shadow: 0 4px 10px rgba(36,20,48,.2); }
</style>export function StickyHeader({
title = "Chapter 2",
bg = "#A8E6C1",
align = "left",
shadow = true,
}) {
const paragraphs = [
"Scroll down inside this box. The header above uses position: sticky with top: 0, so it stays pinned to the top of this scroll container instead of scrolling away with the text.",
"It only sticks within its own scrolling ancestor — once that ancestor ends, the header scrolls away too.",
"A third paragraph adds enough height to make scrolling necessary.",
"A fourth paragraph, for good measure.",
"And a fifth, so the sticky behaviour is unmistakable.",
];
return (
<div style={{ height: 160, overflowY: "auto", border: "1px solid #24143022", fontFamily: "system-ui, sans-serif", fontSize: 13, lineHeight: 1.6, color: "#241430" }}>
<div style={{ position: "sticky", top: 0, zIndex: 2, background: bg, padding: "10px 14px", fontWeight: 700, textAlign: align === "center" ? "center" : "left", boxShadow: shadow ? "0 4px 10px rgba(36,20,48,.2)" : "none" }}>{title}</div>
{paragraphs.map((p, i) => <p key={i} style={{ margin: 0, padding: "10px 14px 0", color: "#241430cc" }}>{p}</p>)}
</div>
);
}