Retour au catalogue

Feature Showcase Holographic

Cartes holographiques avec effet flip 3D pour reveler les details de chaque feature.

feature-showcasecomplex Both Responsive a11y
boldelegantsaasagencygrid
Theme
"use client";

import { useState } from "react";
import { motion } from "framer-motion";
import {
  Cpu,
  Eye,
  Lock,
  Workflow,
  Database,
  Layers,
  type LucideIcon,
} from "lucide-react";

const iconMap: Record<string, LucideIcon> = {
  Cpu,
  Eye,
  Lock,
  Workflow,
  Database,
  Layers,
};

interface HoloFeature {
  icon: string;
  title: string;
  frontText: string;
  backText: string;
}

interface FeatureShowcaseHolographicProps {
  title?: string;
  subtitle?: string;
  features?: HoloFeature[];
}

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

function HoloCard({ feature, index }: { feature: HoloFeature; index: number }) {
  const [isFlipped, setIsFlipped] = useState(false);
  const [mousePos, setMousePos] = useState({ x: 0.5, y: 0.5 });
  const Icon = iconMap[feature.icon] || Cpu;

  function handleMouseMove(e: React.MouseEvent<HTMLDivElement>) {
    const rect = e.currentTarget.getBoundingClientRect();
    setMousePos({
      x: (e.clientX - rect.left) / rect.width,
      y: (e.clientY - rect.top) / rect.height,
    });
  }

  const gradientAngle = Math.atan2(mousePos.y - 0.5, mousePos.x - 0.5) * (180 / Math.PI);

  return (
    <motion.div
      initial={{ opacity: 0, y: 24 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.5, delay: 0.07 * index, ease: EASE }}
      onClick={() => setIsFlipped((f) => !f)}
      onMouseMove={handleMouseMove}
      style={{
        perspective: 1000,
        cursor: "pointer",
        minHeight: 260,
      }}
    >
      <motion.div
        animate={{ rotateY: isFlipped ? 180 : 0 }}
        transition={{ duration: 0.6, ease: EASE }}
        style={{
          position: "relative",
          width: "100%",
          height: "100%",
          minHeight: 260,
          transformStyle: "preserve-3d",
        }}
      >
        {/* Front face */}
        <div
          style={{
            position: "absolute",
            inset: 0,
            backfaceVisibility: "hidden",
            borderRadius: "var(--radius-lg)",
            border: "1px solid var(--color-border)",
            background: "var(--color-background-card)",
            padding: "2rem 1.5rem",
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
            textAlign: "center",
            overflow: "hidden",
          }}
        >
          {/* Holographic sheen overlay */}
          <div
            style={{
              position: "absolute",
              inset: 0,
              background: `linear-gradient(${gradientAngle}deg,
                color-mix(in srgb, var(--color-accent) 8%, transparent) 0%,
                transparent 40%,
                color-mix(in srgb, var(--color-accent) 5%, transparent) 70%,
                transparent 100%)`,
              pointerEvents: "none",
              transition: "background 0.15s ease",
            }}
          />
          <div
            style={{
              width: 56,
              height: 56,
              borderRadius: "var(--radius-md)",
              background: "var(--color-accent-subtle)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              marginBottom: "1.25rem",
            }}
          >
            <Icon style={{ width: 28, height: 28, color: "var(--color-accent)" }} />
          </div>
          <h3
            style={{
              fontSize: "1.125rem",
              fontWeight: 700,
              color: "var(--color-foreground)",
              marginBottom: "0.5rem",
            }}
          >
            {feature.title}
          </h3>
          <p
            style={{
              fontSize: "0.875rem",
              color: "var(--color-foreground-muted)",
              lineHeight: 1.6,
            }}
          >
            {feature.frontText}
          </p>
          <span
            style={{
              marginTop: "1rem",
              fontSize: "0.75rem",
              color: "var(--color-accent)",
              fontWeight: 600,
            }}
          >
            Cliquez pour en savoir plus
          </span>
        </div>

        {/* Back face */}
        <div
          style={{
            position: "absolute",
            inset: 0,
            backfaceVisibility: "hidden",
            transform: "rotateY(180deg)",
            borderRadius: "var(--radius-lg)",
            border: "1px solid var(--color-accent)",
            background: "var(--color-background-alt)",
            padding: "2rem 1.5rem",
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
            textAlign: "center",
          }}
        >
          <Icon
            style={{
              width: 24,
              height: 24,
              color: "var(--color-accent)",
              marginBottom: "1rem",
            }}
          />
          <h3
            style={{
              fontSize: "1rem",
              fontWeight: 700,
              color: "var(--color-foreground)",
              marginBottom: "0.75rem",
            }}
          >
            {feature.title}
          </h3>
          <p
            style={{
              fontSize: "0.8125rem",
              color: "var(--color-foreground-muted)",
              lineHeight: 1.7,
            }}
          >
            {feature.backText}
          </p>
        </div>
      </motion.div>
    </motion.div>
  );
}

export default function FeatureShowcaseHolographic({
  title = "Technologies de pointe",
  subtitle = "Fonctionnalites",
  features = [],
}: FeatureShowcaseHolographicProps) {
  return (
    <section
      style={{
        paddingTop: "var(--section-padding-y)",
        paddingBottom: "var(--section-padding-y)",
        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: 16 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true }}
          transition={{ duration: 0.5, ease: EASE }}
          style={{ textAlign: "center", marginBottom: "3rem" }}
        >
          <p
            style={{
              fontSize: "0.75rem",
              fontWeight: 600,
              textTransform: "uppercase",
              letterSpacing: "0.1em",
              color: "var(--color-accent)",
              marginBottom: "0.5rem",
            }}
          >
            {subtitle}
          </p>
          <h2
            style={{
              fontSize: "clamp(1.75rem, 3vw, 2.5rem)",
              fontWeight: 700,
              color: "var(--color-foreground)",
            }}
          >
            {title}
          </h2>
        </motion.div>

        <div
          style={{
            display: "grid",
            gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))",
            gap: "1.5rem",
          }}
        >
          {features.map((feature, i) => (
            <HoloCard key={i} feature={feature} index={i} />
          ))}
        </div>
      </div>
    </section>
  );
}

Avis

Feature Showcase Holographic — React Feature-showcase Section — Incubator