cards toggle grid list view-switcher articles css-only interactive cards toggle grid list view switcher articles blog css-only grid list view toggle without JavaScript article cards with layout switcher
Toggle Layout Cards
Fetch pattern JSON:
curl https://webspire.de/patterns/cards/toggle-layout.json toggle-layout.html
<section class="ws-cards-toggle-layout bg-[var(--ws-color-bg)] py-20">
<div class="mx-auto max-w-7xl px-6">
<!-- Hidden toggle input -->
<input type="checkbox" id="view-toggle" class="peer sr-only" />
<!-- Header with toggle -->
<div class="mb-8 flex items-center justify-between">
<div>
<h2 class="text-2xl font-bold text-[var(--ws-color-text)]">Latest articles</h2>
<p class="mt-1 text-sm text-[var(--ws-color-text-soft)]">Insights, tutorials, and product updates.</p>
</div>
<!-- Toggle buttons: Grid / List -->
<div class="flex items-center gap-1 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] p-1">
<!-- Grid button: active when unchecked -->
<label for="view-toggle" class="flex cursor-pointer items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-medium transition peer-checked:text-[var(--ws-color-text-soft)] peer-[:not(:checked)]~&:bg-[var(--ws-color-bg-secondary)] peer-[:not(:checked)]~&:text-[var(--ws-color-text)]">
<!-- This label keeps grid active when unchecked, list active when checked -->
<!-- We use the sibling trick differently — see style block below -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
<span>Grid</span>
</label>
<!-- List button: active when checked -->
<label for="view-toggle" class="flex cursor-pointer items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-medium transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<span>List</span>
</label>
</div>
</div>
<!-- Cards container — grid default, list when checked -->
<div class="cards-container grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4 peer-checked:flex peer-checked:flex-col peer-checked:gap-4">
<!-- Article 1 -->
<article class="group overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] transition hover:shadow-md peer-checked:flex peer-checked:flex-row">
<div class="article-image aspect-[16/9] bg-gradient-to-br from-indigo-200 to-violet-300 peer-checked:aspect-auto peer-checked:w-1/4 peer-checked:flex-shrink-0"></div>
<div class="p-5 peer-checked:flex-1">
<span class="text-xs font-semibold uppercase tracking-wider text-[var(--ws-color-accent)]">Design</span>
<h3 class="mt-2 font-bold text-[var(--ws-color-text)] group-hover:text-[var(--ws-color-accent)]">The art of minimal UI — how less delivers more</h3>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)] line-clamp-2">Restraint in design isn't a limitation — it's a skill. Learn how top designers ship with less.</p>
<div class="mt-4 flex items-center gap-2">
<div class="h-6 w-6 rounded-full bg-gradient-to-br from-indigo-400 to-violet-500"></div>
<span class="text-xs text-[var(--ws-color-text-soft)]">Anna K. · 5 min read</span>
</div>
</div>
</article>
<!-- Article 2 -->
<article class="group overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] transition hover:shadow-md">
<div class="article-image aspect-[16/9] bg-gradient-to-br from-emerald-200 to-teal-300"></div>
<div class="p-5">
<span class="text-xs font-semibold uppercase tracking-wider text-[var(--ws-color-accent)]">Engineering</span>
<h3 class="mt-2 font-bold text-[var(--ws-color-text)] group-hover:text-[var(--ws-color-accent)]">Tailwind v4 — what changes and why it matters</h3>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)] line-clamp-2">The new CSS-first config is a paradigm shift. Here's our take after six months in production.</p>
<div class="mt-4 flex items-center gap-2">
<div class="h-6 w-6 rounded-full bg-gradient-to-br from-emerald-400 to-teal-500"></div>
<span class="text-xs text-[var(--ws-color-text-soft)]">Marco R. · 8 min read</span>
</div>
</div>
</article>
<!-- Article 3 -->
<article class="group overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] transition hover:shadow-md">
<div class="article-image aspect-[16/9] bg-gradient-to-br from-rose-200 to-pink-300"></div>
<div class="p-5">
<span class="text-xs font-semibold uppercase tracking-wider text-[var(--ws-color-accent)]">Product</span>
<h3 class="mt-2 font-bold text-[var(--ws-color-text)] group-hover:text-[var(--ws-color-accent)]">Designing for AI-first workflows in 2026</h3>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)] line-clamp-2">When the primary user is an LLM, your UX patterns need to evolve. Here's how we adapted.</p>
<div class="mt-4 flex items-center gap-2">
<div class="h-6 w-6 rounded-full bg-gradient-to-br from-rose-400 to-pink-500"></div>
<span class="text-xs text-[var(--ws-color-text-soft)]">Lena M. · 6 min read</span>
</div>
</div>
</article>
<!-- Article 4 -->
<article class="group overflow-hidden rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] transition hover:shadow-md">
<div class="article-image aspect-[16/9] bg-gradient-to-br from-amber-200 to-orange-300"></div>
<div class="p-5">
<span class="text-xs font-semibold uppercase tracking-wider text-[var(--ws-color-accent)]">Tutorial</span>
<h3 class="mt-2 font-bold text-[var(--ws-color-text)] group-hover:text-[var(--ws-color-accent)]">Build a sticky sidebar layout from scratch</h3>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)] line-clamp-2">Step-by-step walkthrough of building the classic docs layout with Tailwind CSS alone.</p>
<div class="mt-4 flex items-center gap-2">
<div class="h-6 w-6 rounded-full bg-gradient-to-br from-amber-400 to-orange-500"></div>
<span class="text-xs text-[var(--ws-color-text-soft)]">Jonas S. · 10 min read</span>
</div>
</div>
</article>
</div>
</div>
<style>
/* CSS-only view toggle via :checked sibling combinator */
#view-toggle:checked ~ div .cards-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
#view-toggle:checked ~ div .cards-container article {
display: flex;
flex-direction: row;
}
#view-toggle:checked ~ div .cards-container .article-image {
width: 25%;
flex-shrink: 0;
aspect-ratio: auto;
min-height: 120px;
}
/* Toggle button active states */
#view-toggle:not(:checked) ~ div label:first-of-type {
background-color: var(--ws-color-bg-secondary);
color: var(--ws-color-text);
}
#view-toggle:checked ~ div label:last-of-type {
background-color: var(--ws-color-bg-secondary);
color: var(--ws-color-text);
}
</style>
</section>
Details
Responsive Dark Mode Tailwind Only SSR Safe Copy & Paste
Stable Published
cardstogglegridlistview-switcherarticlescss-onlyinteractive
Slots
| Name | Required | Description |
|---|---|---|
| header | Yes | Section title and toggle buttons. |
| cards | Yes | Four article cards with image, category, title, description, and author. |
Article listing with a Grid/List view toggle powered entirely by CSS. A hidden <input type="checkbox"> acts as the state store; a <style> block uses #view-toggle:checked ~ div .cards-container selectors to switch the container from a 4-column grid to a full-width flex column, and to update the active toggle button highlight. Four article cards with image placeholder, category badge, title, description, and author row.