Retour au catalogue

Careers Values First

Page carrieres mettant les valeurs d'entreprise en avant avant les offres d'emploi.

careersmedium Both Responsive a11y
elegantcorporatesaasuniversalagencystackedgrid
Theme

Créer une section carrières centrée sur les valeurs en React

Une section carrières centrée sur les valeurs affiche les valeurs de l'entreprise dans une grille de cartes à 2 colonnes avant les offres d'emploi. Chaque carte apparaît en translate-Y décalé via whileInView de Framer Motion, et une barre CTA à fond accent ferme la section.

  • Stack : React + Framer Motion 11 + Lucide React, ~93 lignes, zéro dépendance supplémentaire.
  • Animation : stagger déclenché au viewport, chaque carte retarde de 0.06s × index via whileInView avec once:true.
  • Les icônes sont résolues à l'exécution depuis un objet ICON_MAP (Shield, Zap, Heart, BookOpen de Lucide).
  • Accessible : balises h2/h3 sémantiques, contraste de couleur délégué aux tokens CSS, aucun sens véhiculé uniquement par l'animation.
  • Responsive : la grille 2 colonnes tient à toutes les largeurs ; elle se replie naturellement sur les petits écrans via CSS grid.

CareersValuesFirst ouvre une page carrières sur ce que défend l'entreprise avant de montrer les postes. Un bloc titre court précède une grille de cartes à 2 colonnes où chaque valeur reçoit une icône, un titre et une description. Une barre accent pleine largeur en bas relie la section à un CTA offres d'emploi, un pitch culture et une action de conversion dans le même scroll.

Anatomie

La section comporte trois parties empilées verticalement. D'abord un bloc header (max-width 640px) avec un h2 et un paragraphe sous-titre, révélé d'un seul tenant. Ensuite une grille CSS à 2 colonnes égales et 1.5rem d'écart ; chaque carte a du padding, des coins arrondis, un fond card et un cercle décoratif flouté en haut à droite à 5% d'opacité. Dans chaque carte : un conteneur icône 48×48px avec fond accent-subtle, puis un titre h3, puis un paragraphe muted. Enfin, une flex row à fond accent avec le nombre de postes ouverts à gauche et un lien CTA en forme de pilule à droite.

Comment ça marche

Chaque élément animé utilise whileInView de Framer Motion avec viewport={{ once: true }} pour que l'animation se déclenche une seule fois à l'entrée dans l'écran et ne rejoue jamais au scroll vers le haut. Le header s'anime en un seul bloc (opacity 0→1, y 20→0, durée 0.6s). Chaque carte de valeur est retardée de `0.08 + i * 0.06` secondes, créant un stagger naturel de gauche à droite sur les colonnes de la grille. La barre CTA attend 0.3s, entrant après la dernière carte. Toutes les transitions partagent le même ease personnalisé `[0.16, 1, 0.3, 1]`, un ease-out rapide qui paraît réactif plutôt que rebondissant.

Comment le coder en React

  1. Définir l'interface Value et la map d'icônes

    Chaque objet valeur porte une clé icon (string), un titre, une description et un hex couleur utilisé pour le blob décoratif. Résous les chaînes d'icônes en vrais composants Lucide en haut du fichier avec un lookup Record pour que l'API de props reste sérialisable.

    const ICON_MAP: Record<string, LucideIcon> = {
      shield: Shield,
      zap: Zap,
      heart: Heart,
      "book-open": BookOpen,
    };
  2. Animer le header comme un seul bloc

    Englobe le h2 + sous-titre dans une seule motion.div avec initial={{ opacity: 0, y: 20 }} et whileInView={{ opacity: 1, y: 0 }}. Mets viewport={{ once: true }} pour éviter les replays. Une durée de 0.6s avec l'ease personnalisé suffit, pas de stagger ici, le bloc est petit.

    <motion.div
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
    >
  3. Décaler les cartes de valeurs

    Mappe sur le tableau de valeurs et donne à chaque motion.div un délai de `0.08 + i * 0.06`. Avec 4 cartes cela étale l'entrée sur ~0.3s au total, ce qui paraît fluide sans sembler lent. Le blob décoratif est un div de 100px avec borderRadius 50%, positionné en absolu à top:-20 right:-20, utilisant la couleur de la valeur à 5% d'opacité.

    transition={{ duration: 0.5, delay: 0.08 + i * 0.06, ease: EASE }}
  4. Construire la barre CTA accent

    Rends une motion.div avec display:flex, le fond accent et un délai de 0.3s pour qu'elle entre en dernier. À l'intérieur, une flex row contient un bloc texte à gauche (nombre de postes + tagline) et un lien pilule à droite avec la couleur de fond comme fond propre, créant un bouton inversé à partir de l'accent de la section.

    <motion.div
      initial={{ opacity: 0, y: 12 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.5, delay: 0.3, ease: EASE }}
      style={{ background: "var(--color-accent)" }}
    >

Quand l'utiliser

Utilise cette section en tête d'une page carrières ou à propos quand la culture est un différenciateur, startups, agences et éditeurs SaaS qui recrutent sur les valeurs plutôt que sur le salaire. Positionne-la avant les offres d'emploi pour que les visiteurs lisent le pourquoi avant le quoi. Évite-la sur les job boards transactionnels ou les pages où le temps jusqu'au dépôt de candidature prime ; dans ces contextes la profondeur de scroll supplémentaire coûte des conversions.

Utilisé par

  • Stripe, Ouvre sa page carrières avec ses principes de fonctionnement avant les offres d'emploi, établissant la culture avant l'opportunité.
  • Linear, La section valeurs et mode de travail précède le tableau des offres, mettant l'accent sur comment l'équipe fonctionne plutôt que sur les rôles.
  • Notion, Affiche les valeurs fondamentales avec icônes et descriptions courtes au-dessus de la liste des postes sur sa page carrières.

FAQ

Comment ajouter une cinquième carte sans casser la grille à 2 colonnes ?

La grille utilise `repeat(2, 1fr)` donc n'importe quel nombre de cartes la remplit automatiquement, une cinquième carte commence simplement une nouvelle rangée. Pour la centrer sur la dernière rangée, entoure le conteneur de grille d'un parent flex et ajoute `justify-items: center`.

Peut-on ajouter une icône personnalisée absente de l'ICON_MAP ?

Étends le Record ICON_MAP avec n'importe quelle icône Lucide ou ton propre composant SVG, le type est `Record<string, LucideIcon>`, donc tout composant acceptant une prop className ou style convient. Le fallback est Shield si la clé n'est pas trouvée.

Est-ce que whileInView rejoue à chaque fois que l'élément entre dans le viewport ?

Pas avec `viewport={{ once: true }}`, qui est ce qu'utilise ce composant. L'animation se déclenche une seule fois à la première intersection et jamais ensuite. Supprime le flag once pour qu'elle rejoue à chaque entrée au scroll vers le bas.

Comment brancher le lien CTA sur une ancre de page d'offres réelle ?

Remplace `href="#"` dans la balise anchor par `href="#jobs"` (ou l'id de ta section listings). Passe `ctaText` et `openPositionsCount` comme props depuis ta source de données pour que la barre reflète le nombre réel de postes.

"use client";

import { motion } from "framer-motion";
import { Shield, Zap, Heart, BookOpen, ArrowRight } from "lucide-react";
import type { LucideIcon } from "lucide-react";

interface Value {
  icon: string;
  title: string;
  description: string;
  color: string;
}

interface CareersValuesFirstProps {
  title?: string;
  subtitle?: string;
  values?: Value[];
  ctaText?: string;
  openPositionsCount?: number;
}

const EASE = [0.16, 1, 0.3, 1] as const;

Code complet réservé à Pro

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

Passer en Pro, 9,99€/mois

Avis

Section valeurs React pour page carrières, grille + Framer