Latest Updated Pages
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
This commit is contained in:
+25
-18
@@ -53,7 +53,7 @@ function formatDate(date: Date): string {
|
||||
</section>
|
||||
|
||||
<!-- Category Filter -->
|
||||
<section class="bg-secondary-50 border-b border-secondary-200">
|
||||
<section class="bg-secondary-50 dark:bg-secondary-900 border-b border-secondary-200 dark:border-secondary-700">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div class="flex flex-wrap gap-2 justify-center" role="tablist" aria-label="Filter posts by category">
|
||||
{categories.map((category, index) => (
|
||||
@@ -66,7 +66,7 @@ function formatDate(date: Date): string {
|
||||
'filter-btn px-4 py-2 rounded-full text-sm font-medium transition-all duration-200',
|
||||
index === 0
|
||||
? 'bg-primary-500 text-white'
|
||||
: 'bg-white text-secondary-600 hover:bg-secondary-100 border border-secondary-200',
|
||||
: 'bg-white dark:bg-secondary-800 text-secondary-600 dark:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-secondary-700 border border-secondary-200 dark:border-secondary-600',
|
||||
]}
|
||||
>
|
||||
{category}
|
||||
@@ -77,18 +77,18 @@ function formatDate(date: Date): string {
|
||||
</section>
|
||||
|
||||
<!-- Blog Posts Grid -->
|
||||
<section class="py-16 bg-white">
|
||||
<section class="py-16 bg-white dark:bg-secondary-900">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" id="posts-grid">
|
||||
{sortedPosts.map((post) => {
|
||||
const readingTime = getReadingTime(post.body);
|
||||
return (
|
||||
<article
|
||||
class="post-card group bg-white rounded-2xl shadow-lg overflow-hidden border border-secondary-100 hover:shadow-xl hover:border-primary-200 transition-all duration-300"
|
||||
class="post-card group bg-white dark:bg-secondary-800 rounded-2xl shadow-lg overflow-hidden border border-secondary-100 dark:border-secondary-700 hover:shadow-xl hover:border-primary-200 dark:hover:border-primary-500 transition-all duration-300"
|
||||
data-category={post.data.category}
|
||||
>
|
||||
<!-- Post Image -->
|
||||
<a href={`/blog/${post.slug}`} class="block aspect-video overflow-hidden bg-secondary-200">
|
||||
<a href={`/blog/${post.slug}`} class="block aspect-video overflow-hidden bg-secondary-200 dark:bg-secondary-700">
|
||||
{post.data.heroImage ? (
|
||||
<img
|
||||
src={post.data.heroImage}
|
||||
@@ -112,10 +112,10 @@ function formatDate(date: Date): string {
|
||||
<div class="p-6">
|
||||
<!-- Category & Reading Time -->
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<span class="px-3 py-1 bg-primary-50 text-primary-700 text-xs font-semibold rounded-full">
|
||||
<span class="px-3 py-1 bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-semibold rounded-full">
|
||||
{post.data.category}
|
||||
</span>
|
||||
<span class="text-secondary-400 text-sm flex items-center gap-1">
|
||||
<span class="text-secondary-400 dark:text-secondary-500 text-sm flex items-center gap-1">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
@@ -124,33 +124,33 @@ function formatDate(date: Date): string {
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<h2 class="text-xl font-bold text-secondary-900 mb-3 line-clamp-2 group-hover:text-primary-600 transition-colors">
|
||||
<h2 class="text-xl font-bold text-secondary-900 dark:text-secondary-100 mb-3 line-clamp-2 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
|
||||
<a href={`/blog/${post.slug}`}>{post.data.title}</a>
|
||||
</h2>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="text-secondary-600 text-sm mb-4 line-clamp-3">
|
||||
<p class="text-secondary-600 dark:text-secondary-400 text-sm mb-4 line-clamp-3">
|
||||
{post.data.description}
|
||||
</p>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{post.data.tags.slice(0, 3).map((tag) => (
|
||||
<span class="text-xs text-secondary-500 bg-secondary-100 px-2 py-1 rounded">
|
||||
<span class="text-xs text-secondary-500 dark:text-secondary-400 bg-secondary-100 dark:bg-secondary-700 px-2 py-1 rounded">
|
||||
#{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- Author & Date -->
|
||||
<div class="flex items-center justify-between pt-4 border-t border-secondary-100">
|
||||
<div class="flex items-center justify-between pt-4 border-t border-secondary-100 dark:border-secondary-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-full bg-gradient-to-br from-primary-400 to-primary-600 flex items-center justify-center text-white text-sm font-bold">
|
||||
{post.data.author.name.charAt(0)}
|
||||
</div>
|
||||
<span class="text-sm text-secondary-600">{post.data.author.name}</span>
|
||||
<span class="text-sm text-secondary-600 dark:text-secondary-400">{post.data.author.name}</span>
|
||||
</div>
|
||||
<time class="text-sm text-secondary-400" datetime={post.data.pubDate.toISOString()}>
|
||||
<time class="text-sm text-secondary-400 dark:text-secondary-500" datetime={post.data.pubDate.toISOString()}>
|
||||
{formatDate(post.data.pubDate)}
|
||||
</time>
|
||||
</div>
|
||||
@@ -162,11 +162,11 @@ function formatDate(date: Date): string {
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="empty-state" class="hidden text-center py-16">
|
||||
<svg class="w-16 h-16 text-secondary-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-16 h-16 text-secondary-300 dark:text-secondary-600 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2z" />
|
||||
</svg>
|
||||
<h3 class="text-xl font-semibold text-secondary-600 mb-2">No posts found</h3>
|
||||
<p class="text-secondary-400">Try selecting a different category.</p>
|
||||
<h3 class="text-xl font-semibold text-secondary-600 dark:text-secondary-400 mb-2">No posts found</h3>
|
||||
<p class="text-secondary-400 dark:text-secondary-500">Try selecting a different category.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -180,14 +180,21 @@ function formatDate(date: Date): string {
|
||||
filterButtons.forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const category = btn.getAttribute('data-category');
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
|
||||
// Update button states
|
||||
filterButtons.forEach((b) => {
|
||||
b.classList.remove('bg-primary-500', 'text-white');
|
||||
b.classList.add('bg-white', 'text-secondary-600', 'border', 'border-secondary-200');
|
||||
if (isDark) {
|
||||
b.classList.add('bg-secondary-800', 'text-secondary-400', 'border', 'border-secondary-600');
|
||||
b.classList.remove('bg-white', 'text-secondary-600', 'border-secondary-200');
|
||||
} else {
|
||||
b.classList.add('bg-white', 'text-secondary-600', 'border', 'border-secondary-200');
|
||||
b.classList.remove('bg-secondary-800', 'text-secondary-400', 'border-secondary-600');
|
||||
}
|
||||
b.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
btn.classList.remove('bg-white', 'text-secondary-600', 'border', 'border-secondary-200');
|
||||
btn.classList.remove('bg-white', 'bg-secondary-800', 'text-secondary-600', 'text-secondary-400', 'border', 'border-secondary-200', 'border-secondary-600');
|
||||
btn.classList.add('bg-primary-500', 'text-white');
|
||||
btn.setAttribute('aria-selected', 'true');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user