Skip to content

Type de ressource : Layout Astro (Gabarit de page)
Fichier source : src/layouts/[NomLayout].astro
Statut : [Actif / Déprécié / En développement]

[Description concise du rôle de ce layout dans l’architecture du site]

  • [Objectif principal : ex. fournir la structure HTML globale pour les pages de blog]
  • [Bénéfice pour l’architecture : ex. cohérence visuelle + SEO centralisé]
  • Type de pages : [Blog / Portfolio / Vitrine / Documentation / Toutes]
  • Mode de rendu : [SSG / SSR / Les deux]
  • Consommateurs : [Liste des pages qui utilisent ce layout]
[Page Astro]
│ (import du layout)
▼
[Layout]
│ • Structure HTML globale
│ • Injection des métadonnées SEO
│ • Inclusion des composants globaux (Header, Footer)
│ • Gestion des slots de contenu
▼
[HTML final]
Composant Rôle Fichier
Layout parent Structure HTML de base src/layouts/BaseLayout.astro (si applicable)
Header Navigation globale src/components/ui/Header.astro
Footer Pied de page global src/components/ui/Footer.astro
SEO Component Injection des balises meta src/components/seo/SeoJsonLd.astro
Slot de contenu Zone de contenu dynamique <slot />
src/layouts/[NomLayout].astro
---
import BaseLayout from './BaseLayout.astro';
import Header from '../components/ui/Header.astro';
import Footer from '../components/ui/Footer.astro';
import SeoJsonLd from '../components/seo/SeoJsonLd.astro';
interface Props {
title: string;
description?: string;
ogImage?: string;
canonical?: string;
jsonLd?: object;
}
const {
title,
description,
ogImage,
canonical = Astro.url.href,
jsonLd
} = Astro.props;
---
<BaseLayout
title={title}
description={description}
ogImage={ogImage}
canonical={canonical}
>
<Header />
<main id="main-content">
<slot />
</main>
<Footer />
{jsonLd && <SeoJsonLd data={jsonLd} />}
</BaseLayout>
src/layouts/BlogLayout.astro
---
import BaseLayout from './BaseLayout.astro';
import Header from '../components/ui/Header.astro';
import Footer from '../components/ui/Footer.astro';
import Sidebar from '../components/ui/BlogSidebar.astro';
interface Props {
title: string;
description?: string;
publishedAt?: string;
author?: {
name: string;
image?: string;
};
}
const { title, description, publishedAt, author } = Astro.props;
---
<BaseLayout title={title} description={description}>
<Header />
<div class="blog-layout">
<article class="blog-content">
<header class="blog-header">
<h1>{title}</h1>
{publishedAt && (
<time datetime={publishedAt}>
{new Date(publishedAt).toLocaleDateString('fr-FR')}
</time>
)}
{author && (
<div class="author">
{author.image && <img src={author.image} alt={author.name} />}
<span>{author.name}</span>
</div>
)}
</header>
<slot />
</article>
<Sidebar />
</div>
<Footer />
</BaseLayout>
<style>
.blog-layout {
display: grid;
grid-template-columns: 1fr 300px;
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
@media (max-width: 768px) {
.blog-layout {
grid-template-columns: 1fr;
}
}
</style>

3.3. Variante : Layout Base (structure minimale)

Section titled “3.3. Variante : Layout Base (structure minimale)”
src/layouts/BaseLayout.astro
---
interface Props {
title: string;
description?: string;
ogImage?: string;
canonical?: string;
}
const {
title,
description,
ogImage = '/images/default-og.jpg',
canonical = Astro.url.href
} = Astro.props;
---
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />
<!-- SEO Meta Tags -->
<title>{title}</title>
{description && <meta name="description" content={description} />}
<link rel="canonical" href={canonical} />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
{description && <meta property="og:description" content={description} />}
<meta property="og:image" content={ogImage} />
<meta property="og:url" content={canonical} />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
{description && <meta name="twitter:description" content={description} />}
<meta name="twitter:image" content={ogImage} />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- Styles -->
<link rel="stylesheet" href="/styles/global.css" />
<!-- Analytics (si applicable) -->
<!-- Cloudflare Web Analytics ou autre -->
</head>
<body>
<slot />
</body>
</html>
Prop Type Description Exemple
title string Titre de la page (utilisé pour <title> et OG) "Mon article de blog"
Prop Type Description Valeur par défaut
description string Meta description undefined
ogImage string URL de l’image Open Graph '/images/default-og.jpg'
canonical string URL canonique Astro.url.href
jsonLd object Données JSON-LD pour le structured data undefined
[Autre prop] [Type] [Description] [Valeur]
Slot Description Obligatoire
default Contenu principal de la page ✅
sidebar Contenu de la sidebar (si applicable) ❌
head Contenu supplémentaire dans <head> ❌
Balise Source Fallback
<title> props.title -
<meta name="description"> props.description -
<link rel="canonical"> props.canonical Astro.url.href
<meta property="og:title"> props.title -
<meta property="og:description"> props.description -
<meta property="og:image"> props.ogImage Image par défaut
<meta property="og:url"> props.canonical Astro.url.href
{jsonLd && (
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
)}

Types de JSON-LD supportés :

  • Organization (global)
  • WebSite + SearchAction
  • Article / BlogPosting
  • CreativeWork / Project
  • ✅ title est toujours obligatoire
  • ✅ description utilise un fallback intelligent depuis les données Sanity si disponible
  • ✅ ogImage utilise l’image principale du contenu si non spécifiée
  • ✅ canonical est toujours généré automatiquement
<body>
<a href="#main-content" class="skip-link">Aller au contenu principal</a>
<Header />
<main id="main-content">
<slot />
</main>
<Footer />
</body>
  • ✅ Skip link vers le contenu principal
  • ✅ Structure HTML sémantique (<header>, <main>, <footer>, <article>)
  • ✅ Attribut lang="fr" sur <html>
  • ✅ Focus visible sur tous les éléments interactifs
  • ✅ Contraste WCAG AA minimum
  • ✅ Navigation clavier complète
/* Le layout utilise les design tokens définis dans tokens.css */
.blog-layout {
max-width: var(--container-max-width, 1200px);
margin: 0 auto;
padding: var(--spacing-lg, 2rem);
font-family: var(--font-family-base);
color: var(--color-text-primary);
background-color: var(--color-bg-primary);
}
/* Mobile-first approach */
.blog-layout {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-md, 1rem);
}
/* Tablet */
@media (min-width: 768px) {
.blog-layout {
grid-template-columns: 1fr 300px;
gap: var(--spacing-lg, 2rem);
}
}
/* Desktop */
@media (min-width: 1024px) {
.blog-layout {
max-width: var(--container-max-width, 1200px);
}
}
/* Support du mode sombre via prefers-color-scheme */
@media (prefers-color-scheme: dark) {
:root {
--color-bg-primary: var(--color-gray-9);
--color-text-primary: var(--color-gray-1);
}
}
/* Ou via classe manuelle */
[data-theme="dark"] {
--color-bg-primary: var(--color-gray-9);
--color-text-primary: var(--color-gray-1);
}
Composant Rôle Hydratation
Header.astro Navigation globale, logo, menu Aucune
MobileNav.astro Menu mobile interactif client:load
Composant Rôle Hydratation
Footer.astro Liens, copyright, réseaux sociaux Aucune
Composant Rôle Hydratation
SeoJsonLd.astro Injection du structured data Aucune
Analytics.astro Scripts analytics (si applicable) client:load
[Autre] [Rôle] [Stratégie]
src/pages/blog/[slug].astro
---
import BlogLayout from '../../layouts/BlogLayout.astro';
import { getSanityClient } from '../../lib/sanity';
export async function getStaticPaths() {
// ... génération des chemins
}
const { slug } = Astro.params;
const post = await getSanityClient(Astro).fetch(/* ... */);
---
<BlogLayout
title={post.title}
description={post.excerpt}
publishedAt={post.publishedAt}
author={post.author}
>
<!-- Contenu de l'article -->
</BlogLayout>
src/pages/contact.astro
---
import MainLayout from '../layouts/MainLayout.astro';
export const prerender = false;
// Logique SSR...
---
<MainLayout title="Contact" description="Formulaire de contact">
<!-- Contenu de la page -->
</MainLayout>
Page Fichier Mode
[Nom de la page] src/pages/[chemin].astro [SSG/SSR]
[Autre page] src/pages/[chemin].astro [SSG/SSR]
  • ✅ HTML statique par défaut (pas de JavaScript sauf si nécessaire)
  • ✅ Images optimisées (WebP/AVIF via Cloudflare)
  • ✅ CSS minimal et critique inline (si applicable)
  • ✅ Fonts préchargées (si applicable)
  • ✅ Pas de JavaScript bloquant sur le chemin critique
Métrique Objectif Stratégie
LCP < 2.5s Images optimisées, fonts préchargées
INP < 200ms JavaScript minimal, îlots ciblés
CLS < 0.1 Dimensions explicites, fonts stables
Type Cache-Control Rationale
HTML (SSG) public, max-age=31536000, immutable Contenu statique
HTML (SSR) public, max-age=60 Contenu semi-dynamique
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
SEO manquant Props non passées Vérifier les props obligatoires
Styles cassés Conflit CSS Vérifier les spécificités et l’ordre
Accessibilité dégradée Structure HTML incorrecte Valider avec axe-core
Performance dégradée JavaScript inutile Vérifier les hydratations
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/
# Validation HTML
npx html-validate src/pages/[page].astro
Terminal window
# Logs de build
npm run build -- --verbose
# Vérification des types
npx astro check
  • ✅ Centraliser la structure HTML globale dans un BaseLayout
  • ✅ Créer des layouts spécialisés pour chaque type de contenu (Blog, Portfolio, etc.)
  • ✅ Utiliser des slots pour la flexibilité
  • ✅ Injecter les métadonnées SEO de manière centralisée
  • ✅ Respecter la structure sémantique HTML
  • ✅ Typer les props avec TypeScript
  • ✅ Utiliser des valeurs par défaut pour les props optionnelles
  • ✅ Valider les props obligatoires
  • ✅ Utiliser les design tokens pour les styles
  • ✅ Séparer le CSS scoped du CSS global
  • ✅ Documenter les props et slots dans ce fichier
  • ✅ Tester l’accessibilité régulièrement
  • ✅ Vérifier les performances avec Lighthouse
  • ✅ Mettre à jour les layouts lors des changements de design
  • [Évolution 1 : ex. Support du mode sombre]
  • [Évolution 2 : ex. Ajout d’un breadcrumbs automatique]
  • [Évolution 3 : ex. Intégration de View Transitions]
  • ❌ Les layouts ne peuvent pas être hydratés (pas de JavaScript côté layout)
  • ❌ Les slots ne supportent pas les props (utiliser des contextes si nécessaire)
  • ❌ Les layouts imbriqués peuvent compliquer la maintenance
  • Documentation Astro : Layouts
  • Documentation Astro : Slots
  • DAT L2 : §3 - Structure de l’Espace de Travail
  • DAT L2 : §10 - Stratégie SEO
  • DAT L2 : §11 - Accessibilité
  • DAT L2 : §12 - Design System
  • Layouts associés :
    • src/layouts/BaseLayout.astro
    • src/layouts/MainLayout.astro
    • src/layouts/BlogLayout.astro
  • Composants utilisés :
    • src/components/ui/Header.astro
    • src/components/ui/Footer.astro
    • src/components/seo/SeoJsonLd.astro
Date Version Description Auteur
YYYY-MM-DD 1.0 Création initiale [Nom]
YYYY-MM-DD 1.1 [Modification] [Nom]
---
**Points clés de ce template :**
- Documente la structure HTML globale et les slots
- Détaille les props et leur typage TypeScript
- Inclut la gestion centralisée du SEO (meta tags, JSON-LD)
- Couvre l'accessibilité (structure sémantique, skip link)
- Fournit l'intégration avec le design system (tokens CSS)
- Sépare clairement les layouts de base et les layouts spécialisés
- Inclut les bonnes pratiques de performance et de maintenance
Edit page

Last updated: