Files
gaea21-v2-web/assets/components/Impact.jsx
MohamedLoum1001 d3b9318dee feat(ui): modularisation du css et fidélisation des composants à la maquette figma
- découpage du css par composant (navbar, hero, footer, global)
- ajustement pixel-perfect du footer (newsletter full-bleed, alignements, typographies)
- documentation et commentaires détaillés du code react et css
2026-09-23 17:27:05 +02:00

180 lines
9.1 KiB
JavaScript

// 1. IMPORTATIONS DES MODULES ET ASSETS
import React, { useState } from 'react';
// Webpack Encore gère l'empreinte de hachage (hash) et le chemin public de l'image de fond
import impactBg from '../img/impact.png';
/**
* 2. STRUCTURE DE DONNÉES STATIQUE : DIAPOSITIVES DU CARROUSEL
* Externaliser les données hors du composant évite de recréer ce tableau
* en mémoire à chaque nouveau cycle de rendu (re-render) du composant.
*
* Le tableau est structuré sous forme de tableau de diapositives (slides).
* SLIDES[0] contient le groupe actif affiché à l'écran.
*/
const SLIDES = [
[
{
value: '3000 +',
label: 'personnes accompagnées',
size: 'w-16 h-16 sm:w-20 sm:h-20', // Taille standard des pastilles latérales
},
{
value: '94% - 97%',
label: 'de réussites en insertion',
size: 'w-24 h-24 sm:w-28 sm:h-28', // Pastille centrale plus imposante pour créer une hiérarchie visuelle
},
{
value: '20 ANS',
label: 'de recherches appliquées',
size: 'w-16 h-16 sm:w-20 sm:h-20', // Taille standard
},
],
];
/**
* 3. COMPOSANT PRINCIPAL : Impact
* Présente la section "NOTRE IMPACT" avec :
* - Un arrière-plan immersif en pleine largeur
* - 3 indicateurs chiffrés majeurs avec pastilles colorées
* - Des contrôles de carrousel (flèches gauche/droite et indicateurs à points)
*/
export default function Impact() {
/**
* ÉTAT LOCAL (State) : Indice du point actif de pagination
* Initialisé à 2 pour correspondre au point central parmi les 5 points du design Figma.
*/
const [currentSlide, setCurrentSlide] = useState(2);
return (
/*
Balise sémantique <section> :
- relative : point d'ancrage pour positionner l'overlay sombre absolu
- w-full : occupe toute la largeur de l'écran (full bleed)
- py-24 sm:py-32 : padding vertical généreux pour donner de l'espace à la section
- px-6 sm:px-12 : padding horizontal adaptatif
- bg-cover bg-center : l'image remplit tout l'arrière-plan sans déformation et reste centrée
- overflow-hidden : empêche tout débordement lié aux animations de zoom
*/
<section
className="relative w-full py-24 sm:py-32 px-6 sm:px-12 bg-cover bg-center overflow-hidden"
style={{ backgroundImage: `url("${impactBg}")` }}
>
{/*
CALQUE D'OVERLAY SOMBRE :
- absolute inset-0 : couvre 100% de la surface de la section
- bg-black/40 : voile noir à 40% d'opacité pour garantir la lisibilité des textes blancs
- pointer-events-none : empêche ce filtre de capturer les clics
*/}
<div className="absolute inset-0 bg-black/40 pointer-events-none" />
{/*
CONTENEUR DE CONTENU :
- relative z-10 : positionne le contenu au-dessus du voile sombre
- max-w-[1400px] mx-auto : largeur maximale cadrée et centrée
- text-center text-white : typographie blanche centrée par défaut
*/}
<div className="relative z-10 max-w-[1400px] mx-auto text-center text-white">
{/* ====================================================================
A. EN-TÊTE : SURTITRE & TITRE PRINCIPAL
==================================================================== */}
{/* Surtitre en majuscules avec espacement étendu (tracking-wide) */}
<span className="text-xs sm:text-sm font-bold tracking-[0.25em] uppercase text-white/90 block mb-2">
NOTRE IMPACT
</span>
{/* Titre descriptif sobre avec marge basse importante avant les chiffres */}
<h2 className="text-lg sm:text-xl font-normal text-white/95 mb-16 sm:mb-20">
Des résultats concrets et durables
</h2>
{/* ====================================================================
B. ZONE CENTRALE : FLÈCHE GAUCHE + 3 MÉTRIQUES + FLÈCHE DROITE
- flex items-center justify-between : alignement horizontal sur une seule ligne
==================================================================== */}
<div className="flex items-center justify-between gap-4 sm:gap-8">
{/* Bouton de navigation gauche (Précédent) */}
<button
type="button"
className="w-10 h-10 sm:w-12 sm:h-12 rounded-full border border-white/60 flex items-center justify-center text-white hover:bg-white/20 transition-colors shrink-0"
aria-label="Précédent"
>
{/* Icône flèche retour */}
<svg className="w-5 h-5 sm:w-6 sm:h-6" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>
</button>
{/*
Grille des 3 métriques chiffrées :
- 1 colonne sur mobile (grid-cols-1)
- 3 colonnes à partir des écrans moyens (md:grid-cols-3)
- items-end : aligne les éléments par le bas pour absorber la différence de taille du cercle central
- flex-1 max-w-4xl mx-auto : occupe l'espace central disponible de façon équilibrée
*/}
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-8 flex-1 items-end max-w-4xl mx-auto">
{SLIDES[0].map((item, index) => (
<div key={index} className="flex flex-col items-center">
{/*
Pastille ronde vert olive (#9da74d) :
- Taille dynamique issue de la donnée (item.size)
- shadow-[...] : ombre portée sombre accentuant la profondeur
- hover:scale-105 : micro-interaction au survol
*/}
<div
className={`${item.size} rounded-full bg-[#9da74d] mb-6 shadow-[0_4px_16px_rgba(0,0,0,0.35)] transition-transform duration-300 hover:scale-105`}
/>
{/* Valeur numérique mise en avant (ex: 3000 +, 94% - 97%) */}
<span className="text-3xl sm:text-4xl md:text-5xl font-normal tracking-tight text-white mb-2">
{item.value}
</span>
{/* Libellé descriptif sous le chiffre */}
<span className="text-xs sm:text-sm font-light text-white/85 max-w-[200px]">
{item.label}
</span>
</div>
))}
</div>
{/* Bouton de navigation droit (Suivant) */}
<button
type="button"
className="w-10 h-10 sm:w-12 sm:h-12 rounded-full border border-white/60 flex items-center justify-center text-white hover:bg-white/20 transition-colors shrink-0"
aria-label="Suivant"
>
{/* Icône flèche suivant */}
<svg className="w-5 h-5 sm:w-6 sm:h-6" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>
</button>
</div>
{/* ====================================================================
C. INDICATEURS DE PAGINATION (CAROUSEL DOTS)
- 5 points horizontaux centrés en bas de section
==================================================================== */}
<div className="flex justify-center items-center gap-2 mt-16 sm:mt-20">
{[0, 1, 2, 3, 4].map((dot) => (
<span
key={dot}
/*
Style dynamique du point :
- Si c'est le point actif (dot === currentSlide) : couleur vert olive (#a3ad53) et taille augmentée (scale-125)
- Sinon : blanc translucide (bg-white/50)
*/
className={`w-2.5 h-2.5 rounded-full transition-all duration-200 ${dot === currentSlide
? 'bg-[#a3ad53] scale-125'
: 'bg-white/50'
}`}
/>
))}
</div>
</div>
</section>
);
}