[Nom de l'Interface de Recherche]
Type de ressource : Interface Utilisateur Pagefind (Barre de recherche / Résultats / Filtres)
Fichier source :src/components/ui/SearchBar.astroou composant associé
Statut : [Actif / Déprécié / En développement]
1. Vue dâensemble
Section titled â1. Vue dâensembleâ1.1. Description
Section titled â1.1. Descriptionâ[Description concise du rĂŽle de cette interface de recherche dans lâexpĂ©rience utilisateur]
1.2. Objectif
Section titled â1.2. Objectifâ- [Objectif principal : ex. fournir une recherche unifiĂ©e sur lâensemble du site]
- [BĂ©nĂ©fice pour lâutilisateur : ex. trouver rapidement du contenu dans le blog, le portfolio ou la documentation]
1.3. PérimÚtre concerné
Section titled â1.3. PĂ©rimĂštre concernĂ©â- Couverture : [Vitrine / Blog / Portfolio / Documentation / Tout]
- Emplacement : [Header global / Sidebar / Page dédiée / Modal]
- Type dâinterface : [Pagefind UI natif / Composant custom / Les deux]
- Cohérence Starlight : [Index partagé / Index séparé mais cohérent]
2. Architecture et flux
Section titled â2. Architecture et fluxâ2.1. Diagramme de flux
Section titled â2.1. Diagramme de fluxâ[Utilisateur] â (saisie dans la barre de recherche) âŒ[Composant SearchBar] â âą Initialisation Pagefind â âą Chargement de l'index chunkĂ© â âą Recherche cĂŽtĂ© client âŒ[Pagefind (client-side)] â âą Matching des termes â âą Application des filtres â âą Ranking des rĂ©sultats âŒ[Affichage des rĂ©sultats] â âą Distinction visuelle par type â âą Badges (Doc, Blog, Portfolio, Page) â âą Pagination âŒ[Utilisateur] â (clic sur un rĂ©sultat) âŒ[Navigation vers la page]2.2. Composants impliquĂ©s
Section titled â2.2. Composants impliquĂ©sâ| Composant | RĂŽle | Fichier |
|---|---|---|
SearchBar.astro |
Interface de recherche globale | src/components/ui/SearchBar.astro |
SearchResults.astro |
Affichage des résultats | src/components/ui/SearchResults.astro |
SearchFilters.astro |
Filtres par type de contenu | src/components/ui/SearchFilters.astro |
| Pagefind UI | BibliothĂšque JS de recherche | pagefind-ui (npm) |
| Index Pagefind | Index chunké généré au build | dist/_pagefind/ |
3. Implémentation
Section titled â3. ImplĂ©mentationâ3.1. Variante : Pagefind UI natif
Section titled â3.1. Variante : Pagefind UI natifâ<div id="search"></div>
<script> import { Search } from 'pagefind-ui';
window.addEventListener('DOMContentLoaded', () => { new Search({ element: '#search', pageSize: 10, showSubResults: true, showImages: false, processTerm: (term) => { // Normalisation des termes de recherche return term.toLowerCase().trim(); }, processResult: (result) => { // Personnalisation des résultats const type = result.meta?.type || 'page'; const badge = { docs: 'Doc', blog: 'Blog', portfolio: 'Portfolio', page: 'Page', }[type] || 'Page';
return { ...result, badge, }; }, }); });</script>
<style> /* Personnalisation des styles Pagefind UI */ :global(.pagefind-ui) { --pagefind-ui-primary: var(--color-primary, #3b82f6); --pagefind-ui-text: var(--color-text-primary, #1f2937); --pagefind-ui-background: var(--color-bg-primary, #ffffff); --pagefind-ui-border: var(--color-border, #e5e7eb); --pagefind-ui-tag: var(--color-gray-2, #e5e7eb); }
:global(.pagefind-ui__search-input) { width: 100%; padding: 0.75rem 1rem; border: 1px solid var(--pagefind-ui-border); border-radius: 0.5rem; font-size: 1rem; }
:global(.pagefind-ui__result) { padding: 1rem; border-bottom: 1px solid var(--pagefind-ui-border); }
:global(.pagefind-ui__result-title) { font-size: 1.125rem; font-weight: 600; color: var(--pagefind-ui-primary); }</style>3.2. Variante : Composant custom (contrĂŽle total)
Section titled â3.2. Variante : Composant custom (contrĂŽle total)â---import SearchResults from './SearchResults.astro';import SearchFilters from './SearchFilters.astro';
interface Props { placeholder?: string; showFilters?: boolean;}
const { placeholder = 'Rechercher...', showFilters = true } = Astro.props;---
<div class="search-container" data-pagefind-ignore="all"> <div class="search-input-wrapper"> <input type="search" id="search-input" placeholder={placeholder} class="search-input" autocomplete="off" /> <svg class="search-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> </svg> </div>
{showFilters && <SearchFilters />}
<div id="search-results"> <SearchResults /> </div></div>
<script> import type * as Pagefind from 'pagefind-ui';
let pagefind: typeof Pagefind | null = null; let currentQuery = ''; let activeFilters: string[] = [];
const searchInput = document.getElementById('search-input') as HTMLInputElement; const resultsContainer = document.getElementById('search-results')!;
// Initialisation de Pagefind async function initPagefind() { if (pagefind) return; // @ts-ignore pagefind = await import('/pagefind/pagefind.js'); await pagefind.init(); }
// Recherche async function performSearch(query: string) { if (!query || query.length < 2) { resultsContainer.innerHTML = ''; return; }
await initPagefind();
const options: any = { filters: activeFilters.length > 0 ? { type: activeFilters } : undefined, };
const search = await pagefind!.search(query, options); const results = await search.results.slice(0, 10); // Limite à 10 résultats
renderResults(results); }
// Rendu des résultats async function renderResults(results: any[]) { if (results.length === 0) { resultsContainer.innerHTML = '<p class="no-results">Aucun résultat trouvé</p>'; return; }
const html = await Promise.all( results.map(async (result) => { const data = await result.data(); const type = data.meta?.type || 'page'; const badge = { docs: 'Doc', blog: 'Blog', portfolio: 'Portfolio', page: 'Page', }[type] || 'Page';
return ` <article class="search-result"> <span class="badge badge-${type}">${badge}</span> <h3 class="result-title"> <a href="${data.url}">${data.meta?.title || data.url}</a> </h3> <p class="result-excerpt">${data.excerpt || ''}</p> </article> `; }) );
resultsContainer.innerHTML = html.join(''); }
// Event listeners searchInput?.addEventListener('input', (e) => { const query = (e.target as HTMLInputElement).value; currentQuery = query; performSearch(query); });
// Debounce let searchTimeout: number; searchInput?.addEventListener('input', (e) => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { performSearch((e.target as HTMLInputElement).value); }, 300); });</script>
<style> .search-container { max-width: 600px; margin: 0 auto; }
.search-input-wrapper { position: relative; }
.search-input { width: 100%; padding: 0.75rem 1rem 0.75rem 2.5rem; border: 1px solid var(--color-border, #e5e7eb); border-radius: 0.5rem; font-size: 1rem; }
.search-icon { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); width: 1.25rem; height: 1.25rem; color: var(--color-gray-5, #6b7280); }
.search-result { padding: 1rem; border-bottom: 1px solid var(--color-border, #e5e7eb); }
.badge { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600; margin-bottom: 0.5rem; }
.badge-docs { background-color: #dbeafe; color: #1e40af; }
.badge-blog { background-color: #d1fae5; color: #065f46; }
.badge-portfolio { background-color: #fed7aa; color: #9a3412; }
.badge-page { background-color: #e5e7eb; color: #374151; }
.result-title { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; }
.result-title a { color: var(--color-primary, #3b82f6); text-decoration: none; }
.result-title a:hover { text-decoration: underline; }
.result-excerpt { color: var(--color-gray-6, #4b5563); font-size: 0.875rem; line-height: 1.5; }
.no-results { text-align: center; padding: 2rem; color: var(--color-gray-5, #6b7280); }</style>3.3. Composant de filtres
Section titled â3.3. Composant de filtresâ<div class="search-filters"> <label class="filter-label"> <input type="checkbox" value="docs" checked /> <span class="filter-badge badge-docs">Doc</span> Documentation </label>
<label class="filter-label"> <input type="checkbox" value="blog" checked /> <span class="filter-badge badge-blog">Blog</span> Blog </label>
<label class="filter-label"> <input type="checkbox" value="portfolio" checked /> <span class="filter-badge badge-portfolio">Portfolio</span> Portfolio </label>
<label class="filter-label"> <input type="checkbox" value="page" checked /> <span class="filter-badge badge-page">Page</span> Pages </label></div>
<script> const filterCheckboxes = document.querySelectorAll<HTMLInputElement>('.search-filters input[type="checkbox"]');
filterCheckboxes.forEach((checkbox) => { checkbox.addEventListener('change', () => { const activeFilters = Array.from(filterCheckboxes) .filter((cb) => cb.checked) .map((cb) => cb.value);
// Déclencher une nouvelle recherche avec les filtres actifs window.dispatchEvent(new CustomEvent('search-filters-changed', { detail: { filters: activeFilters }, })); }); });</script>
<style> .search-filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }
.filter-label { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border: 1px solid var(--color-border, #e5e7eb); border-radius: 0.5rem; cursor: pointer; transition: all 0.2s; }
.filter-label:hover { background-color: var(--color-gray-1, #f3f4f6); }
.filter-label input[type="checkbox"] { width: 1rem; height: 1rem; }
.filter-badge { font-size: 0.75rem; padding: 0.125rem 0.375rem; }</style>4. Intégration avec Starlight
Section titled â4. IntĂ©gration avec Starlightâ4.1. CohĂ©rence de lâexpĂ©rience
Section titled â4.1. CohĂ©rence de lâexpĂ©rienceâ- â Starlight utilise nativement Pagefind pour sa propre barre de recherche
- â Lâobjectif est de partager le mĂȘme index (ou des index trĂšs proches)
- â Les rĂ©sultats de documentation sont distinguĂ©s par le badge âDocâ
- â LâexpĂ©rience de recherche est homogĂšne sur tout le site
4.2. Configuration Starlight
Section titled â4.2. Configuration Starlightâimport starlight from '@astrojs/starlight';
export default defineConfig({ integrations: [ starlight({ title: 'Documentation Technique', // Starlight utilise Pagefind nativement // Pas de configuration supplémentaire nécessaire }), ],});4.3. Personnalisation du thÚme Starlight (optionnel)
Section titled â4.3. Personnalisation du thĂšme Starlight (optionnel)â/* Aligner les styles de recherche Starlight avec le reste du site */:root { --sl-color-accent: var(--color-primary, #3b82f6);}
starlight-search { --sl-search-border: var(--color-border, #e5e7eb); --sl-search-background: var(--color-bg-primary, #ffffff);}5. Distinction visuelle des résultats
Section titled â5. Distinction visuelle des rĂ©sultatsâ5.1. Matrice des badges
Section titled â5.1. Matrice des badgesâ| Type de contenu | Badge | Couleur | Rationale |
|---|---|---|---|
| Documentation | âDocâ | Bleu (#dbeafe / #1e40af) | Contenu technique |
| Article de blog | âBlogâ | Vert (#d1fae5 / #065f46) | Contenu Ă©ditorial |
| Projet portfolio | âPortfolioâ | Orange (#fed7aa / #9a3412) | Contenu showcase |
| Page vitrine | âPageâ | Gris (#e5e7eb / #374151) | Contenu institutionnel |
5.2. Implémentation via métadonnées Pagefind
Section titled â5.2. ImplĂ©mentation via mĂ©tadonnĂ©es Pagefindâ---title: "Guide d'installation"# MĂ©tadonnĂ©es Pagefind pour distinction visuellepagefind: meta: type: "docs"------title: "Mon article de blog"pagefind: meta: type: "blog"------title: "Projet Alpha"pagefind: meta: type: "portfolio"---5.3. Extraction du type dans le composant
Section titled â5.3. Extraction du type dans le composantâ// Dans le composant de rechercheconst type = result.meta?.type || 'page';const badge = { docs: 'Doc', blog: 'Blog', portfolio: 'Portfolio', page: 'Page',}[type] || 'Page';6. AccessibilitĂ© (a11y)
Section titled â6. AccessibilitĂ© (a11y)â6.1. ConformitĂ© WCAG 2.1 AA
Section titled â6.1. ConformitĂ© WCAG 2.1 AAâ| CritĂšre | ImplĂ©mentation | Statut |
|---|---|---|
| 1.3.1 Info and Relationships | Label explicite pour le champ de recherche | â |
| 2.1.1 Keyboard | Navigation clavier complĂšte (Tab, Enter, Escape) | â |
| 2.4.3 Focus Order | Ordre de focus logique (input â filtres â rĂ©sultats) | â |
| 2.4.7 Focus Visible | Focus visible sur tous les Ă©lĂ©ments interactifs | â |
| 3.3.2 Labels or Instructions | Placeholder descriptif + label visible | â |
| 4.1.2 Name, Role, Value | Attributs ARIA appropriés (aria-label, aria-live) |
â |
6.2. Attributs ARIA utilisés
Section titled â6.2. Attributs ARIA utilisĂ©sâ<!-- Champ de recherche --><input type="search" id="search-input" aria-label="Rechercher dans le site" placeholder="Rechercher..." autocomplete="off"/>
<!-- Conteneur de résultats (annoncé aux lecteurs d'écran) --><div id="search-results" role="region" aria-label="Résultats de recherche" aria-live="polite"> <!-- Résultats --></div>
<!-- Filtres --><fieldset aria-label="Filtres de recherche"> <legend class="sr-only">Filtrer par type de contenu</legend> <!-- Checkboxes --></fieldset>6.3. Navigation clavier
Section titled â6.3. Navigation clavierâ| Touche | Action |
|---|---|
Tab |
Navigation vers le champ de recherche, puis les filtres, puis les résultats |
Enter |
Validation de la recherche ou navigation vers un résultat |
Escape |
Fermeture de la recherche (si modal) |
FlĂšches |
Navigation dans les résultats (si implémenté) |
7. Performance et optimisation
Section titled â7. Performance et optimisationâ7.1. Chargement de lâindex
Section titled â7.1. Chargement de lâindexâ- â Lâindex Pagefind est chunkĂ© (chargĂ© Ă la demande)
- â
Seul le script principal (
pagefind.js) est chargĂ© initialement (~10 KB) - â Les fragments dâindex sont chargĂ©s au fur et Ă mesure des recherches
- â Pas de chargement de lâindex complet au dĂ©marrage
7.2. Optimisations appliquées
Section titled â7.2. Optimisations appliquĂ©esâ- â Debounce sur lâinput (300ms) pour Ă©viter les recherches excessives
- â Limite de 10 rĂ©sultats affichĂ©s (pagination si nĂ©cessaire)
- â Pas dâimages dans les rĂ©sultats (rĂ©duit la bande passante)
- â Index chunkĂ© (faible bande passante)
- â Recherche cĂŽtĂ© client (pas dâappel API)
7.3. Core Web Vitals
Section titled â7.3. Core Web Vitalsâ| MĂ©trique | Objectif | StratĂ©gie |
|---|---|---|
| LCP | < 2.5s | Pas dâimpact (recherche post-chargement) |
| INP | < 200ms | Debounce + recherche rapide |
| CLS | < 0.1 | Dimensions fixes pour le conteneur de résultats |
7.4. Taille des assets
Section titled â7.4. Taille des assetsâ| Asset | Taille | Rationale |
|---|---|---|
pagefind.js |
~10 KB | Script principal |
pagefind-ui.css |
~5 KB | Styles UI (optionnel) |
| Fragments dâindex | ~1-5 MB total | ChunkĂ©, chargĂ© Ă la demande |
8. Débogage et troubleshooting
Section titled â8. DĂ©bogage et troubleshootingâ8.1. ProblĂšmes courants
Section titled â8.1. ProblĂšmes courantsâ| SymptĂŽme | Cause probable | Solution |
|---|---|---|
| Aucun résultat | Index non généré | Vérifier que npm run build a été exécuté |
| Résultats non pertinents | Ranking mal configuré | Ajuster les poids dans pagefind.yml |
| Filtres non fonctionnels | Métadonnées manquantes | Ajouter pagefind.meta.type dans le frontmatter |
| Recherche lente | Index trop volumineux | Exclure les éléments non pertinents |
| Styles cassés | Conflit CSS | Vérifier les spécificités et le scope |
| Starlight non indexĂ© | Configuration incorrecte | VĂ©rifier lâintĂ©gration Starlight |
8.2. Tests manuels
Section titled â8.2. Tests manuelsâ# GĂ©nĂ©ration de l'indexnpm run build
# Vérification de l'indexls -lh dist/_pagefind/
# Test de la recherche en localnpm run preview# Ouvrir http://localhost:4321 et tester la recherche
# Inspection de l'index (debug)npx pagefind --source dist --verbose8.3. Logs et monitoring
Section titled â8.3. Logs et monitoringâ# Logs de dĂ©veloppementnpm run dev
# Vérification de la taille de l'indexdu -sh dist/_pagefind/
# Test de l'accessibiliténpx axe http://localhost:4321/9. Bonnes pratiques
Section titled â9. Bonnes pratiquesâ9.1. UX
Section titled â9.1. UXâ- â Fournir un feedback immĂ©diat (spinner pendant la recherche)
- â Afficher un message âAucun rĂ©sultatâ si la recherche est vide
- â SuggĂ©rer des termes de recherche populaires (optionnel)
- â Permettre la navigation clavier complĂšte
- â
Annoncer les rĂ©sultats aux lecteurs dâĂ©cran (
aria-live)
9.2. Accessibilité
Section titled â9.2. AccessibilitĂ©â- â Toujours fournir un label explicite pour le champ de recherche
- â Utiliser des attributs ARIA appropriĂ©s
- â Assurer une navigation clavier complĂšte
- â VĂ©rifier le contraste (WCAG AA minimum)
- â Tester avec un lecteur dâĂ©cran
9.3. Performance
Section titled â9.3. Performanceâ- â Utiliser le debounce pour Ă©viter les recherches excessives
- â Limiter le nombre de rĂ©sultats affichĂ©s
- â Charger lâindex de maniĂšre chunkĂ©e
- â Ăviter les images dans les rĂ©sultats
- â Mesurer les Core Web Vitals avec la recherche active
9.4. Maintenance
Section titled â9.4. Maintenanceâ- â Documenter les changements de configuration dans ce fichier
- â Tester la recherche aprĂšs chaque modification majeure
- â VĂ©rifier les filtres et les mĂ©tadonnĂ©es rĂ©guliĂšrement
- â Surveiller la taille de lâindex (objectif < 5 MB)
10. Ăvolutions et roadmap
Section titled â10. Ăvolutions et roadmapâ10.1. AmĂ©liorations planifiĂ©es
Section titled â10.1. AmĂ©liorations planifiĂ©esâ- [Ăvolution 1 : ex. Suggestions de recherche (autocomplete)]
- [Ăvolution 2 : ex. Historique des recherches (localStorage)]
- [Ăvolution 3 : ex. Recherche facette avancĂ©e (par auteur, catĂ©gorie, date)]
- [Ăvolution 4 : ex. Support du multi-langue (i18n)]
10.2. Limitations connues
Section titled â10.2. Limitations connuesâ- â Pagefind ne supporte pas nativement la recherche dynamique (SSR)
- â Lâindex doit ĂȘtre rĂ©gĂ©nĂ©rĂ© Ă chaque build (pas de mise Ă jour incrĂ©mentale)
- â Les filtres sont limitĂ©s aux mĂ©tadonnĂ©es dĂ©finies au build
- â La recherche full-text ne supporte pas nativement les langues non-latines
11. Ressources liées
Section titled â11. Ressources liĂ©esâ- Documentation Pagefind : Search UI
- Documentation Pagefind : Search API
- Documentation astro-pagefind : npm package
- DAT L2 : §8 - Stratégie de Recherche Unifiée (Pagefind)
- DAT L2 : §8.4 - Expérience utilisateur & distinction visuelle des résultats
- DAT L2 : §8.5 - Cohérence avec Starlight
- Fichiers concernés :
src/components/ui/SearchBar.astrosrc/components/ui/SearchResults.astrosrc/components/ui/SearchFilters.astrodist/_pagefind/(généré au build)
12. Historique des modifications
Section titled â12. Historique des modificationsâ| 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 l'interface de recherche unifiée (Pagefind UI natif ou custom)- Détaille la distinction visuelle des résultats par type de contenu (badges)- Inclut l'intégration avec Starlight (index partagé)- Couvre l'accessibilité WCAG 2.1 AA (navigation clavier, ARIA)- Fournit les bonnes pratiques de performance (debounce, index chunké)- Sépare clairement l'implémentation, les filtres, et la personnalisation- Inclut un guide de débogage complet