Source Code
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
<h1 class="bsblog-title mb-0">From the blog</h1>
<select class="form-select form-select-sm" style="width:auto" id="bsblogSort">
<option value="newest">Newest first</option>
<option value="quickest">Quickest read</option>
</select>
</div>
<div class="row g-4" id="bsblogGrid">
<div class="col-md-4" data-date="2026-09-01" data-mins="8">
<div class="card h-100"><div class="bsblog-cover" style="--h:230"></div><div class="card-body">
<span class="badge bg-primary-subtle text-primary-emphasis mb-2">Engineering</span>
<h5 class="card-title">Scaling our API to 10x traffic</h5>
<p class="text-muted small">Sep 1 · 8 min read</p>
</div></div>
</div>
<div class="col-md-4" data-date="2026-08-20" data-mins="3">
<div class="card h-100"><div class="bsblog-cover" style="--h:20"></div><div class="card-body">
<span class="badge bg-success-subtle text-success-emphasis mb-2">Product</span>
<h5 class="card-title">Introducing dark mode</h5>
<p class="text-muted small">Aug 20 · 3 min read</p>
</div></div>
</div>
<div class="col-md-4" data-date="2026-08-28" data-mins="5">
<div class="card h-100"><div class="bsblog-cover" style="--h:150"></div><div class="card-body">
<span class="badge bg-warning-subtle text-warning-emphasis mb-2">Design</span>
<h5 class="card-title">Redesigning our onboarding flow</h5>
<p class="text-muted small">Aug 28 · 5 min read</p>
</div></div>
</div>
</div>
</div>.bsblog-title { font-weight: 800; letter-spacing: -0.01em; }
.bsblog-cover { height: 140px; border-radius: 8px 8px 0 0; background: linear-gradient(135deg, hsl(calc(var(--h)) 60% 85%), hsl(calc(var(--h) + 50) 60% 70%)); }const grid = document.getElementById('bsblogGrid');
const sort = document.getElementById('bsblogSort');
function applySort() {
const cols = Array.from(grid.children);
const key = sort.value;
cols.sort((a, b) => key === 'newest'
? new Date(b.dataset.date) - new Date(a.dataset.date)
: Number(a.dataset.mins) - Number(b.dataset.mins)
);
cols.forEach(col => grid.appendChild(col));
}
sort.addEventListener('change', applySort);Bootstrap Blog Post Card Grid with Read-Time Sort — Free Snippet
Bootstrap Blog Post Card Grid with Read-Time Filter · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Bootstrap Blog Post Card Grid with Read-Time Sort — HTML, CSS & JavaScript

Each card in this grid carries its real metadata as data attributes — data-date and data-mins — so the sort dropdown can genuinely reorder them: Array.from(grid.children) reads every card, .sort() compares dates or read-time minutes depending on the selected option, and grid.appendChild(col) in the new order physically moves each card to its sorted position, since re-appending an element already in the DOM relocates it rather than duplicating it.
Built on real Bootstrap 5.3 cards and grid, with Bootstrap 5.3's newer "subtle" badge variants for the category tags — soft enough not to compete visually with the headline, which is exactly what a category label on a card should do.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Hand this snippet's HTML, CSS, and JS to an AI coding assistant like Claude and ask it to add a category filter dropdown that combines with the existing sort, or to persist the selected sort order to localStorage so it's remembered on the next visit. It's also a good exercise to ask the assistant to load post data from a JSON file or API and render the cards dynamically instead of hardcoded HTML.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a Bootstrap 5.3 blog post card grid with a working sort dropdown, using the real Bootstrap CDN framework (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble Bootstrap.
Requirements:
- A responsive grid of at least three blog post cards using Bootstrap's real card and grid classes, each with a CSS-gradient cover, a category badge (using Bootstrap 5.3's subtle badge variant), a title, and a date/read-time line — each card carrying its real publish date and read-time-in-minutes as data attributes.
- A select dropdown with at least two options: "Newest first" and "Quickest read". Changing the selection must genuinely reorder the actual card elements in the DOM (using appendChild to reposition existing nodes, not a CSS order trick or re-rendering), sorted by the corresponding data attribute.
- The sort logic must scale to any number of cards added to the grid, without hardcoding which cards exist.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Load the snippetClick the snippet in the sidebar Library tab. The preview loads three post cards in their authored order.
- 2Select "Newest first"The cards reorder so the Sep 1 post leads, followed by Aug 28, then Aug 20.
- 3Select "Quickest read"The cards reorder again — the 3-minute post leads, then 5-minute, then 8-minute.
- 4Add a fourth postCopy a column block with its own data-date and data-mins — it participates in both sort orders automatically.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
It genuinely reorders the underlying DOM elements — grid.appendChild(col) on an element already in the DOM moves it to the new position rather than creating a duplicate, so the real element order (and therefore tab order for keyboard users) changes too.
Each card's data-mins attribute holds its read-time in minutes; the sort compares those numbers directly rather than parsing the visible "X min read" text.
Yes — add a new <option> to the select, and a new comparator branch in applySort() reading each card's title text (e.g. via querySelector('.card-title').textContent) for the comparison.
No — each is a CSS gradient keyed off a --h hue variable, so the grid works immediately with no images. Swap in real cover photography by changing .bsblog-cover's background.
Yes — bg-success-subtle and its -emphasis text counterpart are newer Bootstrap 5.3 utility variants specifically designed for lower-contrast label use, versus the older solid bg-success style.