Retour au catalogue

Split Content Angled

Division diagonale entre image et texte avec clip-path. Effet dynamique et moderne.

split-contentmedium Both Responsive a11y
bolddarkuniversalagencyreal-estatefullscreen
Theme
"use client";

import { motion } from "framer-motion";
import { ChevronRight } from "lucide-react";

interface SplitContentAngledProps {
  title?: string;
  titleAccent?: string;
  description?: string;
  ctaLabel?: string;
  ctaUrl?: string;
  imageSrc?: string;
  imageAlt?: string;
  stats?: { value: string; label: string }[];
}

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

export default function SplitContentAngled({
  title = "Titre principal",
  titleAccent = "accent",
  description = "Description",
  ctaLabel = "Decouvrir",
  ctaUrl = "#",
  imageSrc = "",
  imageAlt = "Image",
  stats = [],
}: SplitContentAngledProps) {
  return (
    <section
      style={{
        position: "relative",
        minHeight: "80vh",
        display: "flex",
        alignItems: "center",
        background: "var(--color-background)",
        overflow: "hidden",
      }}
    >
      {/* Angled image background — right side */}
      <div
        style={{
          position: "absolute",
          top: 0,
          right: 0,
          width: "55%",
          height: "100%",
          clipPath: "polygon(15% 0, 100% 0, 100% 100%, 0% 100%)",
          background: "var(--color-background-alt)",
          overflow: "hidden",
        }}
      >
        {imageSrc ? (
          <img
            src={imageSrc}
            alt={imageAlt}
            style={{ width: "100%", height: "100%", objectFit: "cover" }}
          />
        ) : (
          <div
            style={{
              width: "100%",
              height: "100%",
              background: `linear-gradient(135deg, color-mix(in srgb, var(--color-accent) 15%, var(--color-background-alt)) 0%, var(--color-background-alt) 100%)`,
            }}
          />
        )}
        {/* Diagonal overlay gradient for readability */}
        <div
          aria-hidden
          style={{
            position: "absolute",
            inset: 0,
            background: `linear-gradient(to right, var(--color-background) 0%, transparent 40%)`,
          }}
        />
      </div>

      {/* Content */}
      <div
        style={{
          position: "relative",
          zIndex: 1,
          maxWidth: "var(--container-max-width)",
          margin: "0 auto",
          padding: "var(--section-padding-y-lg) var(--container-padding-x)",
          width: "100%",
        }}
      >
        <motion.div
          initial={{ opacity: 0, y: 30 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.7, ease: EASE }}
          style={{ maxWidth: "520px" }}
        >
          <h2
            style={{
              fontFamily: "var(--font-sans)",
              fontSize: "clamp(2rem, 4vw, 3.75rem)",
              fontWeight: 800,
              lineHeight: 1.08,
              letterSpacing: "-0.03em",
              color: "var(--color-foreground)",
              marginBottom: "1.5rem",
            }}
          >
            {title}{" "}
            {titleAccent && (
              <span
                style={{
                  fontFamily: "var(--font-serif)",
                  fontStyle: "italic",
                  fontWeight: 400,
                  color: "var(--color-accent)",
                }}
              >
                {titleAccent}
              </span>
            )}
          </h2>

          <p
            style={{
              fontSize: "1.0625rem",
              lineHeight: 1.7,
              color: "var(--color-foreground-muted)",
              marginBottom: "2rem",
            }}
          >
            {description}
          </p>

          <a
            href={ctaUrl}
            style={{
              display: "inline-flex",
              alignItems: "center",
              gap: "6px",
              padding: "0.875rem 2rem",
              borderRadius: "var(--radius-full)",
              background: "var(--color-accent)",
              color: "var(--color-foreground)",
              fontWeight: 600,
              fontSize: "0.9375rem",
              textDecoration: "none",
              transition: "opacity var(--duration-normal) var(--ease-out)",
            }}
          >
            {ctaLabel}
            <ChevronRight style={{ width: 16, height: 16 }} />
          </a>

          {stats.length > 0 && (
            <motion.div
              initial={{ opacity: 0, y: 16 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true }}
              transition={{ delay: 0.3, duration: 0.5, ease: EASE }}
              style={{
                display: "flex",
                gap: "2.5rem",
                marginTop: "3rem",
                paddingTop: "2rem",
                borderTop: "1px solid var(--color-border)",
              }}
            >
              {stats.map((s, i) => (
                <div key={i}>
                  <span
                    style={{
                      fontSize: "1.75rem",
                      fontWeight: 800,
                      color: "var(--color-accent)",
                      lineHeight: 1,
                    }}
                  >
                    {s.value}
                  </span>
                  <span
                    style={{
                      display: "block",
                      fontSize: "0.8125rem",
                      color: "var(--color-foreground-muted)",
                      marginTop: "0.25rem",
                    }}
                  >
                    {s.label}
                  </span>
                </div>
              ))}
            </motion.div>
          )}
        </motion.div>
      </div>
    </section>
  );
}

Avis

Split Content Angled — React Split-content Section — Incubator