06 / 22
All 22 in 22 Tailwind CSS Headers
- 01Tailwind CSS Command Palette Header with ⌘K Shortcut
- 02Tailwind Docs Header with Version Switcher and Search
- 03Tailwind SaaS App Header with Workspace Switcher
- 04Tailwind E-Commerce Header with Cart, Wishlist and Mega Menu
- 05Tailwind Marketing Header with Sticky CTA on Scroll
- 06Tailwind Article Header with Reading Progress Bar
- 07Tailwind Centered Logo Editorial Header
- 08Tailwind Transparent Header Over Hero Image
- 09Tailwind Header with Breadcrumb Navigation
- 10Tailwind Header with Notification Dropdown and User Menu
- 11Tailwind Header with GitHub Star Button
- 12Tailwind Sticky Header on Scroll with Scroll-Driven Animation
- 13Tailwind Mega Menu Header with CSS Grid
- 14Tailwind Navbar with Centered Links — Three Column Flexbox
- 15Tailwind Full Screen Menu Overlay with Hamburger Toggle
- 16Tailwind Glassmorphism Header with Backdrop Blur
- 17Tailwind Header with Dismissible Announcement Bar
- 18Tailwind Two Row Header with Top Utility Bar
- 19Tailwind Navbar Link Hover Effects — 5 Animated Styles
- 20Tailwind Sidebar Layout with Collapsible Navigation
- 21Tailwind Multi Level Dropdown Menu Header
- 22Tailwind Shrinking Header on Scroll
Tailwind Article Header with Reading Progress Bar
An editorial header with a hairline meter that fills as the reader moves down the piece — one element scaled with scaleX() on a scroll(root) timeline, a single composited layer instead of a scroll handler. New in this port: the article's title docks into the header once the hero scrolls away, on the same timeline.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<div class="thd-06 group/thd06 w-full min-h-svh bg-thd06-bg font-thd06-sans text-thd06-ink antialiased">
<a href="#thd-06-main" class="sr-only focus:not-sr-only focus:absolute focus:left-3 focus:top-3 focus:z-[60] focus:rounded-lg focus:bg-thd06-acc focus:px-4 focus:py-2.5 focus:text-sm focus:font-semibold focus:text-white">Skip to content</a>
<header class="sticky top-0 z-40 border-b border-thd06-line bg-thd06-bg/92 backdrop-blur-md">
<div class="mx-auto flex h-14 max-w-6xl items-center gap-4 px-[clamp(.75rem,3vw,1.5rem)]">
<a href="#thd-06-main" class="whitespace-nowrap font-thd06-serif text-xl font-semibold italic tracking-tight">The Long Field</a>
<div class="relative hidden min-w-0 flex-1 md:block">
<nav aria-label="Sections" class="thd06-navout">
<ul class="flex items-center gap-5 text-sm font-semibold text-thd06-mut">
<li><a href="#thd-06-main" aria-current="page" class="py-2 hover:text-thd06-ink aria-[current=page]:text-thd06-acc">Reporting</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Essays</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Interviews</a></li>
</ul>
</nav>
<p aria-hidden="true" class="thd06-dock pointer-events-none absolute inset-0 flex items-center truncate font-thd06-serif text-[15px] opacity-0"><span class="truncate">The river that learned to run backwards</span></p>
</div>
<div class="ml-auto flex items-center gap-1">
<input id="thd-06-t" type="checkbox" class="peer/t sr-only">
<label for="thd-06-t" class="grid size-11 cursor-pointer place-items-center rounded-full text-thd06-mut hover:bg-thd06-ink/5 hover:text-thd06-ink peer-focus-visible/t:outline-2 peer-focus-visible/t:outline-thd06-acc">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true" class="size-[18px] group-has-[#thd-06-t:checked]/thd06:hidden"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5a8.6 8.6 0 1 0 10.9 10.9Z"/></svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true" class="hidden size-[18px] group-has-[#thd-06-t:checked]/thd06:block"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5"/></svg>
<span class="sr-only">Switch light or dark theme</span>
</label>
<a href="#thd-06-main" class="grid min-h-10 place-items-center rounded-full bg-thd06-ink px-4 text-sm font-bold text-thd06-bg transition-colors hover:bg-thd06-acc">Subscribe</a>
</div>
</div>
<div aria-hidden="true" class="thd06-meter absolute inset-x-0 -bottom-px h-[3px] origin-left bg-thd06-acc"></div>
</header>
<main id="thd-06-main" class="px-[clamp(1rem,4vw,2rem)]">
<article class="mx-auto max-w-[42rem] py-[clamp(2.5rem,8vw,5rem)]">
<p class="text-xs font-bold uppercase tracking-[.18em] text-thd06-acc">Reporting · Climate</p>
<h1 class="mt-3 font-thd06-serif text-[clamp(2.2rem,6.5vw,3.8rem)] font-medium leading-[1.02] tracking-[-.02em] text-balance">The river that learned to run backwards</h1>
<p class="mt-4 font-thd06-serif text-xl italic leading-snug text-thd06-mut">In the Fens, engineers are letting the sea back in — and the farmers are helping.</p>
<div class="mt-6 flex flex-wrap items-center gap-3 border-y border-thd06-line py-3 text-sm">
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-thd06-acc/15 text-xs font-bold text-thd06-acc">JL</span>
<span><strong>Jordan Lee</strong> <span class="text-thd06-mut">· 25 September 2026</span></span>
<span class="ml-auto rounded-full bg-thd06-ink/5 px-2.5 py-1 text-xs font-semibold tabular-nums text-thd06-mut">14 min read</span>
</div>
<div class="mt-8 space-y-5 font-thd06-serif text-[1.18rem] leading-[1.7]">
<p class="first-letter:float-left first-letter:mr-2 first-letter:mt-1 first-letter:font-semibold first-letter:text-[3.6rem] first-letter:leading-[.8] first-letter:text-thd06-acc">At low tide the new channel is a ribbon of silver mud, pocked by the feet of oystercatchers. At high tide it is a river again — only now it flows inland.</p>
<p>For three centuries the drainage boards of eastern England fought to keep salt water out. The pumps still run, but on this stretch of the Ouse they have been switched off on purpose, and the old sea wall has a forty-metre gap cut through it.</p>
<p>Scroll, and watch the header: the meter fills with the page, and the headline docks into the bar once this hero has gone.</p>
<p>“We spent a generation losing an argument with the tide,” says Sam Rivera, who farms the land either side of the breach. “This is the first time it feels like we’re negotiating.”</p>
<p>The saltmarsh that has formed in eighteen months already stores more carbon per hectare than the wheat it replaced, and the flood risk to the village upstream has fallen, not risen.</p>
</div>
<div aria-hidden="true" class="h-[80svh]"></div>
</article>
</main>
</div><div class="thd-06 group/thd06 w-full min-h-svh bg-thd06-bg font-thd06-sans text-thd06-ink antialiased">
<a href="#thd-06-main" class="sr-only focus:not-sr-only focus:absolute focus:left-3 focus:top-3 focus:z-[60] focus:rounded-lg focus:bg-thd06-acc focus:px-4 focus:py-2.5 focus:text-sm focus:font-semibold focus:text-white">Skip to content</a>
<header class="sticky top-0 z-40 border-b border-thd06-line bg-thd06-bg/92 backdrop-blur-md">
<div class="mx-auto flex h-14 max-w-6xl items-center gap-4 px-[clamp(.75rem,3vw,1.5rem)]">
<a href="#thd-06-main" class="whitespace-nowrap font-thd06-serif text-xl font-semibold italic tracking-tight">The Long Field</a>
<div class="relative hidden min-w-0 flex-1 md:block">
<nav aria-label="Sections" class="thd06-navout">
<ul class="flex items-center gap-5 text-sm font-semibold text-thd06-mut">
<li><a href="#thd-06-main" aria-current="page" class="py-2 hover:text-thd06-ink aria-[current=page]:text-thd06-acc">Reporting</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Essays</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Interviews</a></li>
</ul>
</nav>
<p aria-hidden="true" class="thd06-dock pointer-events-none absolute inset-0 flex items-center truncate font-thd06-serif text-[15px] opacity-0"><span class="truncate">The river that learned to run backwards</span></p>
</div>
<div class="ml-auto flex items-center gap-1">
<input id="thd-06-t" type="checkbox" class="peer/t sr-only">
<label for="thd-06-t" class="grid size-11 cursor-pointer place-items-center rounded-full text-thd06-mut hover:bg-thd06-ink/5 hover:text-thd06-ink peer-focus-visible/t:outline-2 peer-focus-visible/t:outline-thd06-acc">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true" class="size-[18px] group-has-[#thd-06-t:checked]/thd06:hidden"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5a8.6 8.6 0 1 0 10.9 10.9Z"/></svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true" class="hidden size-[18px] group-has-[#thd-06-t:checked]/thd06:block"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5"/></svg>
<span class="sr-only">Switch light or dark theme</span>
</label>
<a href="#thd-06-main" class="grid min-h-10 place-items-center rounded-full bg-thd06-ink px-4 text-sm font-bold text-thd06-bg transition-colors hover:bg-thd06-acc">Subscribe</a>
</div>
</div>
<div aria-hidden="true" class="thd06-meter absolute inset-x-0 -bottom-px h-[3px] origin-left bg-thd06-acc"></div>
</header>
<main id="thd-06-main" class="px-[clamp(1rem,4vw,2rem)]">
<article class="mx-auto max-w-[42rem] py-[clamp(2.5rem,8vw,5rem)]">
<p class="text-xs font-bold uppercase tracking-[.18em] text-thd06-acc">Reporting · Climate</p>
<h1 class="mt-3 font-thd06-serif text-[clamp(2.2rem,6.5vw,3.8rem)] font-medium leading-[1.02] tracking-[-.02em] text-balance">The river that learned to run backwards</h1>
<p class="mt-4 font-thd06-serif text-xl italic leading-snug text-thd06-mut">In the Fens, engineers are letting the sea back in — and the farmers are helping.</p>
<div class="mt-6 flex flex-wrap items-center gap-3 border-y border-thd06-line py-3 text-sm">
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-thd06-acc/15 text-xs font-bold text-thd06-acc">JL</span>
<span><strong>Jordan Lee</strong> <span class="text-thd06-mut">· 25 September 2026</span></span>
<span class="ml-auto rounded-full bg-thd06-ink/5 px-2.5 py-1 text-xs font-semibold tabular-nums text-thd06-mut">14 min read</span>
</div>
<div class="mt-8 space-y-5 font-thd06-serif text-[1.18rem] leading-[1.7]">
<p class="first-letter:float-left first-letter:mr-2 first-letter:mt-1 first-letter:font-semibold first-letter:text-[3.6rem] first-letter:leading-[.8] first-letter:text-thd06-acc">At low tide the new channel is a ribbon of silver mud, pocked by the feet of oystercatchers. At high tide it is a river again — only now it flows inland.</p>
<p>For three centuries the drainage boards of eastern England fought to keep salt water out. The pumps still run, but on this stretch of the Ouse they have been switched off on purpose, and the old sea wall has a forty-metre gap cut through it.</p>
<p>Scroll, and watch the header: the meter fills with the page, and the headline docks into the bar once this hero has gone.</p>
<p>“We spent a generation losing an argument with the tide,” says Sam Rivera, who farms the land either side of the breach. “This is the first time it feels like we’re negotiating.”</p>
<p>The saltmarsh that has formed in eighteen months already stores more carbon per hectare than the wheat it replaced, and the flood risk to the village upstream has fallen, not risen.</p>
</div>
<div aria-hidden="true" class="h-[80svh]"></div>
</article>
</main>
</div>@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400..700;1,6..72,400..600&family=Source+Sans+3:wght@400..700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400..700;1,6..72,400..600&family=Source+Sans+3:wght@400..700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The meter's
transform:scaleX()keyframes stayed CSS inside@utility thd06-meter; its size, colour and origin are utilities. - The byline, kicker and drop cap moved to utilities — the drop cap is five
first-letter:classes. - UI upgrade from the CSS original: the article headline now docks into the header as the nav slides away, so readers always know what they are reading — on the same compositor-only timeline.
- UI upgrade: the meter is hidden rather than frozen at 0% where scroll timelines are unsupported, the headline moved to Newsreader's optical sizes, and the byline gained an avatar and reading-time chip.
Here's a working CSS 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: Tailwind Article Header with Reading Progress Bar
Source: https://codefronts.com/layouts/tailwind-css-headers/tailwind-article-header-reading-progress-bar/
An editorial header with a hairline meter that fills as the reader moves down the piece — one element scaled with scaleX() on a scroll(root) timeline, a single composited layer instead of a scroll handler. New in this port: the article's title docks into the header once the hero scrolls away, on the same timeline.
## HTML
```html
<div class="thd-06 group/thd06 w-full min-h-svh bg-thd06-bg font-thd06-sans text-thd06-ink antialiased">
<a href="#thd-06-main" class="sr-only focus:not-sr-only focus:absolute focus:left-3 focus:top-3 focus:z-[60] focus:rounded-lg focus:bg-thd06-acc focus:px-4 focus:py-2.5 focus:text-sm focus:font-semibold focus:text-white">Skip to content</a>
<header class="sticky top-0 z-40 border-b border-thd06-line bg-thd06-bg/92 backdrop-blur-md">
<div class="mx-auto flex h-14 max-w-6xl items-center gap-4 px-[clamp(.75rem,3vw,1.5rem)]">
<a href="#thd-06-main" class="whitespace-nowrap font-thd06-serif text-xl font-semibold italic tracking-tight">The Long Field</a>
<div class="relative hidden min-w-0 flex-1 md:block">
<nav aria-label="Sections" class="thd06-navout">
<ul class="flex items-center gap-5 text-sm font-semibold text-thd06-mut">
<li><a href="#thd-06-main" aria-current="page" class="py-2 hover:text-thd06-ink aria-[current=page]:text-thd06-acc">Reporting</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Essays</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Interviews</a></li>
</ul>
</nav>
<p aria-hidden="true" class="thd06-dock pointer-events-none absolute inset-0 flex items-center truncate font-thd06-serif text-[15px] opacity-0"><span class="truncate">The river that learned to run backwards</span></p>
</div>
<div class="ml-auto flex items-center gap-1">
<input id="thd-06-t" type="checkbox" class="peer/t sr-only">
<label for="thd-06-t" class="grid size-11 cursor-pointer place-items-center rounded-full text-thd06-mut hover:bg-thd06-ink/5 hover:text-thd06-ink peer-focus-visible/t:outline-2 peer-focus-visible/t:outline-thd06-acc">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true" class="size-[18px] group-has-[#thd-06-t:checked]/thd06:hidden"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5a8.6 8.6 0 1 0 10.9 10.9Z"/></svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true" class="hidden size-[18px] group-has-[#thd-06-t:checked]/thd06:block"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5"/></svg>
<span class="sr-only">Switch light or dark theme</span>
</label>
<a href="#thd-06-main" class="grid min-h-10 place-items-center rounded-full bg-thd06-ink px-4 text-sm font-bold text-thd06-bg transition-colors hover:bg-thd06-acc">Subscribe</a>
</div>
</div>
<div aria-hidden="true" class="thd06-meter absolute inset-x-0 -bottom-px h-[3px] origin-left bg-thd06-acc"></div>
</header>
<main id="thd-06-main" class="px-[clamp(1rem,4vw,2rem)]">
<article class="mx-auto max-w-[42rem] py-[clamp(2.5rem,8vw,5rem)]">
<p class="text-xs font-bold uppercase tracking-[.18em] text-thd06-acc">Reporting · Climate</p>
<h1 class="mt-3 font-thd06-serif text-[clamp(2.2rem,6.5vw,3.8rem)] font-medium leading-[1.02] tracking-[-.02em] text-balance">The river that learned to run backwards</h1>
<p class="mt-4 font-thd06-serif text-xl italic leading-snug text-thd06-mut">In the Fens, engineers are letting the sea back in — and the farmers are helping.</p>
<div class="mt-6 flex flex-wrap items-center gap-3 border-y border-thd06-line py-3 text-sm">
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-thd06-acc/15 text-xs font-bold text-thd06-acc">JL</span>
<span><strong>Jordan Lee</strong> <span class="text-thd06-mut">· 25 September 2026</span></span>
<span class="ml-auto rounded-full bg-thd06-ink/5 px-2.5 py-1 text-xs font-semibold tabular-nums text-thd06-mut">14 min read</span>
</div>
<div class="mt-8 space-y-5 font-thd06-serif text-[1.18rem] leading-[1.7]">
<p class="first-letter:float-left first-letter:mr-2 first-letter:mt-1 first-letter:font-semibold first-letter:text-[3.6rem] first-letter:leading-[.8] first-letter:text-thd06-acc">At low tide the new channel is a ribbon of silver mud, pocked by the feet of oystercatchers. At high tide it is a river again — only now it flows inland.</p>
<p>For three centuries the drainage boards of eastern England fought to keep salt water out. The pumps still run, but on this stretch of the Ouse they have been switched off on purpose, and the old sea wall has a forty-metre gap cut through it.</p>
<p>Scroll, and watch the header: the meter fills with the page, and the headline docks into the bar once this hero has gone.</p>
<p>“We spent a generation losing an argument with the tide,” says Sam Rivera, who farms the land either side of the breach. “This is the first time it feels like we’re negotiating.”</p>
<p>The saltmarsh that has formed in eighteen months already stores more carbon per hectare than the wheat it replaced, and the flood risk to the village upstream has fallen, not risen.</p>
</div>
<div aria-hidden="true" class="h-[80svh]"></div>
</article>
</main>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}
```Here's a working CSS 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: Tailwind Article Header with Reading Progress Bar
Source: https://codefronts.com/layouts/tailwind-css-headers/tailwind-article-header-reading-progress-bar/
An editorial header with a hairline meter that fills as the reader moves down the piece — one element scaled with scaleX() on a scroll(root) timeline, a single composited layer instead of a scroll handler. New in this port: the article's title docks into the header once the hero scrolls away, on the same timeline.
## HTML
```html
<div class="thd-06 group/thd06 w-full min-h-svh bg-thd06-bg font-thd06-sans text-thd06-ink antialiased">
<a href="#thd-06-main" class="sr-only focus:not-sr-only focus:absolute focus:left-3 focus:top-3 focus:z-[60] focus:rounded-lg focus:bg-thd06-acc focus:px-4 focus:py-2.5 focus:text-sm focus:font-semibold focus:text-white">Skip to content</a>
<header class="sticky top-0 z-40 border-b border-thd06-line bg-thd06-bg/92 backdrop-blur-md">
<div class="mx-auto flex h-14 max-w-6xl items-center gap-4 px-[clamp(.75rem,3vw,1.5rem)]">
<a href="#thd-06-main" class="whitespace-nowrap font-thd06-serif text-xl font-semibold italic tracking-tight">The Long Field</a>
<div class="relative hidden min-w-0 flex-1 md:block">
<nav aria-label="Sections" class="thd06-navout">
<ul class="flex items-center gap-5 text-sm font-semibold text-thd06-mut">
<li><a href="#thd-06-main" aria-current="page" class="py-2 hover:text-thd06-ink aria-[current=page]:text-thd06-acc">Reporting</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Essays</a></li>
<li><a href="#thd-06-main" class="py-2 hover:text-thd06-ink">Interviews</a></li>
</ul>
</nav>
<p aria-hidden="true" class="thd06-dock pointer-events-none absolute inset-0 flex items-center truncate font-thd06-serif text-[15px] opacity-0"><span class="truncate">The river that learned to run backwards</span></p>
</div>
<div class="ml-auto flex items-center gap-1">
<input id="thd-06-t" type="checkbox" class="peer/t sr-only">
<label for="thd-06-t" class="grid size-11 cursor-pointer place-items-center rounded-full text-thd06-mut hover:bg-thd06-ink/5 hover:text-thd06-ink peer-focus-visible/t:outline-2 peer-focus-visible/t:outline-thd06-acc">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true" class="size-[18px] group-has-[#thd-06-t:checked]/thd06:hidden"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5a8.6 8.6 0 1 0 10.9 10.9Z"/></svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true" class="hidden size-[18px] group-has-[#thd-06-t:checked]/thd06:block"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5"/></svg>
<span class="sr-only">Switch light or dark theme</span>
</label>
<a href="#thd-06-main" class="grid min-h-10 place-items-center rounded-full bg-thd06-ink px-4 text-sm font-bold text-thd06-bg transition-colors hover:bg-thd06-acc">Subscribe</a>
</div>
</div>
<div aria-hidden="true" class="thd06-meter absolute inset-x-0 -bottom-px h-[3px] origin-left bg-thd06-acc"></div>
</header>
<main id="thd-06-main" class="px-[clamp(1rem,4vw,2rem)]">
<article class="mx-auto max-w-[42rem] py-[clamp(2.5rem,8vw,5rem)]">
<p class="text-xs font-bold uppercase tracking-[.18em] text-thd06-acc">Reporting · Climate</p>
<h1 class="mt-3 font-thd06-serif text-[clamp(2.2rem,6.5vw,3.8rem)] font-medium leading-[1.02] tracking-[-.02em] text-balance">The river that learned to run backwards</h1>
<p class="mt-4 font-thd06-serif text-xl italic leading-snug text-thd06-mut">In the Fens, engineers are letting the sea back in — and the farmers are helping.</p>
<div class="mt-6 flex flex-wrap items-center gap-3 border-y border-thd06-line py-3 text-sm">
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-thd06-acc/15 text-xs font-bold text-thd06-acc">JL</span>
<span><strong>Jordan Lee</strong> <span class="text-thd06-mut">· 25 September 2026</span></span>
<span class="ml-auto rounded-full bg-thd06-ink/5 px-2.5 py-1 text-xs font-semibold tabular-nums text-thd06-mut">14 min read</span>
</div>
<div class="mt-8 space-y-5 font-thd06-serif text-[1.18rem] leading-[1.7]">
<p class="first-letter:float-left first-letter:mr-2 first-letter:mt-1 first-letter:font-semibold first-letter:text-[3.6rem] first-letter:leading-[.8] first-letter:text-thd06-acc">At low tide the new channel is a ribbon of silver mud, pocked by the feet of oystercatchers. At high tide it is a river again — only now it flows inland.</p>
<p>For three centuries the drainage boards of eastern England fought to keep salt water out. The pumps still run, but on this stretch of the Ouse they have been switched off on purpose, and the old sea wall has a forty-metre gap cut through it.</p>
<p>Scroll, and watch the header: the meter fills with the page, and the headline docks into the bar once this hero has gone.</p>
<p>“We spent a generation losing an argument with the tide,” says Sam Rivera, who farms the land either side of the breach. “This is the first time it feels like we’re negotiating.”</p>
<p>The saltmarsh that has formed in eighteen months already stores more carbon per hectare than the wheat it replaced, and the flood risk to the village upstream has fallen, not risen.</p>
</div>
<div aria-hidden="true" class="h-[80svh]"></div>
</article>
</main>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme inline {
--color-thd06-bg: var(--thd06-bg);
--color-thd06-ink: var(--thd06-ink);
--color-thd06-mut: var(--thd06-mut);
--color-thd06-line: var(--thd06-line);
--color-thd06-acc: var(--thd06-acc);
}
@theme {
--font-thd06-serif: 'Newsreader', Georgia, serif;
--font-thd06-sans: 'Source Sans 3', system-ui, sans-serif;
}
.thd-06 {
width: 100%;
min-height: 100svh;
--thd06-bg: #fbf9f4;
--thd06-ink: #1d1b16;
--thd06-mut: #5f5a50;
--thd06-line: #e7e2d6;
--thd06-acc: #b4441c;
}
.thd-06:has(#thd-06-t:checked) {
--thd06-bg: #14130f;
--thd06-ink: #f0ebe0;
--thd06-mut: #aca596;
--thd06-line: #2b2922;
--thd06-acc: #f08a5d;
}
@utility thd06-meter {
display: none;
@supports (animation-timeline: scroll()) {
display: block;
animation: thd06-fill linear both;
animation-timeline: scroll(root);
}
}
@utility thd06-dock {
@supports (animation-timeline: scroll()) {
animation: thd06-in linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@utility thd06-navout {
@supports (animation-timeline: scroll()) {
animation: thd06-out linear both;
animation-timeline: scroll(root);
animation-range: 320px 420px;
}
}
@keyframes thd06-fill {
from {
scale: 0 1;
}
to {
scale: 1 1;
}
}
@keyframes thd06-in {
from {
opacity: 0;
translate: 0 0.5rem;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes thd06-out {
to {
opacity: 0;
translate: 0 -0.5rem;
visibility: hidden;
}
}
```How this works
Transform, not width. The meter is h-[3px] w-full origin-left; its keyframes animate scale from 0 to 1. Width animation would re-lay the header out on every frame; scale is compositor-only.
Two timelines, one scroller. The docked title and the meter both read scroll(root), so they never disagree. Without support the meter is display:none (a frozen 0% bar would be a lie) and the nav stays visible.
Make it yours
- Meter colour:
bg-thd06-acc; addshadow-[0_0_12px_var(--color-thd06-acc)]for a glow. - Measure the article, not the page: switch to a
view()timeline on the<article>. - Change when the title docks via
animation-rangeinthd06-dock.
Gotchas — read before shipping
scroll(root)measures the whole page, so a long footer makes the meter finish late — useview()if that matters.- The docked title is
aria-hidden; the real<h1>stays the accessible heading.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 115+ | 26+ | 128+ (no meter) | 115+ |
Meter and docking need scroll-driven animations; elsewhere the header is static and complete.