Retitle the page, rewrite the lead, give each row a subtle tonal background, and add a back arrow to Pulse.
216 lines
6.5 KiB
Text
216 lines
6.5 KiB
Text
---
|
|
import AppLayout from '../../layouts/AppLayout.astro';
|
|
import Avatar from '../../components/Avatar.astro';
|
|
import { getLatestPublishedDispatches } from '../../lib/db';
|
|
import {
|
|
dispatchSlug, dispatchKindLabel, dispatchKindPigment,
|
|
dispatchExcerptParas, roleLabel,
|
|
} from '../../lib/format';
|
|
|
|
const user = Astro.locals.user;
|
|
const dispatches = getLatestPublishedDispatches(200);
|
|
|
|
function parseUtc(s: string): Date {
|
|
if (/T.*[Zz]$/.test(s) || /[+-]\d{2}:?\d{2}$/.test(s)) return new Date(s);
|
|
return new Date(s.replace(' ', 'T') + 'Z');
|
|
}
|
|
function fmt(iso: string): string {
|
|
return new Intl.DateTimeFormat('en-GB', {
|
|
day: 'numeric', month: 'long', year: 'numeric', timeZone: 'Europe/Copenhagen',
|
|
}).format(parseUtc(iso));
|
|
}
|
|
---
|
|
<AppLayout title="Dispatches" user={user}>
|
|
<div class="page">
|
|
|
|
<a href="/pulse" class="back-link">
|
|
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
|
<path d="M15 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
Pulse
|
|
</a>
|
|
|
|
<header class="head">
|
|
<h1 class="head-title">Dispatch.</h1>
|
|
<p class="head-sub">Where we share news from Fenja — progress, decisions, and what we're building next.</p>
|
|
</header>
|
|
|
|
{dispatches.length === 0 ? (
|
|
<p class="body-md empty">Nothing posted yet.</p>
|
|
) : (
|
|
<ul class="d-list">
|
|
{dispatches.map(d => (
|
|
<li class="d-row">
|
|
<a href={`/dispatches/${dispatchSlug(d)}`} class="d-link">
|
|
<div class="d-byline">
|
|
<Avatar id={d.author_id} name={d.author_name} size={28} />
|
|
<span class="d-author-text">
|
|
<span class="d-author-name">{d.author_name}</span>
|
|
<span class="d-author-role label-sm">{d.author_title ?? roleLabel(d.author_role)}</span>
|
|
</span>
|
|
</div>
|
|
|
|
<div class="d-body">
|
|
<header class="d-title-row">
|
|
<h2 class="d-title">{d.title}</h2>
|
|
<span class="d-kind-pill" style={`--pill: ${dispatchKindPigment(d.kind)}`}>
|
|
{dispatchKindLabel(d.kind)}
|
|
</span>
|
|
</header>
|
|
<p class="d-excerpt">{dispatchExcerptParas(d).lead}</p>
|
|
</div>
|
|
|
|
<time class="d-date label-sm" datetime={d.published_at ?? d.created_at}>
|
|
{fmt(d.published_at ?? d.created_at)}
|
|
</time>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
|
|
</div>
|
|
</AppLayout>
|
|
|
|
<style>
|
|
.page {
|
|
padding: var(--space-12) var(--space-20) var(--space-16);
|
|
max-width: var(--content-max);
|
|
margin: 0 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-8);
|
|
}
|
|
|
|
/* Back-to-Pulse link — a quiet arrow above the page title. */
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
align-self: flex-start;
|
|
font-family: var(--font-sans);
|
|
font-size: var(--text-label-md);
|
|
letter-spacing: var(--tracking-wide);
|
|
text-transform: uppercase;
|
|
color: var(--on-surface-variant);
|
|
text-decoration: none;
|
|
border-bottom: none;
|
|
transition: color var(--duration-fast) var(--ease-standard);
|
|
}
|
|
.back-link:hover { color: var(--pigment-terracotta); border-bottom: none; }
|
|
.back-link svg { transition: transform var(--duration-fast) var(--ease-standard); }
|
|
.back-link:hover svg { transform: translateX(-2px); }
|
|
|
|
.head { max-width: 46rem; }
|
|
.head-eyebrow {
|
|
letter-spacing: var(--tracking-wider);
|
|
text-transform: uppercase;
|
|
color: var(--on-surface-variant);
|
|
margin-bottom: var(--space-3);
|
|
}
|
|
.head-title {
|
|
font-family: var(--font-serif);
|
|
font-weight: 400;
|
|
font-size: var(--text-display-md);
|
|
letter-spacing: var(--tracking-tight);
|
|
line-height: var(--leading-tight);
|
|
color: var(--on-surface);
|
|
margin: 0;
|
|
}
|
|
.head-sub { color: var(--on-surface-variant); margin-top: var(--space-3); max-width: 32rem; }
|
|
.empty { color: var(--on-surface-muted); }
|
|
|
|
/* Rows are now standalone cards on a slight tonal background, separated by
|
|
whitespace rather than borders (per design system). */
|
|
.d-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-3);
|
|
}
|
|
.d-row { border-bottom: none; }
|
|
|
|
.d-link {
|
|
display: grid;
|
|
grid-template-columns: 180px 1fr 130px;
|
|
gap: var(--space-5);
|
|
padding: var(--space-5) var(--space-5);
|
|
align-items: start;
|
|
text-decoration: none;
|
|
border-bottom: none;
|
|
color: inherit;
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in oklab, var(--surface-card) 55%, transparent);
|
|
transition: background var(--duration-fast) var(--ease-standard);
|
|
}
|
|
.d-link:hover {
|
|
background: var(--surface-card);
|
|
border-bottom: none;
|
|
}
|
|
|
|
.d-byline { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
.d-author-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
.d-author-name {
|
|
font-weight: 600;
|
|
color: var(--on-surface);
|
|
font-size: var(--text-body-sm);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.d-author-role {
|
|
color: var(--on-surface-muted);
|
|
letter-spacing: var(--tracking-wide);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.d-body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
|
|
.d-title-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: var(--space-3);
|
|
flex-wrap: wrap;
|
|
}
|
|
.d-title {
|
|
font-family: var(--font-serif);
|
|
font-weight: 400;
|
|
font-size: 1.25rem;
|
|
line-height: 1.3;
|
|
color: var(--on-surface);
|
|
margin: 0;
|
|
}
|
|
.d-kind-pill {
|
|
background: color-mix(in oklab, var(--pill) 14%, transparent);
|
|
color: var(--pill);
|
|
padding: 2px 9px;
|
|
border-radius: 999px;
|
|
font-family: var(--font-sans);
|
|
font-size: var(--text-label-sm);
|
|
letter-spacing: var(--tracking-wide);
|
|
font-weight: 600;
|
|
}
|
|
.d-excerpt {
|
|
color: var(--on-surface-variant);
|
|
margin: 0;
|
|
line-height: var(--leading-relaxed);
|
|
}
|
|
|
|
.d-date {
|
|
color: var(--on-surface-muted);
|
|
letter-spacing: var(--tracking-wide);
|
|
text-transform: uppercase;
|
|
justify-self: end;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.page { padding: var(--space-8) var(--space-5) var(--space-12); }
|
|
.d-link {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--space-3);
|
|
}
|
|
.d-date { justify-self: start; }
|
|
}
|
|
</style>
|