Skip to content

Commit 783129c

Browse files
feat: add real-time search input and category filter chips to blog archive page
1 parent 08205c5 commit 783129c

1 file changed

Lines changed: 187 additions & 3 deletions

File tree

src/pages/blog/index.astro

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,35 @@ const categoriesList = ['All', 'Agentic AI & LLMs', 'Kubernetes', 'Observability
6767
Visit DEV.to Profile →
6868
</a>
6969
</div>
70+
<div class="search-container">
71+
<div class="search-box glass-card">
72+
<span class="search-icon">🔍</span>
73+
<input
74+
type="text"
75+
id="blog-search-input"
76+
placeholder="Search articles by title, topic, or keyword (e.g. Kubernetes, Bedrock, OTEL)..."
77+
autocomplete="off"
78+
/>
79+
<button id="clear-search-btn" class="clear-btn" style="display: none;">✕</button>
80+
</div>
81+
82+
<div class="category-filters">
83+
{categoriesList.map((cat, idx) => (
84+
<button class:list={["filter-chip", { active: idx === 0 }]} data-category={cat}>
85+
{cat}
86+
</button>
87+
))}
88+
</div>
89+
</div>
7090
</header>
7191

72-
<div class="archive-list">
92+
<div id="no-results" class="no-results-msg glass-card" style="display: none;">
93+
<p>No articles found matching your search. Try a different query or select "All".</p>
94+
</div>
95+
96+
<div class="archive-list" id="articles-container">
7397
{processedPosts.map(post => (
74-
<article class="glass-card archive-card">
98+
<article class="glass-card archive-card" data-title={post.title.toLowerCase()} data-category={post.category}>
7599
<div class="card-meta">
76100
<span class="tag-badge">{post.category}</span>
77101
<div class="meta-right">
@@ -96,6 +120,70 @@ const categoriesList = ['All', 'Agentic AI & LLMs', 'Kubernetes', 'Observability
96120
</div>
97121
</BaseLayout>
98122

123+
<script>
124+
document.addEventListener('DOMContentLoaded', () => {
125+
const searchInput = document.getElementById('blog-search-input') as HTMLInputElement;
126+
const clearBtn = document.getElementById('clear-search-btn');
127+
const filterChips = document.querySelectorAll('.filter-chip');
128+
const articles = document.querySelectorAll<HTMLElement>('.archive-card');
129+
const noResultsMsg = document.getElementById('no-results');
130+
131+
let currentCategory = 'All';
132+
let currentQuery = '';
133+
134+
const filterArticles = () => {
135+
let visibleCount = 0;
136+
articles.forEach(article => {
137+
const title = article.getAttribute('data-title') || '';
138+
const category = article.getAttribute('data-category') || '';
139+
140+
const matchesQuery = !currentQuery || title.includes(currentQuery.toLowerCase());
141+
const matchesCategory = currentCategory === 'All' || category === currentCategory;
142+
143+
if (matchesQuery && matchesCategory) {
144+
article.style.display = 'flex';
145+
visibleCount++;
146+
} else {
147+
article.style.display = 'none';
148+
}
149+
});
150+
151+
if (noResultsMsg) {
152+
noResultsMsg.style.display = visibleCount === 0 ? 'block' : 'none';
153+
}
154+
};
155+
156+
if (searchInput) {
157+
searchInput.addEventListener('input', (e) => {
158+
currentQuery = (e.target as HTMLInputElement).value.trim();
159+
if (clearBtn) {
160+
clearBtn.style.display = currentQuery ? 'block' : 'none';
161+
}
162+
filterArticles();
163+
});
164+
}
165+
166+
if (clearBtn && searchInput) {
167+
clearBtn.addEventListener('click', () => {
168+
searchInput.value = '';
169+
currentQuery = '';
170+
clearBtn.style.display = 'none';
171+
searchInput.focus();
172+
filterArticles();
173+
});
174+
}
175+
176+
filterChips.forEach(chip => {
177+
chip.addEventListener('click', () => {
178+
filterChips.forEach(c => c.classList.remove('active'));
179+
chip.classList.add('active');
180+
currentCategory = chip.getAttribute('data-category') || 'All';
181+
filterArticles();
182+
});
183+
});
184+
});
185+
</script>
186+
99187
<style>
100188
.blog-archive-page {
101189
padding: 3rem 1.5rem;
@@ -139,7 +227,7 @@ const categoriesList = ['All', 'Agentic AI & LLMs', 'Kubernetes', 'Observability
139227
padding: 1rem 1.5rem;
140228
font-size: 0.95rem;
141229
max-width: 800px;
142-
margin: 0 auto;
230+
margin: 0 auto 2rem;
143231
}
144232

145233
.banner-label {
@@ -148,6 +236,102 @@ const categoriesList = ['All', 'Agentic AI & LLMs', 'Kubernetes', 'Observability
148236
margin-right: 0.5rem;
149237
}
150238

239+
.search-container {
240+
max-width: 800px;
241+
margin: 0 auto;
242+
display: flex;
243+
flex-direction: column;
244+
gap: 1.25rem;
245+
}
246+
247+
.search-box {
248+
display: flex;
249+
align-items: center;
250+
gap: 0.75rem;
251+
padding: 0.75rem 1.25rem;
252+
border-radius: 12px;
253+
background: rgba(15, 23, 42, 0.6);
254+
}
255+
256+
.search-icon {
257+
font-size: 1.1rem;
258+
opacity: 0.7;
259+
}
260+
261+
.search-box input {
262+
width: 100%;
263+
background: transparent;
264+
border: none;
265+
outline: none;
266+
color: var(--text-main);
267+
font-size: 0.98rem;
268+
font-family: inherit;
269+
}
270+
271+
.search-box input::placeholder {
272+
color: var(--text-muted);
273+
opacity: 0.7;
274+
}
275+
276+
.clear-btn {
277+
background: rgba(255, 255, 255, 0.1);
278+
border: none;
279+
color: var(--text-muted);
280+
border-radius: 50%;
281+
width: 22px;
282+
height: 22px;
283+
display: flex;
284+
align-items: center;
285+
justify-content: center;
286+
cursor: pointer;
287+
font-size: 0.75rem;
288+
transition: all 0.2s ease;
289+
}
290+
291+
.clear-btn:hover {
292+
background: rgba(239, 68, 68, 0.2);
293+
color: #ef4444;
294+
}
295+
296+
.category-filters {
297+
display: flex;
298+
flex-wrap: wrap;
299+
gap: 0.5rem;
300+
justify-content: center;
301+
}
302+
303+
.filter-chip {
304+
padding: 0.4rem 0.9rem;
305+
border-radius: 9999px;
306+
background: rgba(255, 255, 255, 0.04);
307+
border: 1px solid var(--border-glass);
308+
color: var(--text-muted);
309+
font-size: 0.85rem;
310+
font-weight: 500;
311+
cursor: pointer;
312+
transition: all 0.2s ease;
313+
}
314+
315+
.filter-chip:hover {
316+
background: rgba(255, 255, 255, 0.08);
317+
color: var(--text-main);
318+
}
319+
320+
.filter-chip.active {
321+
background: rgba(56, 189, 248, 0.15);
322+
border-color: rgba(56, 189, 248, 0.4);
323+
color: var(--accent-blue);
324+
font-weight: 600;
325+
}
326+
327+
.no-results-msg {
328+
text-align: center;
329+
padding: 3rem 1.5rem;
330+
color: var(--text-muted);
331+
font-size: 1.05rem;
332+
margin-bottom: 2rem;
333+
}
334+
151335
.archive-list {
152336
display: grid;
153337
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));

0 commit comments

Comments
 (0)