1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 | ---
/**
* The listing used by every index page on every site.
*
* One component so blog, fav, io and the home page cannot drift apart. Each
* item is a link, a date, a title and a summary; `kicker` carries whatever the
* section wants to say about the kind of thing it is ("photographs", "book",
* "issue 4").
*/
import { formatDate, isoDate } from '../lib/format.js';
export interface ListEntry {
path: string;
title: string;
summary: string;
pubDate: Date;
kicker?: string;
/** Absolute URL, for cross-site listings on the home page. */
href?: string;
}
interface Props {
entries: ListEntry[];
/** Show dates. Off for short cross-section lists on the home page. */
dates?: boolean;
/** Empty-state text. */
empty?: string;
}
const { entries, dates = true, empty = 'Nothing here yet.' } = Astro.props;
---
{
entries.length === 0 ? (
<p class="meta">{empty}</p>
) : (
<ul class="entry-list">
{entries.map((entry) => (
<li>
<a href={entry.href ?? entry.path}>
<h2>{entry.title}</h2>
</a>
<p class="entry-list__summary">{entry.summary}</p>
<p class="meta entry-list__meta">
{entry.kicker && <span class="entry-list__kicker">{entry.kicker}</span>}
{dates && (
<time datetime={isoDate(entry.pubDate)}>{formatDate(entry.pubDate)}</time>
)}
</p>
</li>
))}
</ul>
)
}
<style>
.entry-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--space-l);
}
.entry-list li {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.entry-list a {
text-decoration: none;
color: inherit;
}
.entry-list h2 {
font-family: var(--font-prose);
font-size: var(--step-2);
font-weight: 600;
line-height: var(--leading-tight);
letter-spacing: -0.014em;
text-wrap: balance;
font-optical-sizing: auto;
}
.entry-list a:hover h2 {
color: var(--accent);
}
.entry-list__summary {
font-family: var(--font-prose);
font-size: var(--step-0);
color: var(--ink-muted);
text-wrap: pretty;
max-inline-size: var(--measure);
}
.entry-list__meta {
display: flex;
flex-wrap: wrap;
gap: var(--space-2xs);
font-variant-numeric: oldstyle-nums;
color: var(--ink-faint);
}
.entry-list__kicker::after {
content: ' ·';
}
</style>
|