Put the blog where people look for it

The blog was reachable only from a link in the footer, which is to say
hardly at all. Once there is a post:

- "Blog" joins the header menu, on desktop and in the mobile menu, on
  every page.
- The landing shows the three latest posts under "Novedades", with a
  link to the rest.

The post card moves into its own component, post-card, shared by the
blog (image beside the text) and the landing (image above).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pigui
2026-09-29 23:02:55 +02:00
parent 5ea9914583
commit e5d91d673a
8 changed files with 224 additions and 116 deletions

View File

@@ -136,6 +136,7 @@ export interface Copy {
readMore: string;
published: string;
feed: string;
seeAll: string;
};
service: {
eyebrow: string;
@@ -326,6 +327,7 @@ const es: Copy = {
readMore: 'Leer',
published: 'Publicado el',
feed: 'Suscríbete por RSS',
seeAll: 'Ver todas las novedades',
},
service: {
eyebrow: 'Servicio',
@@ -529,6 +531,7 @@ const en: Copy = {
readMore: 'Read',
published: 'Published on',
feed: 'Subscribe via RSS',
seeAll: 'See all the news',
},
service: {
eyebrow: 'Service',
@@ -732,6 +735,7 @@ const ca: Copy = {
readMore: 'Llegir',
published: 'Publicat el',
feed: "Subscriu-t'hi per RSS",
seeAll: 'Veure totes les novetats',
},
service: {
eyebrow: 'Servei',

View File

@@ -23,7 +23,8 @@ import type {
PublicTechnology,
} from './api.types';
import { BLOG_CATEGORY, byPublication, isBlogPost } from './blog';
import { resolveRoute, type RouteMatch } from './routes';
import { copyFor } from './copy';
import { listingPath, resolveRoute, type RouteMatch } from './routes';
/** The record, tagged with its type so the template can switch on it. */
export type RoutedRecord =
@@ -132,13 +133,15 @@ async function fetchRecord(route: RouteMatch): Promise<RoutedRecord | null> {
/**
* Featured pages go in the header; the rest are one click away in the footer.
* Blog posts are neither: they have the blog.
* Blog posts are neither: they have the blog, which joins the header as soon
* as there is a post in it — a blog reachable only from the footer is one
* nobody finds.
*/
export function pageNavLinks(
pages: PublicPage[],
locale: Locale,
): { href: string; label: string }[] {
return pages
const featured = pages
.filter(
(page) =>
page.is_featured &&
@@ -150,4 +153,16 @@ export function pageNavLinks(
href: page.path as string,
label: page.title as string,
}));
const hasPosts = pages.some((page) => isBlogPost(page, locale));
return hasPosts
? [
...featured,
{
href: listingPath('blog', locale),
label: copyFor(locale).blog.eyebrow,
},
]
: featured;
}