Retour au catalogue

Event Countdown Hero

Hero evenement avec grand compte a rebours anime, nom de l'evenement, date, lieu et CTA d'inscription.

eventmedium Both Responsive a11y
boldeleganteventeducationuniversalcentered
Theme
"use client";

import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { MapPin, CalendarDays } from "lucide-react";

interface EventCountdownHeroProps {
  eventName?: string;
  tagline?: string;
  date?: string;
  location?: string;
  ctaLabel?: string;
  ctaHref?: string;
  targetDate?: string;
}

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

function useCountdown(target: string) {
  const [diff, setDiff] = useState({ days: 0, hours: 0, minutes: 0, seconds: 0 });

  useEffect(() => {
    const calc = () => {
      const ms = Math.max(0, new Date(target).getTime() - Date.now());
      setDiff({
        days: Math.floor(ms / 86400000),
        hours: Math.floor((ms % 86400000) / 3600000),
        minutes: Math.floor((ms % 3600000) / 60000),
        seconds: Math.floor((ms % 60000) / 1000),
      });
    };
    calc();
    const id = setInterval(calc, 1000);
    return () => clearInterval(id);
  }, [target]);

  return diff;
}

const units = [
  { key: "days", label: "Jours" },
  { key: "hours", label: "Heures" },
  { key: "minutes", label: "Minutes" },
  { key: "seconds", label: "Secondes" },
] as const;

export default function EventCountdownHero({
  eventName = "Sommet Digital 2026",
  tagline = "L'evenement tech incontournable de l'annee",
  date = "15 juin 2026",
  location = "Palais des Congres, Paris",
  ctaLabel = "Reserver ma place",
  ctaHref = "#",
  targetDate = "2026-06-15T09:00:00",
}: EventCountdownHeroProps) {
  const countdown = useCountdown(targetDate);

  return (
    <section
      style={{
        padding: "var(--section-padding-y) 0",
        background: "var(--color-background-dark, var(--color-background))",
        minHeight: "70vh",
        display: "flex", alignItems: "center",
      }}
    >
      <div style={{ maxWidth: "var(--container-max-width)", margin: "0 auto", padding: "0 var(--container-padding-x)", width: "100%", textAlign: "center" }}>
        <motion.div
          initial={{ opacity: 0, y: 24 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6, ease: EASE }}
        >
          <div style={{ display: "flex", justifyContent: "center", gap: "1.5rem", marginBottom: "1rem", flexWrap: "wrap" }}>
            <span style={{ display: "inline-flex", alignItems: "center", gap: "0.35rem", fontSize: "0.875rem", color: "var(--color-foreground-on-dark, var(--color-foreground-muted))" }}>
              <CalendarDays style={{ width: 14, height: 14 }} /> {date}
            </span>
            <span style={{ display: "inline-flex", alignItems: "center", gap: "0.35rem", fontSize: "0.875rem", color: "var(--color-foreground-on-dark, var(--color-foreground-muted))" }}>
              <MapPin style={{ width: 14, height: 14 }} /> {location}
            </span>
          </div>

          <h1 style={{
            fontFamily: "var(--font-sans)",
            fontSize: "clamp(2rem, 5vw, 3.5rem)",
            fontWeight: 800,
            color: "var(--color-foreground-on-dark, var(--color-foreground))",
            marginBottom: "0.75rem",
            letterSpacing: "-0.02em",
          }}>
            {eventName}
          </h1>
          {tagline && (
            <p style={{ fontSize: "1.125rem", color: "var(--color-foreground-on-dark, var(--color-foreground-muted))", marginBottom: "2.5rem", maxWidth: "480px", margin: "0 auto 2.5rem" }}>
              {tagline}
            </p>
          )}
        </motion.div>

        {/* Countdown */}
        <motion.div
          initial={{ opacity: 0, scale: 0.95 }}
          animate={{ opacity: 1, scale: 1 }}
          transition={{ duration: 0.5, delay: 0.2, ease: EASE }}
          style={{ display: "flex", justifyContent: "center", gap: "clamp(0.75rem, 2vw, 1.5rem)", marginBottom: "2.5rem", flexWrap: "wrap" }}
        >
          {units.map((u) => (
            <div key={u.key} style={{
              width: "clamp(72px, 12vw, 100px)",
              padding: "1rem 0.5rem",
              borderRadius: "var(--radius-lg)",
              background: "var(--color-background-alt)",
              border: "1px solid var(--color-border)",
              textAlign: "center",
            }}>
              <div style={{
                fontFamily: "var(--font-mono, var(--font-sans))",
                fontSize: "clamp(1.5rem, 3vw, 2.25rem)",
                fontWeight: 700,
                color: "var(--color-accent)",
                lineHeight: 1,
              }}>
                {String(countdown[u.key]).padStart(2, "0")}
              </div>
              <div style={{ fontSize: "0.6875rem", color: "var(--color-foreground-muted)", marginTop: "0.35rem", textTransform: "uppercase", letterSpacing: "0.06em" }}>
                {u.label}
              </div>
            </div>
          ))}
        </motion.div>

        <motion.a
          href={ctaHref}
          initial={{ opacity: 0, y: 12 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.4, delay: 0.35, ease: EASE }}
          whileHover={{ scale: 1.03 }}
          whileTap={{ scale: 0.97 }}
          style={{
            display: "inline-block",
            padding: "0.875rem 2rem",
            borderRadius: "var(--radius-md)",
            background: "var(--color-accent)",
            color: "var(--color-background)",
            fontWeight: 600,
            fontSize: "0.9375rem",
            textDecoration: "none",
            cursor: "pointer",
          }}
        >
          {ctaLabel}
        </motion.a>
      </div>
    </section>
  );
}

Reviews

Event Countdown Hero — React Event Section — Incubator