summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-21 00:37:34 -0300
committerJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-21 00:37:34 -0300
commitc530d30a74bb521b1c22f0e2a468d9b69c6a2e8e (patch)
treec9230fdf28aa8865ac2066450b84009bc9da0168
parent25375fccfe4477022c0b354c6e1018ff5a07df95 (diff)
feat: blog listing style
Signed-off-by: João Augusto Costa Branco Marado Torres <torres.dev@disroot.org>
-rw-r--r--src/components/DateSelector.astro172
-rw-r--r--src/components/templates/SimplePostList.astro91
-rw-r--r--src/lib/collection/helpers.ts172
-rw-r--r--src/lib/collection/schemas.ts9
-rw-r--r--src/pages/blog/[...date].astro251
-rw-r--r--src/pages/index.astro4
6 files changed, 463 insertions, 236 deletions
diff --git a/src/components/DateSelector.astro b/src/components/DateSelector.astro
index d57919e..ab10fd1 100644
--- a/src/components/DateSelector.astro
+++ b/src/components/DateSelector.astro
@@ -1,6 +1,8 @@
---
+import { defined } from "@utils/anonymous";
+
interface Props {
- date: Date;
+ date?: Date;
years: number[];
months: number[];
days?: number[];
@@ -8,9 +10,10 @@ interface Props {
const { date, years, months, days } = Astro.props;
-const y = date.getFullYear();
-const m = date.getMonth() + 1;
-const d = date.getDate();
+const dateParts = Astro.params.date?.split("/").map(Number);
+const y = dateParts?.[0];
+const m = dateParts?.[1];
+const d = dateParts?.[2];
let yI = 0;
let mI = 0;
let dI = 0;
@@ -19,23 +22,31 @@ const list = new Intl.ListFormat("pt-PT", { type: "unit", style: "narrow" });
const pad = (n: number) => String(n).padStart(2, "0");
---
-<nav>
+<nav class="mute small">
<span role="list">
Anos:{" "}
{
- list.formatToParts(years.map((y) =>
- new Intl.DateTimeFormat("pt-PT", { year: "2-digit" }).format(
- new Date(
- Date.UTC(
- y,
- 0,
- 1,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
+ list.formatToParts(
+ years.sort().map((y) =>
+ new Intl.DateTimeFormat("pt-PT", { year: "numeric" }).format(
+ new Date(
+ Date.UTC(
+ y,
+ 0,
+ 3,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
+ ),
),
- ),
- )
- )).map(({ type, value }: { type: string; value: string }) => {
+ )
+ ),
+ ).map(({ type, value }: { type: string; value: string }) => {
switch (type) {
case "element": {
const year = years[yI++];
@@ -53,67 +64,39 @@ const pad = (n: number) => String(n).padStart(2, "0");
})
}
</span>
- <br />
- <span role="list">
- Meses:{" "}
- {
- list.formatToParts(months.map((m) =>
- new Intl.DateTimeFormat("pt-PT", { month: "short" }).format(
- new Date(
- Date.UTC(
- y,
- m - 1,
- 1,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
- ),
- ),
- )
- )).map(({ type, value }: { type: string; value: string }) => {
- switch (type) {
- case "element": {
- const month = months[mI++];
- return (
- <span role="listitem"><a
- class:list={[{ active: month === m }]}
- href={`/blog/${y}/${pad(month)}`}
- >{value}</a></span>
- );
- }
- case "literal": {
- return <span>{value}</span>;
- }
- }
- })
- }
- </span>
{
- days &&
- (
+ y && (
<><br /><span role="list">
- Dias:{" "}
+ Meses:{" "}
{
- list.formatToParts(days.map((d) => {
- return new Intl.DateTimeFormat("pt-PT", { day: "numeric" })
- .format(
+ list.formatToParts(
+ months.sort().map((m) =>
+ new Intl.DateTimeFormat("pt-PT", { month: "short" }).format(
new Date(
Date.UTC(
y,
m - 1,
- d,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
+ 3,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
),
),
- );
- })).map(({ type, value }: { type: string; value: string }) => {
+ )
+ ),
+ ).map(({ type, value }: { type: string; value: string }) => {
switch (type) {
case "element": {
- const day = days[dI++];
+ const month = months[mI++];
return (
<span role="listitem"><a
- class:list={[{ active: day === d }]}
- href={`/blog/${y}/${pad(m)}/${pad(d)}`}
+ class:list={[{ active: month === m }]}
+ href={`/blog/${y}/${pad(month)}`}
>{value}</a></span>
);
}
@@ -126,10 +109,69 @@ const pad = (n: number) => String(n).padStart(2, "0");
</span></>
)
}
+ {
+ days &&
+ (
+ <><br /><span role="list">
+ Dias:{" "}
+ {
+ y && m &&
+ list.formatToParts(
+ days.sort().map((d) => {
+ return new Intl.DateTimeFormat("pt-PT", {
+ day: "numeric",
+ })
+ .format(
+ new Date(
+ Date.UTC(
+ y,
+ m - 1,
+ d,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
+ ),
+ ),
+ );
+ }),
+ ).map(
+ ({ type, value }: { type: string; value: string }) => {
+ switch (type) {
+ case "element": {
+ const day = days[dI++];
+ return (
+ <span role="listitem"><a
+ class:list={[{ active: day === d }]}
+ href={`/blog/${y}/${pad(m)}/${pad(day)}`}
+ >{value}</a></span>
+ );
+ }
+ case "literal": {
+ return <span>{value}</span>;
+ }
+ }
+ },
+ )
+ }
+ </span></>
+ )
+ }
</nav>
<style>
+ nav {
+ padding-block: calc(var(--size-2) * 1em);
+ }
a.active {
font-weight: bolder;
}
+
+ a:hover {
+ color: var(--color-active);
+ }
</style>
diff --git a/src/components/templates/SimplePostList.astro b/src/components/templates/SimplePostList.astro
index 164b32b..6d05a1f 100644
--- a/src/components/templates/SimplePostList.astro
+++ b/src/components/templates/SimplePostList.astro
@@ -1,25 +1,45 @@
---
import Date from "@components/organisms/Date.astro";
import KeywordsList from "@components/organisms/KeywordsList.astro";
-import { getFirstUserID, getLastUpdate } from "@lib/collection/helpers";
-import type { Original } from "@lib/collection/schemas";
-import type { z } from "astro:content";
+import {
+ getFirstUserID,
+ getLastUpdate,
+ getTranslationOriginal,
+ isMicro,
+ isTranslation,
+} from "@lib/collection/helpers";
import type { CollectionEntry } from "astro:content";
interface Props {
- posts: (CollectionEntry<"blog"> & { data: z.infer<typeof Original> })[];
+ posts: CollectionEntry<"blog">[];
+ small?: boolean;
+ dateOptions?: Intl.DateTimeFormatOptions;
}
-const { posts } = Astro.props;
+const {
+ posts,
+ small = false,
+ dateOptions = { year: "numeric", month: "long", day: "numeric" },
+} = Astro.props;
---
-<ol>
+<ol class:list={{ "remove-nums": !small }}>
{
- await Promise.all(posts.map(async (post) => {
- const { id, data } = post;
- const { title, description, lang, keywords } = data;
- const { name, email, entity } = await getFirstUserID(post);
- const display = name ?? email ?? entity;
- return (
+ await Promise.all(
+ posts.map(
+ async (post) => {
+ const { id, data } = post;
+ const { title, lang } = data;
+ const description = isMicro(post)
+ ? post.rendered?.html
+ : ("description" in data ? data.description : undefined);
+ const keywords = isTranslation(post)
+ ? await getTranslationOriginal(post).then((x) =>
+ x?.data?.keywords
+ )
+ : ("keywords" in data ? data.keywords : undefined);
+ const { name, email, entity } = await getFirstUserID(post);
+ const display = name ?? email ?? entity;
+ return (
<li>
<article
itemprop="blogPost"
@@ -29,36 +49,39 @@ const { posts } = Astro.props;
<h3>
<a href={`/blog/read/${id}`} itemprop="headline name">{title}</a>
</h3>
- <div itemprop="abstract">
- {
- description &&
- description.split("\n\n").map((paragraph) => (
- <p class="small">{paragraph}</p>
- ))
- }
- </div>
+ {
+ description && (
+ <div itemprop="abstract">
+ {
+ isMicro(post)
+ ? <Fragment set:html={description} />
+ : description.split("\n\n").map((
+ paragraph,
+ ) => <p class:list={{ small }}>{paragraph}</p>)
+ }
+ </div>
+ )
+ }
<footer class="small">
<Date
date={getLastUpdate(post)}
locales={lang}
- options={{
- year: "numeric",
- month: "long",
- day: "numeric",
- }}
+ options={dateOptions}
itemprop="dateModified"
/><span
itemprop="author"
itemscope
itemtype="https://schema.org/Person"
><span itemprop="alternateName">{display}</span></span>
- <KeywordsList {keywords} />
+ {Array.isArray(keywords) && <KeywordsList {keywords} />}
</footer>
</article>
</li>
);
- }))
+ },
+ ),
+ )
}
</ol>
@@ -82,6 +105,20 @@ const { posts } = Astro.props;
}
}
}
+
+ & > li:not(:first-of-type) {
+ border-block-start: 1px solid var(--color-dark);
+ }
+ }
+
+ ol.remove-nums {
+ margin-inline-start: 0;
+ & > li {
+ margin-inline-end: calc(var(--size-7) * 1em);
+ & > article {
+ padding-inline-end: 0;
+ }
+ }
}
@media (width >= 40rem) {
diff --git a/src/lib/collection/helpers.ts b/src/lib/collection/helpers.ts
index 587180b..0bfd3c8 100644
--- a/src/lib/collection/helpers.ts
+++ b/src/lib/collection/helpers.ts
@@ -13,6 +13,12 @@ import { createKeyFromArmor } from "../pgp/create.ts";
import { getUserIDsFromKey } from "../pgp/user.ts";
import type { UserIDPacket } from "openpgp";
import { getCollection } from "astro:content";
+import {
+ GetStaticPaths,
+ GetStaticPathsItem,
+ GetStaticPathsResult,
+} from "astro";
+import { getEntry } from "astro:content";
export function getLastUpdate({ data }: CollectionEntry<"blog">): Date {
return data.dateUpdated ?? data.dateCreated;
@@ -108,3 +114,169 @@ export const isTranslation = (
export const isMicro = (
entry: CollectionEntry<"blog">,
): entry is MicroEntry => entry.data.kind === "micro";
+
+export async function getTranslationOriginal(
+ translation: TranslationEntry,
+): Promise<OriginalEntry | undefined> {
+ if (!isTranslation(translation)) {
+ throw new Error();
+ }
+ return await getEntry(translation.data.translationOf);
+}
+
+export const datePaths = (async (): Promise<
+ {
+ params: { date?: string };
+ props: {
+ posts: OriginalEntry[];
+ next?: string;
+ previous?: string;
+ years: number[];
+ months: number[];
+ days?: number[];
+ };
+ }[]
+> => {
+ const posts = await fromPosts(isEntry, identity);
+
+ const archive = {
+ years: new Set<number>(),
+ monthsByYear: new Map<string, Set<number>>(),
+ daysByMonth: new Map<string, Set<number>>(),
+ postsByDate: new Map<string, typeof posts>(),
+ sortedDates: [] as string[],
+ };
+
+ const getYMD = (date: Date) => {
+ const y = date.getFullYear();
+ const m = date.getMonth() + 1;
+ const d = date.getDate();
+ return { y, m, d };
+ };
+
+ for (const post of posts) {
+ const { y, m, d } = getYMD(post.data.dateCreated);
+
+ archive.years.add(y);
+
+ const months = archive.monthsByYear.get(y.toString());
+ if (months === undefined) {
+ archive.monthsByYear.set(y.toString(), new Set([m]));
+ } else {
+ months.add(m);
+ }
+
+ const ym = `${y}/${String(m).padStart(2, "0")}`;
+ const days = archive.daysByMonth.get(ym);
+ if (days === undefined) {
+ archive.daysByMonth.set(ym, new Set([d]));
+ } else {
+ days.add(d);
+ }
+
+ const ymd = `${ym}/${String(d).padStart(2, "0")}`;
+ const posts = archive.postsByDate.get(ymd);
+ if (posts === undefined) {
+ archive.postsByDate.set(ymd, [post]);
+ } else {
+ posts.push(post);
+ }
+ }
+
+ archive.sortedDates = Array.from(archive.postsByDate.keys()).sort();
+
+ const paths: {
+ params: { date?: string };
+ props: {
+ posts: OriginalEntry[];
+ next?: string;
+ previous?: string;
+ years: number[];
+ months: number[];
+ days?: number[];
+ };
+ }[] = [] satisfies GetStaticPathsItem[];
+
+ const sortedYears = Array.from(archive.years).sort();
+
+ const lastYear = Math.max(...sortedYears.map(Number));
+ paths.push({
+ params: { date: undefined },
+ props: {
+ posts: posts.filter((p) => p.data.dateCreated.getFullYear() === lastYear),
+ next: undefined,
+ previous: sortedYears?.[sortedYears.length - 2]?.toString(),
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(lastYear.toString()) ?? []),
+ },
+ });
+
+ for (const y of sortedYears) {
+ const yearPosts = posts.filter((p) =>
+ p.data.dateCreated.getFullYear() === Number(y)
+ );
+ const idx = sortedYears.indexOf(y);
+ paths.push({
+ params: { date: y.toString() },
+ props: {
+ posts: yearPosts,
+ next: sortedYears?.[idx + 1]?.toString(),
+ previous: sortedYears?.[idx - 1]?.toString(),
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(y.toString()) ?? []),
+ },
+ });
+ }
+
+ const allMonths = Array.from(archive.monthsByYear.entries())
+ .flatMap(([year, mset]) =>
+ Array.from(mset).map((m) => `${year}/${String(m).padStart(2, "0")}`)
+ )
+ .sort();
+
+ for (const [y, months] of archive.monthsByYear) {
+ const sortedMonths = Array.from(months).sort();
+ for (const m of sortedMonths) {
+ const monthPosts = posts.filter((p) => {
+ const d = p.data.dateCreated;
+ return (
+ d.getFullYear() === Number(y) &&
+ d.getMonth() + 1 === m
+ );
+ });
+
+ const ym = `${y}/${String(m).padStart(2, "0")}`;
+ const idx = allMonths.indexOf(ym);
+
+ paths.push({
+ params: { date: ym },
+ props: {
+ posts: monthPosts,
+ next: allMonths?.[idx + 1],
+ previous: allMonths?.[idx - 1],
+ years: sortedYears,
+ months: Array.from(months).sort(),
+ days: Array.from(archive.daysByMonth.get(ym) ?? []).sort(),
+ },
+ });
+ }
+ }
+
+ for (let i = 0; i < archive.sortedDates.length; i++) {
+ const ymd = archive.sortedDates[i];
+ const [y, m] = ymd.split("/");
+ paths.push({
+ params: { date: ymd },
+ props: {
+ posts: archive.postsByDate.get(ymd) ?? [],
+ next: archive.sortedDates?.[i + 1],
+ previous: archive.sortedDates?.[i - 1],
+ years: sortedYears,
+ months: Array.from(archive.monthsByYear.get(y) ?? []).sort(),
+ days: Array.from(archive.daysByMonth.get(`${y}/${m}`) ?? []).sort(),
+ },
+ });
+ }
+
+ return paths;
+}) satisfies GetStaticPaths;
diff --git a/src/lib/collection/schemas.ts b/src/lib/collection/schemas.ts
index eca996f..f8a021d 100644
--- a/src/lib/collection/schemas.ts
+++ b/src/lib/collection/schemas.ts
@@ -3,7 +3,14 @@ import { isValidLocale } from "../../utils/lang.ts";
import { get } from "../../utils/anonymous.ts";
import type { CollectionEntry } from "astro:content";
-export const KEYWORDS = ["Portugal", "democracy", "test"] as const;
+export const KEYWORDS = [
+ "Portugal",
+ "democracy",
+ "test",
+ "health",
+ "gym",
+ "diet",
+] as const;
export const KeywordsEnum = z.enum(KEYWORDS);
export const ENTITY_TYPES = ["author", "co-author", "translator"] as const;
diff --git a/src/pages/blog/[...date].astro b/src/pages/blog/[...date].astro
index 1742baa..d66ac8e 100644
--- a/src/pages/blog/[...date].astro
+++ b/src/pages/blog/[...date].astro
@@ -1,161 +1,128 @@
---
import type {
- GetStaticPaths,
InferGetStaticParamsType,
InferGetStaticPropsType,
} from "astro";
-import { getCollection } from "astro:content";
import Base from "@layouts/Base.astro";
import DateSelector from "@components/DateSelector.astro";
-import BlogCard from "@components/BlogCard.astro";
-import { sortLastCreated } from "@lib/collection/helpers";
+import SimplePostList from "@components/templates/SimplePostList.astro";
+import { datePaths, sortLastCreated } from "@lib/collection/helpers";
-export const getStaticPaths = (async () => {
- const posts = await getCollection("blog");
-
- const archive = {
- years: new Set<number>(),
- monthsByYear: new Map<string, Set<number>>(),
- daysByMonth: new Map<string, Set<number>>(),
- postsByDate: new Map<string, typeof posts>(),
- sortedDates: [] as string[],
- };
-
- const getYMD = (date: Date) => {
- const y = date.getFullYear();
- const m = date.getMonth() + 1;
- const d = date.getDate();
- return { y, m, d };
- };
-
- for (const post of posts) {
- const { y, m, d } = getYMD(post.data.dateCreated);
-
- archive.years.add(y);
-
- if (!archive.monthsByYear.has(y.toString())) {
- archive.monthsByYear.set(y.toString(), new Set());
- }
- archive.monthsByYear.get(y.toString())!.add(m);
-
- const ym = `${y}/${String(m).padStart(2, "0")}`;
- if (!archive.daysByMonth.has(ym)) archive.daysByMonth.set(ym, new Set());
- archive.daysByMonth.get(ym)!.add(d);
-
- const ymd = `${ym}/${String(d).padStart(2, "0")}`;
- if (!archive.postsByDate.has(ymd)) archive.postsByDate.set(ymd, []);
- archive.postsByDate.get(ymd)!.push(post);
- }
-
- archive.sortedDates = Array.from(archive.postsByDate.keys()).sort();
-
- const paths = [];
-
- const sortedYears = Array.from(archive.years).sort();
-
- const lastYear = Math.max(...sortedYears.map(Number));
- paths.push({
- params: { year: undefined },
- props: {
- posts: posts.filter((p) =>
- p.data.dateCreated.getFullYear() === lastYear
- ),
- next: undefined,
- previous: sortedYears?.[sortedYears.length - 2],
- years: sortedYears,
- months: Array.from(archive.monthsByYear.get(lastYear.toString()) ?? []),
- },
- });
-
- for (const y of sortedYears) {
- const yearPosts = posts.filter((p) =>
- p.data.dateCreated.getFullYear() === Number(y)
- );
- const idx = sortedYears.indexOf(y);
- paths.push({
- params: { year: y },
- props: {
- posts: yearPosts,
- next: sortedYears?.[idx + 1],
- previous: sortedYears?.[idx - 1],
- years: sortedYears,
- months: Array.from(archive.monthsByYear.get(y.toString()) ?? []),
- },
- });
- }
-
- const allMonths = Array.from(archive.monthsByYear.entries())
- .flatMap(([year, mset]) =>
- Array.from(mset).map((m) => `${year}/${String(m).padStart(2, "0")}`)
- )
- .sort();
-
- for (const [y, months] of archive.monthsByYear) {
- const sortedMonths = Array.from(months).sort();
- for (const m of sortedMonths) {
- const monthPosts = posts.filter((p) => {
- const d = p.data.dateCreated;
- return (
- d.getFullYear() === Number(y) &&
- d.getMonth() + 1 === m
- );
- });
-
- const ym = `${y}/${String(m).padStart(2, "0")}`;
- const idx = allMonths.indexOf(ym);
-
- paths.push({
- params: { year: ym },
- props: {
- posts: monthPosts,
- next: allMonths?.[idx + 1],
- previous: allMonths?.[idx - 1],
- years: sortedYears,
- months: Array.from(months).sort(),
- days: Array.from(archive.daysByMonth.get(ym) ?? []).sort(),
- },
- });
- }
- }
-
- for (let i = 0; i < archive.sortedDates.length; i++) {
- const ymd = archive.sortedDates[i];
- const [y, m] = ymd.split("/");
- paths.push({
- params: { year: ymd },
- props: {
- posts: archive.postsByDate.get(ymd) ?? [],
- next: archive.sortedDates?.[i + 1],
- previous: archive.sortedDates?.[i - 1],
- years: sortedYears,
- months: Array.from(archive.monthsByYear.get(y) ?? []).sort(),
- days: Array.from(archive.daysByMonth.get(`${y}/${m}`) ?? []).sort(),
- },
- });
- }
-
- return paths;
-}) satisfies GetStaticPaths;
+export const getStaticPaths = datePaths;
export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
-const title = "Blog";
-const description = "Latest articles.";
-
let { posts, previous, next, years, months, days } = Astro.props;
posts = posts.sort(sortLastCreated);
-const date = posts[0].data.dateCreated as Date;
+
+const dateParts = Astro.params.date?.split("/").map(Number);
+const y = dateParts?.[0];
+const m = dateParts?.[1] ?? 1;
+const d = dateParts?.[2] ?? 3;
+const date = (y !== undefined) ? new Date(Date.UTC(y, m - 1, d)) : undefined;
+
+const format = date === undefined
+ ? undefined
+ : new Intl.DateTimeFormat("pt-PT", {
+ year: y === undefined ? undefined : "numeric",
+ month: dateParts?.[1] === undefined ? undefined : "long",
+ day: dateParts?.[2] === undefined ? undefined : "numeric",
+ }).format(date);
+const title = "Publicações" + (format !== undefined ? ` -- ${format}` : "");
+const description = "Ultímas publicações" +
+ (format !== undefined ? ` do dia ${format}` : "") + ".";
---
<Base {title} {description}>
- <main>
- <h2>Blogue</h2>
- {date && <DateSelector {date} {years} {months} {days} />}
- {posts.map((post) => <BlogCard {...post} />)}
- <div>
- {previous && <a href={`/blog/${Astro.props.previous}`}>Previous</a>}
- {next && <a href={`/blog/${Astro.props.next}`}>Next</a>}
- </div>
+ <main
+ itemprop="mainContentOfPage"
+ itemscope
+ itemtype="https://schema.org/WebPageElement"
+ >
+ <section
+ id="posts"
+ itemprop="citation"
+ itemscope
+ itemtype="http://schema.org/Blog"
+ >
+ <h2 itemprop="name description">{title}</h2>
+ <DateSelector {date} {years} {months} {days} />
+ {
+ (next || previous) && (
+ <nav>
+ {
+ previous && (
+ <p>
+ &lt; <a href={`/blog/${Astro.props.previous}`}>Anterior</a>
+ </p>
+ )
+ }
+ <span class="small">{format}</span>
+ {
+ next && (
+ <p><a href={`/blog/${Astro.props.next}`}>Próximo</a> &gt;</p>
+ )
+ }
+ </nav>
+ )
+ }
+ <SimplePostList
+ {posts}
+ dateOptions={{
+ weekday: "long",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ timeZoneName: "long",
+ }}
+ />
+ {
+ (next || previous) && (
+ <nav>
+ {
+ previous && (
+ <p>
+ &lt; <a href={`/blog/${Astro.props.previous}`}>Anterior</a>
+ </p>
+ )
+ }
+ <span class="small">{format}</span>
+ {
+ next && (
+ <p><a href={`/blog/${Astro.props.next}`}>Próximo</a> &gt;</p>
+ )
+ }
+ </nav>
+ )
+ }
+ <DateSelector {date} {years} {months} {days} />
+ </section>
</main>
</Base>
+
+<style>
+ nav {
+ display: flex;
+ align-items: center;
+ padding-block: calc(var(--size-2) * 1em);
+ gap: calc(var(--size-2) * 1em);
+ justify-content: center;
+ & > p {
+ border-radius: calc(var(--size-0) * 1em);
+ border-width: 1px;
+ box-shadow: 0 1px 2px var(--color-light);
+ font-size: calc(var(--size-3) * 1rem);
+ font-weight: 500;
+ padding-inline: calc(var(--size-3) * 1em);
+ padding-block: calc(var(--size-2) * 1em);
+ display: inline-flex;
+ gap: calc(var(--size-2) * 1em);
+ align-items: center;
+ justify-content: center;
+ height: 2ch;
+ }
+ }
+</style>
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 5a52100..bb4108c 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -56,7 +56,9 @@ const micro = await fromPosts(
>
<h2 itemprop="name description">Últimas publicações atualizadas</h2>
{micro && <div id="last-micro"><MicroBlog {...micro} /></div>}
- <div id="last-originals"><SimplePostList posts={originals} /></div>
+ <div id="last-originals">
+ <SimplePostList posts={originals} small />
+ </div>
</section>
)
}