Skip to content

[Nom du Composant]

[Nom du Composant]

Documentation technique

Type de ressource : Composant Astro (UI atomique / Îlot interactif)
Fichier source : src/components/[ui|interactive]/[NomComposant].astro
Statut : [Actif / Déprécié / En développement]

1. Vue d’ensemble

1.1. Description

[Description concise du rôle de ce composant dans l’interface utilisateur]

1.2. Objectif

  • [Objectif principal : ex. afficher un bouton d’action réutilisable]
  • [Bénéfice pour l’UX ou l’architecture]

1.3. Caractéristiques

Propriété Valeur
Type [UI atomique / Îlot interactif]
Hydratation [Aucune / client:load / client:visible / client:idle]
Framework JS [Aucun / React / Vue / Svelte / Web Component]
Accessibilité [WCAG AA conforme / Partiellement conforme / Non testé]
Responsive [Mobile-first / Desktop-first]

2. Architecture et structure

2.1. Emplacement dans l’arborescence

src/components/
├── ui/ # Composants atomiques (HTML + CSS purs)
│ ├── Button.astro
│ ├── Badge.astro
│ └── [NomComposant].astro
└── interactive/ # Îlots dynamiques (React/Vue/Svelte)
├── MobileNav.tsx
├── Carousel.tsx
└── [NomComposant].tsx

2.2. Composants dépendants

Composant parent Fichier Relation
[Nom du parent] src/components/[chemin] [Composition / Wrapper]

2.3. Composants enfants utilisés

Composant enfant Fichier Rôle
[Nom de l’enfant] src/components/[chemin] [Description]

3. Implémentation

3.1. Variante : Composant UI atomique (HTML + CSS purs)1/2

3.1. Variante : Composant UI atomique (HTML + CSS purs)2/2

src/components/ui/[NomComposant].astro
---
interface Props {
label: string;
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
href?: string;
onClick?: () => void;
}
const {
label,
variant = 'primary',
size = 'md',
disabled = false,
href,
onClick,
} = Astro.props;
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-500',
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`;
---
{href ? (
<a
href={href}
class={classes}
aria-disabled={disabled}
>
{label}
</a>
) : (
<button
class={classes}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
)}

3.2. Variante : Îlot interactif (React)1/2

3.2. Variante : Îlot interactif (React)2/2

src/components/interactive/[NomComposant].tsx
import { useState } from 'react';
interface Props {
items: Array<{
id: string;
title: string;
content: string;
}>;
autoPlay?: boolean;
interval?: number;
}
export default function Carousel({ items, autoPlay = true, interval = 5000 }: Props) {
const [currentIndex, setCurrentIndex] = useState(0);
const nextSlide = () => {
setCurrentIndex((prev) => (prev + 1) % items.length);
};
const prevSlide = () => {
setCurrentIndex((prev) => (prev - 1 + items.length) % items.length);
};
// Auto-play
useState(() => {
if (autoPlay) {
const timer = setInterval(nextSlide, interval);
return () => clearInterval(timer);
}
}, [autoPlay, interval]);
return (
<div className="carousel" role="region" aria-label="Carrousel" aria-roledescription="carousel">
<div className="carousel-track">
{items.map((item, index) => (
<div
key={item.id}
className={`carousel-slide ${index === currentIndex ? 'active' : ''}`}
role="group"
aria-roledescription="slide"
aria-label={`${index + 1} sur ${items.length}`}
aria-hidden={index !== currentIndex}
>
<h3>{item.title}</h3>
<p>{item.content}</p>
</div>
))}
</div>
<button
onClick={prevSlide}
aria-label="Diapositive précédente"
className="carousel-prev"
>
←
</button>
<button
onClick={nextSlide}
aria-label="Diapositive suivante"
className="carousel-next"
>
→
</button>
<div className="carousel-indicators">
{items.map((_, index) => (
<button
key={index}
onClick={() => setCurrentIndex(index)}
className={`indicator ${index === currentIndex ? 'active' : ''}`}
aria-label={`Aller à la diapositive ${index + 1}`}
aria-current={index === currentIndex}
/>
))}
</div>
</div>
);
}

3.3. Utilisation dans une page

src/pages/[page].astro
---
import Button from '../components/ui/Button.astro';
import Carousel from '../components/interactive/Carousel';
const items = [
{ id: '1', title: 'Témoignage 1', content: 'Contenu...' },
{ id: '2', title: 'Témoignage 2', content: 'Contenu...' },
];
---
<!-- Composant UI (pas d'hydratation) -->
<Button label="En savoir plus" variant="primary" href="/about" />
<!-- Îlot interactif (hydratation différée) -->
<Carousel client:visible items={items} autoPlay={true} interval={5000} />

4. Props et interface

4.1. Props obligatoires

Prop Type Description Exemple
label string Texte affiché dans le bouton "En savoir plus"

4.2. Props optionnelles

Prop Type Description Valeur par défaut
variant 'primary' | 'secondary' | 'outline' Style visuel du bouton 'primary'
size 'sm' | 'md' | 'lg' Taille du bouton 'md'
disabled boolean État désactivé false
href string URL de redirection (rend le composant en <a>) undefined
onClick () => void Callback au clic undefined

4.3. Slots (si applicable)

Slot Description Obligatoire
default Contenu personnalisé (remplace label) ❌
icon Icône à afficher avant le label ❌

5. Stratégie d’hydratation

5.1. Politique d’hydratation

Type de composant Stratégie Rationale
Composants UI atomiques Aucune HTML + CSS purs, pas d’interaction
Navigation mobile client:load Critique pour l’UX immédiate
Carrousel témoignages client:visible Chargement différé (plus bas dans la page)
Formulaire de contact client:load Interaction immédiate requise
Graphiques / Charts client:visible Chargement différé

5.2. Directives Astro utilisées

<!-- Aucune hydratation (HTML pur) -->
<Button label="Cliquez ici" />
<!-- Hydratation immédiate -->
<MobileNav client:load items={navItems} />
<!-- Hydratation au scroll (viewport) -->
<Carousel client:visible items={testimonials} />
<!-- Hydratation au repos (idle) -->
<Analytics client:idle />

5.3. Impact sur les performances

Métrique Sans hydratation Avec client:load Avec client:visible
HTML size +2 KB +2 KB +2 KB
JS size 0 KB +15 KB +15 KB (chargé plus tard)
TTI (Time to Interactive) Immédiat +50ms +200ms (après scroll)
INP N/A Impacté Impacté

6. Styles et design system

6.1. Utilisation des design tokens

/* Le composant utilise les design tokens définis dans tokens.css */
.button {
background-color: var(--color-primary, #3b82f6);
color: var(--color-text-inverse, #ffffff);
padding: var(--spacing-md, 1rem) var(--spacing-lg, 1.5rem);
border-radius: var(--radius-md, 0.5rem);
font-family: var(--font-family-base, system-ui);
font-size: var(--font-size-base, 1rem);
font-weight: var(--font-weight-medium, 500);
transition: background-color var(--transition-fast, 150ms) ease;
}
.button:hover {
background-color: var(--color-primary-dark, #2563eb);
}
.button:focus-visible {
outline: 2px solid var(--color-focus, #3b82f6);
outline-offset: 2px;
}

6.2. Responsive design

/* Mobile-first approach */
.button {
width: 100%;
padding: var(--spacing-sm, 0.75rem) var(--spacing-md, 1rem);
font-size: var(--font-size-sm, 0.875rem);
}
/* Tablet et desktop */
@media (min-width: 768px) {
.button {
width: auto;
padding: var(--spacing-md, 1rem) var(--spacing-lg, 1.5rem);
font-size: var(--font-size-base, 1rem);
}
}

6.3. Mode sombre (si applicable)

/* Support automatique via prefers-color-scheme */
@media (prefers-color-scheme: dark) {
.button {
background-color: var(--color-primary-dark, #2563eb);
}
}
/* Ou via classe manuelle */
[data-theme="dark"] .button {
background-color: var(--color-primary-dark, #2563eb);
}

7. Accessibilité (a11y)

7.1. Conformité WCAG 2.1 AA1/2

Critère Implémentation Statut
1.1.1 Non-text Content Attribut alt obligatoire pour les images ✅
1.3.1 Info and Relationships Structure HTML sémantique (<button>, <a>) ✅
1.4.3 Contrast (Minimum) Contraste texte/fond ≥ 4.5:1 ✅
1.4.11 Non-text Contrast Contraste éléments interactifs ≥ 3:1 ✅

7.1. Conformité WCAG 2.1 AA2/2

Critère Implémentation Statut
2.1.1 Keyboard Navigation clavier complète (Tab, Enter, Space) ✅
2.1.2 No Keyboard Trap Pas de piège au clavier ✅
2.4.7 Focus Visible Focus visible sur tous les éléments interactifs ✅
4.1.2 Name, Role, Value Attributs ARIA appropriés (aria-label, aria-expanded) ✅

7.2. Attributs ARIA utilisés

<!-- Bouton -->
<button
aria-label="Fermer la modale"
aria-expanded="false"
aria-controls="modal-content"
>
×
</button>
<!-- Carrousel -->
<div
role="region"
aria-label="Carrousel de témoignages"
aria-roledescription="carousel"
>
<div
role="group"
aria-roledescription="slide"
aria-label="1 sur 5"
aria-hidden="false"
>
<!-- Contenu -->
</div>
</div>
<!-- Navigation -->
<nav aria-label="Navigation principale">
<ul role="menubar">
<li role="none">
<a role="menuitem" href="/">Accueil</a>
</li>
</ul>
</nav>

7.3. Navigation clavier

Touche Action
Tab Navigation vers l’élément suivant
Shift + Tab Navigation vers l’élément précédent
Enter / Space Activation du bouton ou du lien
Escape Fermeture de la modale ou du menu
Flèches Navigation dans les menus / carrousels

7.4. Tests d’accessibilité

Terminal window
# Test automatique avec axe-core
npx axe http://localhost:4321/
# Test avec Lighthouse
npx lighthouse http://localhost:4321/ --view
# Test manuel
# 1. Navigation complète au clavier (Tab, Shift+Tab)
# 2. Vérification du focus visible
# 3. Test avec un lecteur d'écran (VoiceOver, NVDA)
# 4. Vérification du contraste avec WebAIM Contrast Checker

8. Performance et optimisation

8.1. Optimisations appliquées

  • ✅ HTML statique par défaut (pas de JavaScript sauf si nécessaire)
  • ✅ CSS scoped (pas de fuite de styles)
  • ✅ Images optimisées (WebP/AVIF via Cloudflare)
  • ✅ Pas de JavaScript bloquant sur le chemin critique
  • ✅ Lazy loading des composants interactifs (client:visible)

8.2. Core Web Vitals

Métrique Objectif Stratégie
LCP < 2.5s Images optimisées, pas de JS bloquant
INP < 200ms JavaScript minimal, îlots ciblés
CLS < 0.1 Dimensions explicites, pas de layout shift

8.3. Taille du composant

Type HTML CSS JS (hydraté) Total
UI atomique 2 KB 1 KB 0 KB 3 KB
Îlot interactif 2 KB 1 KB 15 KB 18 KB

9. Débogage et troubleshooting

9.1. Problèmes courants

Symptôme Cause probable Solution
Composant non visible Import incorrect Vérifier le chemin d’import
Styles cassés Conflit CSS Vérifier les spécificités et le scope
Hydratation ne fonctionne pas Directive manquante Ajouter client:load ou client:visible
Accessibilité dégradée Structure HTML incorrecte Valider avec axe-core
Performance dégradée JavaScript inutile Vérifier les hydratations
Focus invisible CSS outline: none Retirer ou remplacer par focus-visible

9.2. Tests manuels

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/
# Test responsive
# Utiliser les DevTools du navigateur (mode responsive)
# Test des interactions
# Vérifier tous les états (hover, focus, active, disabled)

9.3. Logs et monitoring

Terminal window
# Logs de développement
npm run dev
# Vérification des types
npx astro check
# Analyse du bundle
npm run build -- --analyze

10. Bonnes pratiques

10.1. Architecture

  • ✅ Séparer les composants UI (atomiques) des composants interactifs (îlots)
  • ✅ Utiliser les design tokens pour tous les styles (couleurs, spacing, typographie)
  • ✅ Typer les props avec TypeScript
  • ✅ Valider les props obligatoires
  • ✅ Utiliser des valeurs par défaut pour les props optionnelles

10.2. Accessibilité

  • ✅ Toujours utiliser des éléments HTML sémantiques (<button>, <a>, <nav>)
  • ✅ Fournir des attributs ARIA appropriés
  • ✅ Assurer une navigation clavier complète
  • ✅ Vérifier le contraste (WCAG AA minimum)
  • ✅ Tester avec un lecteur d’écran

10.3. Performance

  • ✅ Éviter l’hydratation sauf si interaction requise
  • ✅ Utiliser client:visible pour les composants bas de page
  • ✅ Optimiser les images (WebP/AVIF, dimensions explicites)
  • ✅ Éviter le JavaScript bloquant sur le chemin critique
  • ✅ Mesurer les Core Web Vitals régulièrement

10.4. Maintenance

  • ✅ Documenter les props et slots dans ce fichier
  • ✅ Créer des exemples d’utilisation
  • ✅ Tester l’accessibilité régulièrement
  • ✅ Vérifier les performances avec Lighthouse
  • ✅ Mettre à jour les composants lors des changements de design

11. Évolutions et roadmap

11.1. Améliorations planifiées

  • [Évolution 1 : ex. Support du mode sombre]
  • [Évolution 2 : ex. Ajout de variantes de taille]
  • [Évolution 3 : ex. Intégration d’animations]

11.2. Limitations connues

  • ❌ Les composants UI ne peuvent pas avoir d’état (pas de JavaScript)
  • ❌ Les îlots interactifs augmentent la taille du bundle JS
  • ❌ L’hydratation client:visible nécessite l’Intersection Observer API

12. Ressources liées1/2

12. Ressources liées2/2

  • DAT L2 : §12 - Design System
  • Composants associés :
    • src/components/ui/[ComposantParent].astro
    • src/components/interactive/[ComposantEnfant].tsx
  • Design tokens : src/styles/tokens.css

13. Historique des modifications1/2

Date Version Description Auteur
YYYY-MM-DD 1.0 Création initiale [Nom]
YYYY-MM-DD 1.1 [Modification] [Nom]

13. Historique des modifications2/2

---
**Points clés de ce template :**
- Couvre les deux types de composants (UI atomique et îlot interactif)
- Détaille la stratégie d'hydratation (critique pour les performances)
- Inclut la conformité WCAG 2.1 AA (accessibilité)
- Fournit l'intégration avec le design system (tokens CSS)
- Sépare clairement les props, les slots, et les exemples d'utilisation
- Inclut les bonnes pratiques de performance et de maintenance
- Documente l'impact sur les Core Web Vitals
Switch to normal mode

Contents