Retour au catalogue

About Quote Wall

Mur de citations et principes fondateurs en layout disperse.

aboutmedium Both Responsive a11y
editorialelegantagencyportfoliouniversalmasonry
Theme

Créer un mur de citations React en grille masonry avec animations décalées

Un mur de citations React dispose des cartes de tailles variables dans une grille CSS et anime chaque carte au scroll avec whileInView de Framer Motion, en décalant les délais par index et en ajoutant une légère rotation alternée qui revient à zéro, sans bibliothèque masonry JavaScript.

  • Stack : React + Framer Motion + Tailwind v4 + lucide-react (icône Quote), ~67 lignes au total.
  • Moteur de mise en page : CSS grid avec classes col-span (1, 2 ou 3 colonnes), pas de JS masonry.
  • Animation : whileInView avec once:true, stagger 0,08 s par carte, rotation ±2° à l'entrée qui revient à 0.
  • Entièrement thémed : chaque couleur est une CSS custom property (--color-background, --color-accent, --color-border).
  • Accessible : balisage sémantique <section> et <p> ; l'icône Quote est décorative.

About Quote Wall est une section React qui transforme des citations de fondateurs, des valeurs d'entreprise ou des témoignages en une mosaïque vivante. Les cartes existent en trois tailles couvrant différents nombres de colonnes, pour un rendu éditorial magazine sans aucune bibliothèque masonry. Chaque carte arrive légèrement inclinée puis se redresse en se posant.

Anatomie

Le composant se divise en deux parties. En haut, un en-tête centré avec un badge optionnel (pill avec bordure) et un titre h2, enveloppés dans une motion.div whileInView qui monte en fondu. En dessous, une grille CSS `grid-cols-1 md:grid-cols-3 lg:grid-cols-6` dispose les cartes de citations. Chaque carte est une motion.div avec des coins arrondis et une bordure, une icône Quote semi-transparente en haut à gauche, du texte en italique serif, et un bloc d'attribution en bas (nom + rôle optionnel) séparé par une bordure fine.

Comment ça marche

Chaque carte s'initialise avec `opacity: 0`, `y: 20` et une rotation de `+2°` (index pair) ou `-2°` (index impair). Quand elle entre dans le viewport, whileInView l'amène à `opacity: 1`, `y: 0`, `rotate: 0` en 0,5 s. Le stagger est manuel : `delay: i * 0.08`, donc la 8e carte démarre 640 ms après la première, se lisant de gauche à droite comme un dévoilement. L'option viewport `once: true` fait que l'animation ne se déclenche qu'une fois par session, gardant les performances de scroll propres.

Comment le coder en React

  1. Définir la structure de données et la carte des tailles

    Crée une interface QuoteItem avec id, text, author, un rôle optionnel et une taille optionnelle ('small' | 'medium' | 'large'). Associe les tailles à des classes Tailwind col-span et text-size en amont pour garder la boucle de rendu simple.

    const sizeClasses = {
      small:  "col-span-1",
      medium: "col-span-1 md:col-span-2",
      large:  "col-span-1 md:col-span-2 lg:col-span-3",
    };
  2. Construire le conteneur CSS grid

    Utilise `grid-cols-1 md:grid-cols-3 lg:grid-cols-6` avec `auto-rows-auto` pour que les cartes ne prennent que la hauteur dont elles ont besoin. Six colonnes au grand breakpoint permet à une carte 'large' de remplir la moitié de la rangée, 'medium' un tiers, et 'small' un sixième.

    <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-4 auto-rows-auto">
  3. Animer chaque carte avec un stagger et une rotation

    Enveloppe chaque carte dans une motion.div. Règle la rotation initiale à `(i % 2 === 0 ? 1 : -1) * 2` degrés pour que les cartes adjacentes s'inclinent dans des directions opposées à l'entrée. Ramène tout à zéro dans whileInView avec un délai par carte.

    <motion.div
      initial={{ opacity: 0, y: 20, rotate: (i % 2 === 0 ? 1 : -1) * 2 }}
      whileInView={{ opacity: 1, y: 0, rotate: 0 }}
      viewport={{ once: true }}
      transition={{ delay: i * 0.08, duration: 0.5 }}
    >
  4. Composer l'intérieur de la carte

    Place l'icône Quote (lucide-react) à 50% d'opacité comme marqueur décoratif, puis le texte de la citation dans un paragraphe en italique serif, puis un bloc d'attribution séparé par une border-t. Toutes les couleurs référencent des CSS custom properties pour hériter du preset de thème actif.

    <Quote className="h-5 w-5 mb-3" style={{ color: "var(--color-accent)", opacity: 0.5 }} />
    <p className="font-serif italic">&ldquo;{quote.text}&rdquo;</p>
    <div className="mt-4 pt-3 border-t" style={{ borderColor: "var(--color-border)" }}>
      <p className="text-xs font-semibold">{quote.author}</p>
    </div>

Quand l'utiliser

Cette section fonctionne bien comme bloc culturel ou de valeurs sur la page about d'une agence ou d'un portfolio, dans la section 'ce en quoi on croit' d'une startup, ou sur une page produit où la preuve sociale vient des fondateurs plutôt que des utilisateurs. À placer après un paragraphe about narratif. À éviter pour les pages de témoignages à fort volume (20+ avis) où un carousel ou une liste paginée convient mieux, et sur les pages critiques pour la conversion où un texte concentré prime sur la mosaïque.

Utilisé par

  • Notion, Utilise un mur de citations de fondateurs et d'équipe sur sa page about pour transmettre la philosophie de l'entreprise.
  • Basecamp, Présente les valeurs et principes de l'entreprise comme des blocs de citations autonomes dans une mise en page éditoriale dispersée.
  • Mailchimp, Mélange des citations mises en avant et des déclarations de fondateurs dans une grille mosaïque sur ses pages culture et about.

FAQ

Comment contrôler quelles cartes sont larges ?

Règle size: 'medium' ou size: 'large' sur chaque objet QuoteItem de ton tableau de données. Les cartes medium couvrent 2 des 3 colonnes au breakpoint md ; les larges couvrent 3 des 6 au lg. Les petites prennent toujours 1 colonne.

Puis-je l'utiliser pour des témoignages clients plutôt que des citations internes ?

Oui, la structure de données est identique, remplace les valeurs author/role par le nom du client et le titre de son entreprise. Pour une preuve sociale à grande échelle (50+ témoignages), filtre à 5-8 highlights avant de les passer ; la grille ne pagine pas.

Le stagger avec rotation fonctionne-t-il sur mobile ?

L'animation fonctionne bien sur mobile. La grille se replie sur une seule colonne, donc toutes les variantes de taille s'empilent en pleine largeur. La rotation et le stagger restent visibles au scroll.

Comment changer la police pour correspondre à ma marque ?

Le texte des citations utilise l'utilitaire font-serif de Tailwind. Remplace la pile serif globalement via ta config Tailwind ou la règle @font-face pour ta police, aucun changement dans le composant.

"use client";

import React from "react";
import { motion } from "framer-motion";
import { Quote } from "lucide-react";

interface QuoteItem {
  id: string;
  text: string;
  author: string;
  role?: string;
  size?: "small" | "medium" | "large";
}

interface AboutQuoteWallProps {
  badge?: string;
  title?: string;
  quotes?: QuoteItem[];
}

export default function AboutQuoteWall({ badge, title, quotes = [] }: AboutQuoteWallProps) {
  const sizeClasses: Record<string, string> = {

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Avis

Mur de citations React, grille masonry + Framer Motion