Retour au catalogue

Careers Benefits Visual

Avantages affiches en cartes visuelles avec icones et animations de revelation au scroll.

careersmedium Both Responsive a11y
playfulelegantboldsaasuniversalgrid
Theme
"use client";

import { motion } from "framer-motion";
import {
  Laptop,
  GraduationCap,
  HeartPulse,
  Plane,
  Baby,
  Dumbbell,
  PiggyBank,
  Utensils,
} from "lucide-react";
import type { LucideIcon } from "lucide-react";

interface Benefit {
  icon: string;
  title: string;
  description: string;
}

interface CareersBenefitsVisualProps {
  title?: string;
  subtitle?: string;
  benefits?: Benefit[];
}

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

const ICON_MAP: Record<string, LucideIcon> = {
  laptop: Laptop,
  "graduation-cap": GraduationCap,
  "heart-pulse": HeartPulse,
  plane: Plane,
  baby: Baby,
  dumbbell: Dumbbell,
  "piggy-bank": PiggyBank,
  utensils: Utensils,
};

export default function CareersBenefitsVisual({
  title = "Pourquoi nous rejoindre",
  subtitle = "Des avantages concrets.",
  benefits = [],
}: CareersBenefitsVisualProps) {
  return (
    <section style={{ paddingTop: "var(--section-padding-y-lg)", paddingBottom: "var(--section-padding-y-lg)", background: "var(--color-background)" }}>
      <div style={{ maxWidth: "var(--container-max-width)", margin: "0 auto", padding: "0 var(--container-padding-x)" }}>
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true }}
          transition={{ duration: 0.6, ease: EASE }}
          style={{ textAlign: "center", maxWidth: 560, margin: "0 auto 3rem" }}
        >
          <h2 style={{ fontFamily: "var(--font-sans)", fontSize: "clamp(1.75rem, 3vw, 2.5rem)", fontWeight: 700, color: "var(--color-foreground)", marginBottom: "0.75rem" }}>{title}</h2>
          <p style={{ fontSize: "1.0625rem", color: "var(--color-foreground-muted)", lineHeight: 1.7 }}>{subtitle}</p>
        </motion.div>

        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: "1.25rem" }}>
          {benefits.map((b, i) => {
            const Icon = ICON_MAP[b.icon] ?? Laptop;
            return (
              <motion.div
                key={b.title}
                initial={{ opacity: 0, y: 24, scale: 0.96 }}
                whileInView={{ opacity: 1, y: 0, scale: 1 }}
                viewport={{ once: true, margin: "-30px" }}
                transition={{ duration: 0.5, delay: i * 0.05, ease: EASE }}
                whileHover={{ y: -4, transition: { duration: 0.2 } }}
                style={{ padding: "1.5rem", borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border)", background: "var(--color-background-card)", textAlign: "center", cursor: "default" }}
              >
                <motion.div
                  initial={{ scale: 0 }}
                  whileInView={{ scale: 1 }}
                  viewport={{ once: true }}
                  transition={{ duration: 0.4, delay: 0.1 + i * 0.05, ease: EASE, type: "spring", stiffness: 200 }}
                  style={{ width: 52, height: 52, borderRadius: "var(--radius-lg)", background: "var(--color-accent-subtle)", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 1rem" }}
                >
                  <Icon style={{ width: 24, height: 24, color: "var(--color-accent)" }} />
                </motion.div>
                <h3 style={{ fontFamily: "var(--font-sans)", fontSize: "0.9375rem", fontWeight: 700, color: "var(--color-foreground)", marginBottom: "0.4rem" }}>{b.title}</h3>
                <p style={{ fontSize: "0.8125rem", lineHeight: 1.6, color: "var(--color-foreground-muted)" }}>{b.description}</p>
              </motion.div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

Avis

Careers Benefits Visual — React Careers Section — Incubator