- 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
180 lines
9.1 KiB
JavaScript
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>
|
|
);
|
|
} |