portfolio filter tabs projects gallery categories portfolio filter tabs categories projects gallery filterable portfolio with category tabs project gallery with filter buttons
Portfolio With Filter
Fetch pattern JSON:
curl https://webspire.de/patterns/portfolio/with-filter.json with-filter.html
<section class="ws-portfolio bg-[var(--ws-portfolio-bg)] px-6 py-20">
<div class="mx-auto max-w-7xl">
<h2 class="text-3xl font-bold tracking-tight text-[var(--ws-portfolio-text)] sm:text-4xl">Portfolio</h2>
<p class="mt-4 max-w-2xl text-base text-[var(--ws-portfolio-text-soft)]">Browse our work by category.</p>
<!-- Filter tabs -->
<div class="mt-8 flex flex-wrap gap-2" role="tablist" aria-label="Project categories">
<button type="button" role="tab" aria-selected="true" class="rounded-full bg-[var(--ws-portfolio-action-bg)] px-4 py-2 text-sm font-medium text-[var(--ws-portfolio-action-text)]">All</button>
<button type="button" role="tab" aria-selected="false" class="rounded-full border border-[var(--ws-portfolio-border)] px-4 py-2 text-sm font-medium text-[var(--ws-portfolio-text-soft)] transition hover:border-[var(--ws-portfolio-accent)] hover:text-[var(--ws-portfolio-text)]">Web</button>
<button type="button" role="tab" aria-selected="false" class="rounded-full border border-[var(--ws-portfolio-border)] px-4 py-2 text-sm font-medium text-[var(--ws-portfolio-text-soft)] transition hover:border-[var(--ws-portfolio-accent)] hover:text-[var(--ws-portfolio-text)]">Mobile</button>
<button type="button" role="tab" aria-selected="false" class="rounded-full border border-[var(--ws-portfolio-border)] px-4 py-2 text-sm font-medium text-[var(--ws-portfolio-text-soft)] transition hover:border-[var(--ws-portfolio-accent)] hover:text-[var(--ws-portfolio-text)]">Branding</button>
</div>
<!-- Project grid -->
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3" role="tabpanel">
<!-- Project 1 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-violet-200 to-indigo-300 dark:from-violet-800 dark:to-indigo-700" role="img" aria-label="SaaS Dashboard">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Web</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">SaaS Dashboard</h3>
</div>
</div>
<!-- Project 2 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-emerald-200 to-teal-300 dark:from-emerald-800 dark:to-teal-700" role="img" aria-label="Fitness Tracker">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Mobile</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">Fitness Tracker</h3>
</div>
</div>
<!-- Project 3 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-amber-200 to-orange-300 dark:from-amber-800 dark:to-orange-700" role="img" aria-label="Brand Identity">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Branding</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">Brand Identity</h3>
</div>
</div>
<!-- Project 4 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-cyan-200 to-blue-300 dark:from-cyan-800 dark:to-blue-700" role="img" aria-label="E-Commerce Platform">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Web</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">E-Commerce Platform</h3>
</div>
</div>
<!-- Project 5 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-rose-200 to-pink-300 dark:from-rose-800 dark:to-pink-700" role="img" aria-label="Banking App">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Mobile</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">Banking App</h3>
</div>
</div>
<!-- Project 6 -->
<div class="overflow-hidden rounded-2xl border border-[var(--ws-portfolio-border)]">
<div class="flex aspect-[16/10] items-center justify-center bg-gradient-to-br from-fuchsia-200 to-purple-300 dark:from-fuchsia-800 dark:to-purple-700" role="img" aria-label="Startup Rebrand">
<svg class="h-8 w-8 text-white/60" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</div>
<div class="p-4">
<span class="text-xs font-medium text-[var(--ws-portfolio-accent)]">Branding</span>
<h3 class="mt-1 text-sm font-semibold text-[var(--ws-portfolio-text)]">Startup Rebrand</h3>
</div>
</div>
</div>
</div>
</section>
Details
Responsive Dark Mode Tailwind Only SSR Safe Copy & Paste
Stable Published
portfoliofiltertabsprojectsgallerycategories
Slots
| Name | Required | Description |
|---|---|---|
| heading | Yes | Section heading and subtitle. |
| filters | Yes | Category filter tab buttons. |
| projects | Yes | Filterable project cards. |
Filterable portfolio with four category tabs (All, Web, Mobile, Branding) and a 3-column grid of six project cards. The active tab is highlighted with the action background color. Add JS to show/hide projects based on selected category.