Retour au catalogue

Services Cards Grid

Grille de cartes de services avec icones et descriptions. Layout responsive 3 colonnes.

servicessimple Both Responsive a11y
minimalcorporateuniversalgrid
Theme
"use client";

import React from "react";
import { motion } from "framer-motion";
import * as LucideIcons from "lucide-react";

interface ServiceItem {
  id: string;
  title: string;
  description: string;
  icon?: string;
}

interface ServicesCardsGridProps {
  badge?: string;
  title?: string;
  subtitle?: string;
  services: ServiceItem[];
}

function getIcon(name?: string) {
  if (!name) return null;
  const Icon = (LucideIcons as unknown as Record<string, React.ElementType>)[name];
  return Icon || null;
}

export default function ServicesCardsGrid({
  badge,
  title,
  subtitle,
  services,
}: ServicesCardsGridProps) {
  return (
    <section
      className="py-[var(--section-padding-y,6rem)]"
      style={{ backgroundColor: "var(--color-background)" }}
    >
      <div className="mx-auto max-w-7xl px-[var(--container-padding-x,1.5rem)]">
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.5 }}
          className="text-center max-w-2xl mx-auto"
        >
          {badge && (
            <span
              className="inline-block text-xs font-medium tracking-wider uppercase px-3 py-1 rounded-full border"
              style={{
                color: "var(--color-accent)",
                borderColor: "var(--color-border)",
                backgroundColor: "var(--color-background-alt)",
              }}
            >
              {badge}
            </span>
          )}
          {title && (
            <h2
              className="mt-4 text-3xl font-bold tracking-tight md:text-4xl lg:text-5xl"
              style={{ color: "var(--color-foreground)" }}
            >
              {title}
            </h2>
          )}
          {subtitle && (
            <p
              className="mt-4 text-base leading-relaxed"
              style={{ color: "var(--color-foreground-muted)" }}
            >
              {subtitle}
            </p>
          )}
        </motion.div>

        <div className="mt-16 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {services.map((service, i) => {
            const Icon = getIcon(service.icon);
            return (
              <motion.div
                key={service.id}
                initial={{ opacity: 0, y: 24 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true, margin: "-60px" }}
                transition={{
                  delay: i * 0.08,
                  duration: 0.5,
                  ease: [0.16, 1, 0.3, 1],
                }}
                className="group relative rounded-[var(--radius-lg,1rem)] border p-6 transition-shadow duration-300 hover:shadow-lg"
                style={{
                  backgroundColor: "var(--color-background-card)",
                  borderColor: "var(--color-border)",
                }}
              >
                {Icon && (
                  <div
                    className="mb-4 flex h-12 w-12 items-center justify-center rounded-[var(--radius-md,0.75rem)]"
                    style={{ backgroundColor: "var(--color-accent)", opacity: 0.15 }}
                  >
                    <Icon
                      className="h-6 w-6"
                      style={{ color: "var(--color-accent)" }}
                    />
                  </div>
                )}
                <h3
                  className="text-lg font-semibold tracking-tight"
                  style={{ color: "var(--color-foreground)" }}
                >
                  {service.title}
                </h3>
                <p
                  className="mt-2 text-sm leading-relaxed"
                  style={{ color: "var(--color-foreground-muted)" }}
                >
                  {service.description}
                </p>
              </motion.div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

Avis

Services Cards Grid — React Services Section — Incubator