Something went wrong. Try again.
Website hub for the atmosphere community, aggregating posts, events, regional, and more
Something went wrong. Try again.
5.1 kB · 196 lines
Astro
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197---import Avatar, { type AvatarSource } from "../Avatar.astro";import SafeExternalLink from "../SafeExternalLink.astro";import { hueFromString } from "../../lib/avatar-fallback";
interface Props { title: string; url: string; excerpt?: string; publishedAt?: Date; sharedAt: Date; author: { did: string; handle: string; displayName?: string; avatar: AvatarSource; }; sharedBy?: { did: string; handle: string; displayName?: string; avatar: AvatarSource; }; source: string; documentUri?: string; shareRecordUri?: string; shareRecordRkey?: string; tags?: string[];}
const { title, url, excerpt, publishedAt, sharedAt, author, sharedBy, source, tags } = Astro.props;const hasActions = Astro.slots.has("actions");
const date = publishedAt ?? sharedAt;const authorLabel = author.displayName || `@${author.handle}`;const sharedByLabel = sharedBy ? sharedBy.displayName || `@${sharedBy.handle}` : null;const shouldShowSharedBy = !!sharedBy && sharedBy.did !== author.did;const tag = tags?.[0];const community = `@${source}`;
const communityHue = hueFromString(source);---
<article class="post-card"> <SafeExternalLink href={url} class="post-card__link" fallbackTag="div"> <Avatar avatar={author.avatar} size={36} imgClass="post-card__avatar-img" fallbackClass="post-card__avatar" /> <div class="post-card__body"> <h3 class="post-card__title">{title}</h3> {excerpt && <p class="post-card__excerpt">{excerpt}</p>} <div class="post-card__meta"> <span class="badge badge--location" style={`--community-hue:${communityHue}`} >{community}</span> <span class="post-card__person"> <span class="post-card__person-label">by</span> <span class="post-card__person-name">{authorLabel}</span> </span> {shouldShowSharedBy && sharedByLabel && ( <span class="post-card__person"> <span class="post-card__person-label">shared by</span> <span class="post-card__person-name">{sharedByLabel}</span> </span> )} <span class="post-card__date"> <time datetime={date.toISOString()}>{date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</time> </span> {tag && <span class="post-card__tag">#{tag}</span>} </div> </div> </SafeExternalLink> {hasActions && ( <div class="post-card__actions"> <slot name="actions" /> </div> )}</article>
<style> .post-card { background: var(--card); border: 1px solid var(--card-border); border-radius: var(--radius-md); transition: border-color 0.15s ease; } .post-card:hover { border-color: var(--card-border-hover); }
.post-card__link { display: flex; gap: var(--space-md); padding: var(--space-base); text-decoration: none; color: inherit; }
.post-card :global(.post-card__avatar-img) { flex-shrink: 0; width: 36px; height: 36px; border-radius: var(--radius-sm); object-fit: cover; }
.post-card :global(.post-card__avatar) { flex-shrink: 0; width: 36px; height: 36px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: oklch(98% 0 0); font-size: var(--text-xs); font-weight: 700; font-family: var(--font-body); }
.post-card__body { min-width: 0; }
.post-card__title { font-family: var(--font-body); font-weight: 600; font-size: var(--text-sm); color: var(--card-foreground); line-height: var(--leading-tight); }
.post-card__excerpt { font-size: var(--text-xs); color: var(--foreground-muted); margin-top: var(--space-xs); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: var(--leading-normal); }
.post-card__meta { display: flex; align-items: center; gap: var(--space-sm); margin-top: var(--space-sm); font-size: var(--text-xs); color: var(--foreground-muted); }
.post-card__person { display: inline-flex; align-items: baseline; gap: 0.25rem; min-width: 0; }
.post-card__person-label { color: var(--separator-fg); }
.post-card__person-name { font-weight: 500; color: var(--foreground-secondary); }
.post-card__date { color: var(--separator-fg); }
.post-card__tag { color: var(--link); font-weight: 500; text-decoration: underline; text-decoration-color: color-mix(in oklch, var(--link) 35%, transparent); text-underline-offset: 2px; } .post-card__link:hover .post-card__tag { text-decoration-color: currentColor; }
.post-card__actions { display: flex; justify-content: flex-end; padding: 0 var(--space-base) var(--space-base); }
.post-card :global(.badge--location) { background: oklch(94% 0.04 var(--community-hue, 250)); color: oklch(38% 0.12 var(--community-hue, 250)); }</style>