Skip to content

Type de ressource : Layout Astro (Gabarit de page)
Fichier source : src/layouts/BlogPost.astro
Statut : Actif

Layout pour les articles de blog. Fournit la structure HTML complète pour les pages d’articles, incluant l’en-tête, l’image héro, la date de publication, la date de mise à jour, et le contenu de l’article.

  • Fournir une structure cohérente pour tous les articles de blog
  • Afficher les métadonnées de l’article (titre, dates, image)
  • Intégrer les composants globaux (Header, Footer)
  • Optimiser le rendu des images via Astro Assets
  • Type de pages : Blog (articles individuels)
  • Mode de rendu : SSG (via Astro content collections)
  • Consommateurs : src/pages/blog/[...slug].astro
[Page Astro (blog/[...slug].astro)]
│ (import du layout)
▼
[BlogPost Layout]
│ • Structure HTML complète
│ • Injection des métadonnées SEO (via BaseHead)
│ • Inclusion des composants globaux (Header, Footer)
│ • Affichage de l'image héro
│ • Affichage des dates (pubDate, updatedDate)
│ • Gestion du slot de contenu
▼
[HTML final]
Composant Rôle Fichier
BaseHead Métadonnées SEO globales src/components/BaseHead.astro
Header Navigation globale src/components/Header.astro
Footer Pied de page global src/components/Footer.astro
FormattedDate Formatage des dates src/components/FormattedDate.astro
Slot de contenu Zone de contenu dynamique <slot />
---
import { Image } from 'astro:assets';
import type { CollectionEntry } from 'astro:content';
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
import FormattedDate from '../components/FormattedDate.astro';
import Header from '../components/Header.astro';
type Props = CollectionEntry<'blog'>['data'];
const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
---
<html lang="en">
<head>
<BaseHead title={title} description={description} />
<style>
main {
width: calc(100% - 2em);
max-width: 100%;
margin: 0;
}
.hero-image {
width: 100%;
}
.hero-image img {
display: block;
margin: 0 auto;
border-radius: 12px;
box-shadow: var(--box-shadow);
}
.prose {
width: 720px;
max-width: calc(100% - 2em);
margin: auto;
padding: 1em;
color: rgb(var(--gray-dark));
}
.title {
margin-bottom: 1em;
padding: 1em 0;
text-align: center;
line-height: 1;
}
.title h1 {
margin: 0 0 0.5em 0;
}
.date {
margin-bottom: 0.5em;
color: rgb(var(--gray));
}
.last-updated-on {
font-style: italic;
}
</style>
</head>
<body>
<Header />
<main>
<article>
<div class="hero-image">
{heroImage && <Image width={1020} height={510} src={heroImage} alt="" />}
</div>
<div class="prose">
<div class="title">
<div class="date">
<FormattedDate date={pubDate} />
{
updatedDate && (
<div class="last-updated-on">
Last updated on <FormattedDate date={updatedDate} />
</div>
)
}
</div>
<h1>{title}</h1>
<hr />
</div>
<slot />
</div>
</article>
</main>
<Footer />
</body>
</html>
---
import BlogPost from '../../layouts/BlogPost.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<BlogPost {...post.data}>
<Content />
</BlogPost>
Prop Type Description Exemple
title string Titre de l’article "Mon article de blog"
pubDate Date Date de publication new Date('2024-01-15')
Prop Type Description Valeur par défaut
description string Meta description undefined
updatedDate Date Date de mise à jour undefined
heroImage ImageMetadata Image héro de l’article undefined
Slot Description Obligatoire
default Contenu principal de l’article ✅
Balise Source Fallback
<title> props.title -
<meta name="description"> props.description -
Meta tags globaux BaseHead -

Non implémenté - ce layout n’injecte pas de JSON-LD.

  • ✅ title est toujours obligatoire
  • ✅ pubDate est toujours obligatoire
  • ✅ heroImage est optionnel (affichage conditionnel)
  • ✅ updatedDate est optionnel (affichage conditionnel)
<body>
<Header />
<main>
<article>
<div class="hero-image">
<Image />
</div>
<div class="prose">
<div class="title">
<time>
<FormattedDate />
</time>
<h1>{title}</h1>
</div>
<slot />
</div>
</article>
</main>
<Footer />
</body>
  • ✅ Structure HTML sémantique (<header>, <main>, <article>, <footer>)
  • ✅ Attribut lang="en" sur <html>
  • ⚠️ Pas de skip link vers le contenu principal
  • ⚠️ Attribut alt vide pour l’image héro (à corriger)
  • ⚠️ Focus visible non testé
  • ⚠️ Contraste WCAG AA non testé
/* Le layout utilise des variables CSS personnalisées */
.prose {
color: rgb(var(--gray-dark));
}
.hero-image img {
box-shadow: var(--box-shadow);
}
/* Responsive par défaut */
main {
width: calc(100% - 2em);
max-width: 100%;
}
.prose {
width: 720px;
max-width: calc(100% - 2em);
}

Non implémenté - le layout utilise les couleurs par défaut.

Composant Rôle Hydratation
Header.astro Navigation globale, logo, menu Aucune
Composant Rôle Hydratation
Footer.astro Liens, copyright, réseaux sociaux Aucune
Composant Rôle Hydratation
BaseHead.astro Métadonnées SEO globales Aucune
FormattedDate.astro Formatage des dates Aucune
---
import BlogPost from '../../layouts/BlogPost.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<BlogPost {...post.data}>
<Content />
</BlogPost>
Page Fichier Mode
Blog post src/pages/blog/[...slug].astro SSG
  • ✅ HTML statique par défaut (pas de JavaScript)
  • ✅ Images optimisées via Astro Assets (width, height spécifiés)
  • ✅ CSS scoped minimal
  • ✅ Pas de JavaScript bloquant
Métrique Objectif Stratégie
LCP < 2.5s Images optimisées via Astro Assets
INP < 200ms Pas de JavaScript
CLS < 0.1 Dimensions explicites des images
Type Cache-Control Rationale
HTML (SSG) public, max-age=31536000, immutable Contenu statique
CSS/JS public, max-age=31536000, immutable Assets versionnés
Symptôme Cause probable Solution
Layout non appliqué Import incorrect Vérifier le chemin d’import
Image non affichée heroImage manquant Vérifier que l’image est dans la collection
Dates incorrectes Format de date invalide Vérifier que les dates sont des objets Date valides
Styles cassés Variables CSS non définies Vérifier les variables CSS personnalisées
Terminal window
# Test local
npm run dev
# Vérification de l'accessibilité
npx axe http://localhost:4321/
# Vérification des performances
npx lighthouse http://localhost:4321/
Terminal window
# Logs de build
npm run build -- --verbose
# Vérification des types
npx astro check
  • ✅ Centraliser la structure HTML dans le layout
  • ✅ Utiliser Astro Assets pour l’optimisation des images
  • ✅ Utiliser les composants globaux (Header, Footer)
  • ✅ Séparer le CSS scoped du CSS global
  • ✅ Typer les props avec TypeScript
  • ✅ Utiliser des valeurs par défaut pour les props optionnelles
  • ✅ Affichage conditionnel pour les éléments optionnels (heroImage, updatedDate)
  • ✅ Mettre à jour les styles si nécessaire
  • ✅ Tester l’accessibilité régulièrement
  • ⚠️ Ajouter un skip link pour l’accessibilité
  • ⚠️ Corriger l’attribut alt vide pour l’image héro
  • Ajout d’un skip link pour l’accessibilité
  • Correction de l’attribut alt pour l’image héro
  • Ajout du structured data (JSON-LD) pour les articles
  • Support du mode sombre
  • Ajout d’un breadcrumbs automatique
  • ❌ Pas de skip link
  • ❌ Attribut alt vide pour l’image héro
  • ❌ Pas de structured data (JSON-LD)
  • ❌ Pas de mode sombre
  • ❌ Locale hardcodée (‘en’)
  • Documentation Astro : Layouts
  • Documentation Astro : Astro Assets
  • Documentation Astro : Content Collections
  • Layouts associés : Aucun (layout indépendant)
  • Composants utilisés :
    • src/components/BaseHead.astro
    • src/components/Header.astro
    • src/components/Footer.astro
    • src/components/FormattedDate.astro
Date Version Description Auteur
2026-09-15 1.0 Création initiale Devin
Edit page

Last updated: