blog filter chips topics categories scrollable navigation grid filter chips topics categories scrollable blog tabs navigation grid blog section with horizontal category filter chips scrollable topic tabs with article grid filterable blog post grid with pill navigation
Blog Topic Filter
Fetch pattern JSON:
curl https://webspire.de/patterns/blog/topic-filter.json topic-filter.html
<section class="ws-blog bg-[var(--ws-blog-bg)] py-20">
<div class="mx-auto max-w-7xl px-6">
<!-- Section heading -->
<div class="mx-auto max-w-2xl text-center">
<p class="text-sm font-semibold uppercase tracking-[0.18em] text-[var(--ws-blog-accent)]">Blog</p>
<h2 class="mt-3 text-balance text-3xl font-bold tracking-tight text-[var(--ws-blog-text)] sm:text-4xl">Latest from the team</h2>
</div>
<!-- Scrollable topic filter strip -->
<div class="relative mt-10">
<!-- Left arrow (shows when scrolled right) -->
<button class="absolute left-0 top-1/2 z-10 hidden -translate-y-1/2 items-center justify-center rounded-full border border-[var(--ws-color-border)] bg-[var(--ws-blog-bg)] p-1.5 shadow-sm sm:flex" aria-label="Scroll left">
<svg class="h-4 w-4 text-[var(--ws-blog-text-soft)]" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5"/></svg>
</button>
<div class="no-scrollbar flex gap-2 overflow-x-auto px-0 sm:px-8" role="tablist" aria-label="Filter by topic">
<button role="tab" aria-selected="true" class="shrink-0 rounded-full bg-[var(--ws-blog-accent)] px-4 py-1.5 text-sm font-medium text-white transition">All</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">AI & Research</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Products</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Company</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Community</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Tutorials</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Case Studies</button>
<button role="tab" aria-selected="false" class="shrink-0 rounded-full border border-[var(--ws-color-border)] px-4 py-1.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Events</button>
</div>
<!-- Right arrow -->
<button class="absolute right-0 top-1/2 z-10 hidden -translate-y-1/2 items-center justify-center rounded-full border border-[var(--ws-color-border)] bg-[var(--ws-blog-bg)] p-1.5 shadow-sm sm:flex" aria-label="Scroll right">
<svg class="h-4 w-4 text-[var(--ws-blog-text-soft)]" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/></svg>
</button>
</div>
<!-- Article grid -->
<div class="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<!-- Card 1 -->
<article class="group flex flex-col overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-blog-card-bg)]">
<a href="#" class="block aspect-video overflow-hidden">
<img src="https://picsum.photos/seed/blog-tf1/800/450" alt="Gemini 2.0 Flash" class="h-full w-full object-cover transition duration-500 group-hover:scale-105">
</a>
<div class="flex flex-1 flex-col p-5">
<p class="text-xs font-semibold uppercase tracking-[0.15em] text-indigo-500">AI & Research</p>
<h3 class="mt-2 text-base font-semibold leading-snug text-[var(--ws-blog-text)] transition group-hover:text-[var(--ws-blog-accent)]">
<a href="#">Gemini 2.0 Flash: faster, more capable, ready for production</a>
</h3>
<p class="mt-2 flex-1 text-sm leading-relaxed text-[var(--ws-blog-text-soft)]">Our most efficient model yet ships with native tool use and multimodal output built in.</p>
<div class="mt-4 flex items-center gap-2.5">
<img src="https://i.pravatar.cc/28?img=11" alt="DeepMind Team" class="h-7 w-7 shrink-0 rounded-full object-cover">
<div class="text-xs text-[var(--ws-blog-text-muted)]">
<span class="font-medium text-[var(--ws-blog-text-soft)]">DeepMind Team</span>
<span aria-hidden="true"> · </span>Mar 26 · 6 min
</div>
</div>
</div>
</article>
<!-- Card 2 -->
<article class="group flex flex-col overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-blog-card-bg)]">
<a href="#" class="block aspect-video overflow-hidden">
<img src="https://picsum.photos/seed/blog-tf2/800/450" alt="Google Search AI" class="h-full w-full object-cover transition duration-500 group-hover:scale-105">
</a>
<div class="flex flex-1 flex-col p-5">
<p class="text-xs font-semibold uppercase tracking-[0.15em] text-emerald-600">Products</p>
<h3 class="mt-2 text-base font-semibold leading-snug text-[var(--ws-blog-text)] transition group-hover:text-[var(--ws-blog-accent)]">
<a href="#">Search's next chapter: understanding context across every query</a>
</h3>
<p class="mt-2 flex-1 text-sm leading-relaxed text-[var(--ws-blog-text-soft)]">How AI Overviews are changing the way a billion people find information every day.</p>
<div class="mt-4 flex items-center gap-2.5">
<img src="https://i.pravatar.cc/28?img=32" alt="Search Team" class="h-7 w-7 shrink-0 rounded-full object-cover">
<div class="text-xs text-[var(--ws-blog-text-muted)]">
<span class="font-medium text-[var(--ws-blog-text-soft)]">Search Team</span>
<span aria-hidden="true"> · </span>Mar 20 · 4 min
</div>
</div>
</div>
</article>
<!-- Card 3 -->
<article class="group flex flex-col overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-blog-card-bg)]">
<a href="#" class="block aspect-video overflow-hidden">
<img src="https://picsum.photos/seed/blog-tf3/800/450" alt="Environmental Report" class="h-full w-full object-cover transition duration-500 group-hover:scale-105">
</a>
<div class="flex flex-1 flex-col p-5">
<p class="text-xs font-semibold uppercase tracking-[0.15em] text-amber-600">Company</p>
<h3 class="mt-2 text-base font-semibold leading-snug text-[var(--ws-blog-text)] transition group-hover:text-[var(--ws-blog-accent)]">
<a href="#">Our 2025 Environmental Report: progress on our climate commitments</a>
</h3>
<p class="mt-2 flex-1 text-sm leading-relaxed text-[var(--ws-blog-text-soft)]">A look at the steps we're taking toward net-zero and 24/7 carbon-free energy worldwide.</p>
<div class="mt-4 flex items-center gap-2.5">
<img src="https://i.pravatar.cc/28?img=25" alt="Sustainability" class="h-7 w-7 shrink-0 rounded-full object-cover">
<div class="text-xs text-[var(--ws-blog-text-muted)]">
<span class="font-medium text-[var(--ws-blog-text-soft)]">Sustainability</span>
<span aria-hidden="true"> · </span>Mar 15 · 5 min
</div>
</div>
</div>
</article>
</div>
<!-- Load more -->
<div class="mt-10 flex justify-center">
<button class="rounded-full border border-[var(--ws-color-border)] px-6 py-2.5 text-sm font-medium text-[var(--ws-blog-text-soft)] transition hover:border-[var(--ws-blog-accent)] hover:text-[var(--ws-blog-accent)]">Load more articles</button>
</div>
</div>
</section>
Details
Responsive Dark Mode Tailwind Only SSR Safe Copy & Paste
Stable Published
blogfilterchipstopicscategoriesscrollablenavigationgrid
Slots
| Name | Required | Description |
|---|---|---|
| heading | No | Optional kicker and section title. |
| filter-strip | Yes | Scrollable chip row with active/inactive pill states and arrow buttons. |
| articles | Yes | 3-column article grid with aspect-video thumbnail, eyebrow, title, description, and author row. |
| load-more | No | Centered outlined pill button for pagination. |
Full blog section with two parts: a scrollable horizontal chip strip (pill buttons, role="tablist", left/right arrow controls for overflow) and a 3-column article card grid below. Each card has an aspect-video thumbnail, colored text eyebrow (not a pill badge), title, description, and author avatar + name + date. Ends with a “Load more articles” pill button. Inspired by Google’s blog category navigation — use for any high-volume blog with 5+ topic areas.