Updated UX to remove the weird looking cards. No more cards!
All checks were successful
Docker Deploy / build-and-push (push) Successful in 5m47s
All checks were successful
Docker Deploy / build-and-push (push) Successful in 5m47s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
import PostCard from "../components/PostCard.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
|
||||
// Get all posts from the content collection
|
||||
const posts = await getCollection("posts");
|
||||
@@ -11,6 +11,14 @@ const sortedPosts = posts.sort(
|
||||
(a: CollectionEntry<"posts">, b: CollectionEntry<"posts">) =>
|
||||
new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(),
|
||||
);
|
||||
|
||||
function formatDate(date: Date): string {
|
||||
return date.toLocaleDateString("en-us", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
---
|
||||
|
||||
<Layout>
|
||||
@@ -20,27 +28,98 @@ const sortedPosts = posts.sort(
|
||||
>
|
||||
Posts
|
||||
</h1>
|
||||
<div
|
||||
class:list={[
|
||||
sortedPosts.length <= 2
|
||||
? "flex flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto px-4"
|
||||
: "columns-1 sm:columns-2 lg:columns-3 gap-4 sm:gap-6 max-w-6xl mx-auto px-4",
|
||||
]}
|
||||
>
|
||||
|
||||
{/* Mobile: One-sided compact timeline */}
|
||||
<ul class="timeline timeline-vertical timeline-compact timeline-snap-icon max-w-3xl mx-auto px-4 md:hidden">
|
||||
{
|
||||
sortedPosts.map((post) => (
|
||||
<div
|
||||
class:list={[
|
||||
sortedPosts.length <= 2
|
||||
? "w-full sm:w-96"
|
||||
: "inline-block break-inside-avoid w-full",
|
||||
]}
|
||||
>
|
||||
<PostCard post={post} />
|
||||
</div>
|
||||
sortedPosts.map((post, index) => (
|
||||
<li>
|
||||
{index > 0 && <hr class="bg-primary" />}
|
||||
|
||||
<div class="timeline-middle">
|
||||
<Icon name="mdi:circle" class="w-4 h-4 text-primary" />
|
||||
</div>
|
||||
|
||||
<div class="timeline-end mb-8 ml-4">
|
||||
<div class="border border-base-content/20 rounded-box p-4 bg-base-200 hover:border-primary transition-colors">
|
||||
<time class="font-mono text-sm opacity-60">
|
||||
{formatDate(new Date(post.data.pubDate))}
|
||||
</time>
|
||||
|
||||
<a
|
||||
href={`/post/${post.slug}`}
|
||||
class="block group"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-primary group-hover:text-accent transition-colors">
|
||||
{post.data.title}
|
||||
</h3>
|
||||
|
||||
<p class="text-sm opacity-80 mt-1">
|
||||
{post.data.description || "No description available."}
|
||||
</p>
|
||||
</a>
|
||||
|
||||
{post.data.tags && post.data.tags.length > 0 && (
|
||||
<div class="flex gap-1 flex-wrap mt-2">
|
||||
{post.data.tags.slice(0, 3).map((tag: string) => (
|
||||
<span class="badge badge-sm badge-outline">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{index < sortedPosts.length - 1 && <hr class="bg-primary" />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
{/* Desktop: Dual-sided alternating timeline */}
|
||||
<ul class="timeline timeline-vertical timeline-snap-icon max-w-3xl mx-auto px-4 hidden md:block">
|
||||
{
|
||||
sortedPosts.map((post, index) => (
|
||||
<li>
|
||||
{index > 0 && <hr class="bg-primary" />}
|
||||
|
||||
<div class="timeline-middle">
|
||||
<Icon name="mdi:circle" class="w-4 h-4 text-primary" />
|
||||
</div>
|
||||
|
||||
<div class={`timeline-${index % 2 === 0 ? 'start' : 'end'} text-${index % 2 === 0 ? 'end' : 'start'} mb-8 mx-4`}>
|
||||
<div class="border border-base-content/20 rounded-box p-4 bg-base-200 hover:border-primary transition-colors">
|
||||
<time class="font-mono text-sm opacity-60">
|
||||
{formatDate(new Date(post.data.pubDate))}
|
||||
</time>
|
||||
|
||||
<a
|
||||
href={`/post/${post.slug}`}
|
||||
class="block group"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-primary group-hover:text-accent transition-colors">
|
||||
{post.data.title}
|
||||
</h3>
|
||||
|
||||
<p class="text-sm opacity-80 mt-1">
|
||||
{post.data.description || "No description available."}
|
||||
</p>
|
||||
</a>
|
||||
|
||||
{post.data.tags && post.data.tags.length > 0 && (
|
||||
<div class={`flex gap-1 flex-wrap mt-2 ${index % 2 === 0 ? 'justify-end' : 'justify-start'}`}>
|
||||
{post.data.tags.slice(0, 3).map((tag: string) => (
|
||||
<span class="badge badge-sm badge-outline">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{index < sortedPosts.length - 1 && <hr class="bg-primary" />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
{
|
||||
sortedPosts.length === 0 && (
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import TalkCard from "../components/TalkCard.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { config } from "../config";
|
||||
|
||||
// Sort talks by date, newest first
|
||||
const sortedTalks = [...config.talks].sort((a, b) => {
|
||||
if (!a.date || !b.date) return 0;
|
||||
return new Date(b.date).valueOf() - new Date(a.date).valueOf();
|
||||
});
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
return new Date(dateStr).toLocaleDateString("en-us", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
---
|
||||
|
||||
<Layout>
|
||||
@@ -11,30 +25,130 @@ import { config } from "../config";
|
||||
>
|
||||
Talks
|
||||
</h1>
|
||||
<div
|
||||
class:list={[
|
||||
config.talks.length <= 2
|
||||
? "flex flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto px-4"
|
||||
: "columns-1 sm:columns-2 lg:columns-3 gap-4 sm:gap-6 max-w-6xl mx-auto px-4",
|
||||
]}
|
||||
>
|
||||
{
|
||||
config.talks.map((talk) => (
|
||||
<div
|
||||
class:list={[
|
||||
config.talks.length <= 2
|
||||
? "w-full sm:w-96"
|
||||
: "inline-block break-inside-avoid w-full",
|
||||
]}
|
||||
|
||||
{/* Single talk: Simple centered card without timeline */}
|
||||
{sortedTalks.length === 1 && (
|
||||
<div class="max-w-xl mx-auto px-4">
|
||||
<div class="border border-base-content/20 rounded-box p-6 bg-base-200 hover:border-primary transition-colors">
|
||||
{sortedTalks[0].date && (
|
||||
<time class="font-mono text-sm opacity-60">
|
||||
{formatDate(sortedTalks[0].date)}
|
||||
</time>
|
||||
)}
|
||||
|
||||
<a
|
||||
href={sortedTalks[0].link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block group"
|
||||
>
|
||||
<TalkCard talk={talk} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-primary group-hover:text-accent transition-colors">
|
||||
{sortedTalks[0].name}
|
||||
</h3>
|
||||
|
||||
<p class="opacity-80 mt-2">
|
||||
{sortedTalks[0].description}
|
||||
</p>
|
||||
|
||||
<span class="inline-flex items-center gap-1 text-sm text-primary mt-3 group-hover:text-accent transition-colors">
|
||||
<Icon name="mdi:open-in-new" class="w-4 h-4" />
|
||||
View talk
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Multiple talks: Mobile one-sided compact timeline */}
|
||||
{sortedTalks.length > 1 && (
|
||||
<ul class="timeline timeline-vertical timeline-compact timeline-snap-icon max-w-3xl mx-auto px-4 md:hidden">
|
||||
{
|
||||
sortedTalks.map((talk, index) => (
|
||||
<li>
|
||||
{index > 0 && <hr class="bg-primary" />}
|
||||
|
||||
<div class="timeline-middle">
|
||||
<Icon name="mdi:circle" class="w-4 h-4 text-primary" />
|
||||
</div>
|
||||
|
||||
<div class="timeline-end mb-8 ml-4">
|
||||
<div class="border border-base-content/20 rounded-box p-4 bg-base-200 hover:border-primary transition-colors">
|
||||
{talk.date && (
|
||||
<time class="font-mono text-sm opacity-60">
|
||||
{formatDate(talk.date)}
|
||||
</time>
|
||||
)}
|
||||
|
||||
<a
|
||||
href={talk.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block group"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-primary group-hover:text-accent transition-colors">
|
||||
{talk.name}
|
||||
</h3>
|
||||
|
||||
<p class="text-sm opacity-80 mt-1">
|
||||
{talk.description}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{index < sortedTalks.length - 1 && <hr class="bg-primary" />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* Multiple talks: Desktop dual-sided alternating timeline */}
|
||||
{sortedTalks.length > 1 && (
|
||||
<ul class="timeline timeline-vertical timeline-snap-icon max-w-3xl mx-auto px-4 hidden md:block">
|
||||
{
|
||||
sortedTalks.map((talk, index) => (
|
||||
<li>
|
||||
{index > 0 && <hr class="bg-primary" />}
|
||||
|
||||
<div class="timeline-middle">
|
||||
<Icon name="mdi:circle" class="w-4 h-4 text-primary" />
|
||||
</div>
|
||||
|
||||
<div class={`timeline-${index % 2 === 0 ? 'start' : 'end'} text-${index % 2 === 0 ? 'end' : 'start'} mb-8 mx-4`}>
|
||||
<div class="border border-base-content/20 rounded-box p-4 bg-base-200 hover:border-primary transition-colors">
|
||||
{talk.date && (
|
||||
<time class="font-mono text-sm opacity-60">
|
||||
{formatDate(talk.date)}
|
||||
</time>
|
||||
)}
|
||||
|
||||
<a
|
||||
href={talk.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block group"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-primary group-hover:text-accent transition-colors">
|
||||
{talk.name}
|
||||
</h3>
|
||||
|
||||
<p class="text-sm opacity-80 mt-1">
|
||||
{talk.description}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{index < sortedTalks.length - 1 && <hr class="bg-primary" />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{
|
||||
config.talks.length === 0 && (
|
||||
sortedTalks.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No talks available yet. Check back soon!
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user