09 / 31
All 31 in 31 Tailwind CSS Hero Sections
- 01Centered Hero Section in Tailwind CSS with Serif Headline & Dual CTA
- 02Tailwind CSS Split Hero Section with Dashboard Mockup
- 03Tailwind CSS Animated Gradient Mesh Hero Section
- 04Glassmorphism Hero Section in Tailwind CSS with Aurora Background
- 05Tailwind CSS Developer Hero Section with Terminal Window
- 06Tailwind Hero Section with Animated Geometric Shapes Background
- 07Tailwind CSS Hero Section with Star Rating & Logo Cloud
- 08Tailwind CSS Animated Headline Hero with Staggered Word Reveal
- 09Before and After Image Comparison Slider Hero in Tailwind CSS
- 10Full-Screen Tailwind Hero with Animated Scroll Down Indicator
- 11Magazine Cover Hero Section in Tailwind CSS
- 12Asymmetric Typography Hero Section in Tailwind CSS
- 13Tailwind CSS Hero Section with Oversized Background Number
- 14Infinite Scrolling Marquee Hero Section in Tailwind CSS
- 15Tailwind CSS Hero Section with Image & Overlay Card
- 16Brutalist Hero Section in Tailwind CSS
- 17Vertical Text Hero Section in Tailwind CSS (Japanese Writing Mode)
- 18Kinetic Typography Hero in Tailwind CSS with Animated Letters
- 19Bento Grid Hero Section in Tailwind CSS
- 20Animated Gradient Text Hero Section in Tailwind CSS
- 21Tailwind CSS Product Hero with Floating Feature Badges
- 22Tailwind CSS Hero Section with Pricing Card
- 23Mobile App Landing Page Hero in Tailwind CSS with App Store Buttons
- 24Neon Glow Hero Section in Tailwind CSS (Cyberpunk)
- 25Parallax Mountain Landscape Hero in Tailwind CSS
- 26Isometric Illustration Hero Section in Tailwind CSS
- 273D Card Stack Hero in Tailwind CSS with Hover Fan-Out
- 28Animated Starfield Background Hero in Tailwind CSS
- 29Glitch Text Effect Hero Section in Tailwind CSS
- 30Animated Conic Gradient Ring Hero in Tailwind CSS
- 31Mouse-Tracking Liquid Glass Lens Hero in Tailwind CSS
Before and After Image Comparison Slider Hero in Tailwind CSS
A native <input type=range> drives a single CSS custom property that wipes a clip-path between a scorched before landscape and a restored after one. Keyboard-accessible with a visible focus ring on the handle. Burnt orange, petrol blue and cream.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="ths-09 grid w-full min-h-svh items-center gap-10 bg-ths09-bg px-5 py-12 font-ths09-sans text-ths09-ink sm:px-10 lg:grid-cols-[1.1fr_1fr] lg:gap-14" aria-label="Before and after comparison hero">
<div data-ths09-stage style="--ths09-pos:50%" class="relative h-[min(68svh,560px)] min-h-[340px] overflow-hidden rounded-2xl border-4 border-ths09-ink shadow-[8px_8px_0_var(--color-ths09-rust)]">
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-sky via-ths09-meadow to-ths09-field">
<span class="absolute top-[16%] left-[70%] size-16 rounded-full bg-ths09-sun shadow-[0_0_60px_var(--color-ths09-sun)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-pine/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-pine [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute right-4 bottom-4 bg-ths09-ink px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">AFTER</span>
</div>
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-rust via-ths09-dune to-ths09-sand [clip-path:inset(0_calc(100%_-_var(--ths09-pos))_0_0)]">
<span class="absolute top-[14%] left-[24%] size-14 rounded-full bg-ths09-bg shadow-[0_0_30px_rgb(247_236_217/0.5)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-clay/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-clay [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute bottom-4 left-4 bg-ths09-clay px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">BEFORE</span>
</div>
<input type="range" min="0" max="100" value="50" aria-label="Compare before and after" aria-valuetext="50 percent restored" class="peer absolute inset-0 z-30 m-0 size-full cursor-ew-resize opacity-0">
<span aria-hidden="true" class="pointer-events-none absolute inset-y-0 left-(--ths09-pos) z-20 w-[3px] -translate-x-1/2 bg-ths09-bg shadow-[0_0_0_1px_var(--color-ths09-ink)] peer-focus-visible:[&>span]:ring-4 peer-focus-visible:[&>span]:ring-ths09-rust/60">
<span class="absolute top-1/2 left-1/2 grid size-11 -translate-1/2 place-items-center rounded-full border-2 border-ths09-ink bg-ths09-bg text-sm font-bold text-ths09-ink shadow-lg">⇆</span>
</span>
</div>
<div class="max-w-lg">
<span class="font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-rust">DRAG TO COMPARE</span>
<h1 class="mt-4 font-ths09-serif text-[clamp(32px,4.6vw,52px)] leading-[1.06] font-bold tracking-[-0.01em] text-balance">Restoration, <em class="text-ths09-rust">one acre at a time.</em></h1>
<p class="mt-5 text-base/relaxed text-ths09-body">We turn dust to forest in 18 months — proven on three continents.</p>
<dl class="mt-7 grid grid-cols-3 gap-4 border-y border-ths09-ink/15 py-5">
<div><dt class="text-xs text-ths09-body">Acres</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">41k</dd></div>
<div><dt class="text-xs text-ths09-body">Trees</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">9.2M</dd></div>
<div><dt class="text-xs text-ths09-body">Survival</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">87%</dd></div>
</dl>
<a href="#donate" class="mt-8 inline-flex min-h-12 items-center rounded-md bg-ths09-rust px-6 text-sm font-bold text-ths09-bg transition hover:bg-ths09-clay focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ths09-rust motion-reduce:transition-none">Plant a tree →</a>
</div>
</section><section class="ths-09 grid w-full min-h-svh items-center gap-10 bg-ths09-bg px-5 py-12 font-ths09-sans text-ths09-ink sm:px-10 lg:grid-cols-[1.1fr_1fr] lg:gap-14" aria-label="Before and after comparison hero">
<div data-ths09-stage style="--ths09-pos:50%" class="relative h-[min(68svh,560px)] min-h-[340px] overflow-hidden rounded-2xl border-4 border-ths09-ink shadow-[8px_8px_0_var(--color-ths09-rust)]">
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-sky via-ths09-meadow to-ths09-field">
<span class="absolute top-[16%] left-[70%] size-16 rounded-full bg-ths09-sun shadow-[0_0_60px_var(--color-ths09-sun)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-pine/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-pine [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute right-4 bottom-4 bg-ths09-ink px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">AFTER</span>
</div>
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-rust via-ths09-dune to-ths09-sand [clip-path:inset(0_calc(100%_-_var(--ths09-pos))_0_0)]">
<span class="absolute top-[14%] left-[24%] size-14 rounded-full bg-ths09-bg shadow-[0_0_30px_rgb(247_236_217/0.5)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-clay/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-clay [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute bottom-4 left-4 bg-ths09-clay px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">BEFORE</span>
</div>
<input type="range" min="0" max="100" value="50" aria-label="Compare before and after" aria-valuetext="50 percent restored" class="peer absolute inset-0 z-30 m-0 size-full cursor-ew-resize opacity-0">
<span aria-hidden="true" class="pointer-events-none absolute inset-y-0 left-(--ths09-pos) z-20 w-[3px] -translate-x-1/2 bg-ths09-bg shadow-[0_0_0_1px_var(--color-ths09-ink)] peer-focus-visible:[&>span]:ring-4 peer-focus-visible:[&>span]:ring-ths09-rust/60">
<span class="absolute top-1/2 left-1/2 grid size-11 -translate-1/2 place-items-center rounded-full border-2 border-ths09-ink bg-ths09-bg text-sm font-bold text-ths09-ink shadow-lg">⇆</span>
</span>
</div>
<div class="max-w-lg">
<span class="font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-rust">DRAG TO COMPARE</span>
<h1 class="mt-4 font-ths09-serif text-[clamp(32px,4.6vw,52px)] leading-[1.06] font-bold tracking-[-0.01em] text-balance">Restoration, <em class="text-ths09-rust">one acre at a time.</em></h1>
<p class="mt-5 text-base/relaxed text-ths09-body">We turn dust to forest in 18 months — proven on three continents.</p>
<dl class="mt-7 grid grid-cols-3 gap-4 border-y border-ths09-ink/15 py-5">
<div><dt class="text-xs text-ths09-body">Acres</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">41k</dd></div>
<div><dt class="text-xs text-ths09-body">Trees</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">9.2M</dd></div>
<div><dt class="text-xs text-ths09-body">Survival</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">87%</dd></div>
</dl>
<a href="#donate" class="mt-8 inline-flex min-h-12 items-center rounded-md bg-ths09-rust px-6 text-sm font-bold text-ths09-bg transition hover:bg-ths09-clay focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ths09-rust motion-reduce:transition-none">Plant a tree →</a>
</div>
</section>@import "tailwindcss";
@theme {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}@import "tailwindcss";
@theme {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}(() => {
document.querySelectorAll('[data-ths09-stage]').forEach((stage) => {
if (stage.dataset.wired) return;
stage.dataset.wired = '1';
const range = stage.querySelector('input[type="range"]');
const update = () => {
stage.style.setProperty('--ths09-pos', range.value + '%');
range.setAttribute('aria-valuetext', range.value + ' percent restored');
};
range.addEventListener('input', update);
update();
});
})();(() => {
document.querySelectorAll('[data-ths09-stage]').forEach((stage) => {
if (stage.dataset.wired) return;
stage.dataset.wired = '1';
const range = stage.querySelector('input[type="range"]');
const update = () => {
stage.style.setProperty('--ths09-pos', range.value + '%');
range.setAttribute('aria-valuetext', range.value + ' percent restored');
};
range.addEventListener('input', update);
update();
});
})();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 {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}@import "tailwindcss";
@theme {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}<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=Playfair+Display:ital,wght@0,700;1,700&family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@600;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=Playfair+Display:ital,wght@0,700;1,700&family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Three
getElementByIdlookups and an inlineclipPathstring became one custom property; the script no longer knows how the wipe is drawn. - The repeating-gradient mountains became
clip-pathpolygons with a near and far ridge, giving the scene depth. - UI upgrade from the CSS original: the invisible range had no focus indicator;
peer-focus-visible:now rings the handle, and the handle grew to 44px. - UI upgrade: stage height is
min(68svh,560px)so it no longer collapses to 360px on stacked layouts; proof stats and a Drag to compare cue added; ids replaced by a data hook so multiple sliders can coexist.
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: Before and After Image Comparison Slider Hero in Tailwind CSS
Source: https://codefronts.com/layouts/tailwind-css-hero-sections/tailwind-before-after-image-comparison-slider-hero/
A native <input type=range> drives a single CSS custom property that wipes a clip-path between a scorched before landscape and a restored after one. Keyboard-accessible with a visible focus ring on the handle. Burnt orange, petrol blue and cream.
## HTML
```html
<section class="ths-09 grid w-full min-h-svh items-center gap-10 bg-ths09-bg px-5 py-12 font-ths09-sans text-ths09-ink sm:px-10 lg:grid-cols-[1.1fr_1fr] lg:gap-14" aria-label="Before and after comparison hero">
<div data-ths09-stage style="--ths09-pos:50%" class="relative h-[min(68svh,560px)] min-h-[340px] overflow-hidden rounded-2xl border-4 border-ths09-ink shadow-[8px_8px_0_var(--color-ths09-rust)]">
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-sky via-ths09-meadow to-ths09-field">
<span class="absolute top-[16%] left-[70%] size-16 rounded-full bg-ths09-sun shadow-[0_0_60px_var(--color-ths09-sun)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-pine/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-pine [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute right-4 bottom-4 bg-ths09-ink px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">AFTER</span>
</div>
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-rust via-ths09-dune to-ths09-sand [clip-path:inset(0_calc(100%_-_var(--ths09-pos))_0_0)]">
<span class="absolute top-[14%] left-[24%] size-14 rounded-full bg-ths09-bg shadow-[0_0_30px_rgb(247_236_217/0.5)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-clay/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-clay [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute bottom-4 left-4 bg-ths09-clay px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">BEFORE</span>
</div>
<input type="range" min="0" max="100" value="50" aria-label="Compare before and after" aria-valuetext="50 percent restored" class="peer absolute inset-0 z-30 m-0 size-full cursor-ew-resize opacity-0">
<span aria-hidden="true" class="pointer-events-none absolute inset-y-0 left-(--ths09-pos) z-20 w-[3px] -translate-x-1/2 bg-ths09-bg shadow-[0_0_0_1px_var(--color-ths09-ink)] peer-focus-visible:[&>span]:ring-4 peer-focus-visible:[&>span]:ring-ths09-rust/60">
<span class="absolute top-1/2 left-1/2 grid size-11 -translate-1/2 place-items-center rounded-full border-2 border-ths09-ink bg-ths09-bg text-sm font-bold text-ths09-ink shadow-lg">⇆</span>
</span>
</div>
<div class="max-w-lg">
<span class="font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-rust">DRAG TO COMPARE</span>
<h1 class="mt-4 font-ths09-serif text-[clamp(32px,4.6vw,52px)] leading-[1.06] font-bold tracking-[-0.01em] text-balance">Restoration, <em class="text-ths09-rust">one acre at a time.</em></h1>
<p class="mt-5 text-base/relaxed text-ths09-body">We turn dust to forest in 18 months — proven on three continents.</p>
<dl class="mt-7 grid grid-cols-3 gap-4 border-y border-ths09-ink/15 py-5">
<div><dt class="text-xs text-ths09-body">Acres</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">41k</dd></div>
<div><dt class="text-xs text-ths09-body">Trees</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">9.2M</dd></div>
<div><dt class="text-xs text-ths09-body">Survival</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">87%</dd></div>
</dl>
<a href="#donate" class="mt-8 inline-flex min-h-12 items-center rounded-md bg-ths09-rust px-6 text-sm font-bold text-ths09-bg transition hover:bg-ths09-clay focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ths09-rust motion-reduce:transition-none">Plant a tree →</a>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('[data-ths09-stage]').forEach((stage) => {
if (stage.dataset.wired) return;
stage.dataset.wired = '1';
const range = stage.querySelector('input[type="range"]');
const update = () => {
stage.style.setProperty('--ths09-pos', range.value + '%');
range.setAttribute('aria-valuetext', range.value + ' percent restored');
};
range.addEventListener('input', update);
update();
});
})();
```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: Before and After Image Comparison Slider Hero in Tailwind CSS
Source: https://codefronts.com/layouts/tailwind-css-hero-sections/tailwind-before-after-image-comparison-slider-hero/
A native <input type=range> drives a single CSS custom property that wipes a clip-path between a scorched before landscape and a restored after one. Keyboard-accessible with a visible focus ring on the handle. Burnt orange, petrol blue and cream.
## HTML
```html
<section class="ths-09 grid w-full min-h-svh items-center gap-10 bg-ths09-bg px-5 py-12 font-ths09-sans text-ths09-ink sm:px-10 lg:grid-cols-[1.1fr_1fr] lg:gap-14" aria-label="Before and after comparison hero">
<div data-ths09-stage style="--ths09-pos:50%" class="relative h-[min(68svh,560px)] min-h-[340px] overflow-hidden rounded-2xl border-4 border-ths09-ink shadow-[8px_8px_0_var(--color-ths09-rust)]">
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-sky via-ths09-meadow to-ths09-field">
<span class="absolute top-[16%] left-[70%] size-16 rounded-full bg-ths09-sun shadow-[0_0_60px_var(--color-ths09-sun)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-pine/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-pine [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute right-4 bottom-4 bg-ths09-ink px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">AFTER</span>
</div>
<div aria-hidden="true" class="absolute inset-0 bg-linear-to-b from-ths09-rust via-ths09-dune to-ths09-sand [clip-path:inset(0_calc(100%_-_var(--ths09-pos))_0_0)]">
<span class="absolute top-[14%] left-[24%] size-14 rounded-full bg-ths09-bg shadow-[0_0_30px_rgb(247_236_217/0.5)]"></span>
<span class="absolute inset-x-0 bottom-[22%] h-[38%] bg-ths09-clay/70 [clip-path:polygon(0_70%,14%_30%,28%_62%,44%_18%,60%_58%,76%_26%,90%_52%,100%_34%,100%_100%,0_100%)]"></span>
<span class="absolute inset-x-0 bottom-0 h-[36%] bg-ths09-clay [clip-path:polygon(0_40%,18%_10%,34%_46%,52%_0,70%_38%,86%_14%,100%_30%,100%_100%,0_100%)]"></span>
<span class="absolute bottom-4 left-4 bg-ths09-clay px-2.5 py-1 font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-bg">BEFORE</span>
</div>
<input type="range" min="0" max="100" value="50" aria-label="Compare before and after" aria-valuetext="50 percent restored" class="peer absolute inset-0 z-30 m-0 size-full cursor-ew-resize opacity-0">
<span aria-hidden="true" class="pointer-events-none absolute inset-y-0 left-(--ths09-pos) z-20 w-[3px] -translate-x-1/2 bg-ths09-bg shadow-[0_0_0_1px_var(--color-ths09-ink)] peer-focus-visible:[&>span]:ring-4 peer-focus-visible:[&>span]:ring-ths09-rust/60">
<span class="absolute top-1/2 left-1/2 grid size-11 -translate-1/2 place-items-center rounded-full border-2 border-ths09-ink bg-ths09-bg text-sm font-bold text-ths09-ink shadow-lg">⇆</span>
</span>
</div>
<div class="max-w-lg">
<span class="font-ths09-mono text-[11px] font-bold tracking-[0.18em] text-ths09-rust">DRAG TO COMPARE</span>
<h1 class="mt-4 font-ths09-serif text-[clamp(32px,4.6vw,52px)] leading-[1.06] font-bold tracking-[-0.01em] text-balance">Restoration, <em class="text-ths09-rust">one acre at a time.</em></h1>
<p class="mt-5 text-base/relaxed text-ths09-body">We turn dust to forest in 18 months — proven on three continents.</p>
<dl class="mt-7 grid grid-cols-3 gap-4 border-y border-ths09-ink/15 py-5">
<div><dt class="text-xs text-ths09-body">Acres</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">41k</dd></div>
<div><dt class="text-xs text-ths09-body">Trees</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">9.2M</dd></div>
<div><dt class="text-xs text-ths09-body">Survival</dt><dd class="mt-1 font-ths09-serif text-2xl font-bold">87%</dd></div>
</dl>
<a href="#donate" class="mt-8 inline-flex min-h-12 items-center rounded-md bg-ths09-rust px-6 text-sm font-bold text-ths09-bg transition hover:bg-ths09-clay focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ths09-rust motion-reduce:transition-none">Plant a tree →</a>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ths09-bg: #f7ecd9;
--color-ths09-bg: oklch(0.947 0.028 81.5);
--color-ths09-ink: #2c4a4f;
--color-ths09-ink: oklch(0.388 0.037 209.1);
--color-ths09-body: #3f5e4c;
--color-ths09-body: oklch(0.452 0.047 158.4);
--color-ths09-rust: #b5501f;
--color-ths09-rust: oklch(0.553 0.144 43.3);
--color-ths09-clay: #8a3614;
--color-ths09-clay: oklch(0.445 0.123 40.3);
--color-ths09-dune: #e8a36e;
--color-ths09-dune: oklch(0.771 0.107 57.9);
--color-ths09-sand: #f0b87a;
--color-ths09-sand: oklch(0.82 0.102 68.4);
--color-ths09-sky: #87b8a8;
--color-ths09-sky: oklch(0.743 0.057 172.5);
--color-ths09-meadow: #b9c08f;
--color-ths09-meadow: oklch(0.79 0.067 114.5);
--color-ths09-field: #d6c69a;
--color-ths09-field: oklch(0.829 0.061 90.4);
--color-ths09-pine: #3f6450;
--color-ths09-pine: oklch(0.469 0.054 159.8);
--color-ths09-sun: #f7d875;
--color-ths09-sun: oklch(0.888 0.124 92.4);
--font-ths09-serif: 'Playfair Display', Georgia, serif;
--font-ths09-sans: 'Geist', system-ui, sans-serif;
--font-ths09-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ths-09 {
width: 100%;
min-height: 100svh;
}
[data-ths09-stage] {
--ths09-pos: 50%;
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('[data-ths09-stage]').forEach((stage) => {
if (stage.dataset.wired) return;
stage.dataset.wired = '1';
const range = stage.querySelector('input[type="range"]');
const update = () => {
stage.style.setProperty('--ths09-pos', range.value + '%');
range.setAttribute('aria-valuetext', range.value + ' percent restored');
};
range.addEventListener('input', update);
update();
});
})();
```How this works
One variable, two consumers. The script writes --ths09-pos on the stage and nothing else. The before layer clips itself with calc(100% - var(--ths09-pos)), the handle moves with left-(--ths09-pos). No inline clipPath strings, no class toggles.
Accessible by construction. The range input covers the stage at opacity-0 so drag, click, arrow keys and screen-reader stepping all work natively. Because it is a peer placed before the handle, peer-focus-visible:ring-4 lights the handle when the slider has keyboard focus.
Make it yours
- Real photos: replace each scene's contents with an
<img class=absolute inset-0 size-full object-cover>. - Vertical wipe: swap to
inset(calc(100%-var(--ths09-pos))_0_0_0)andtop-(--ths09-pos). - Start position: change
valueand the inline--ths09-postogether.
Gotchas — read before shipping
- Subtraction in
calc()needs spaces; inside a class write them as underscores:calc(100%_-_var(--ths09-pos)). opacity-0keeps the input focusable;invisibleorhiddenwould remove it from the tab order.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
clip-path: inset() and range inputs are universal.