77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { cn } from "@/lib/cn";
|
|
import { motion, useReducedMotion, type Variants } from "framer-motion";
|
|
import type { ReactNode } from "react";
|
|
|
|
type Props = {
|
|
eyebrow?: string;
|
|
index?: string;
|
|
title: ReactNode;
|
|
/** The gold-accent phrase rendered inside the title pass as JSX or separate prop. */
|
|
accent?: ReactNode;
|
|
subtitle?: ReactNode;
|
|
align?: "left" | "center";
|
|
className?: string;
|
|
};
|
|
|
|
export function SectionHeading({
|
|
eyebrow,
|
|
index,
|
|
title,
|
|
accent,
|
|
subtitle,
|
|
align = "left",
|
|
className,
|
|
}: Props) {
|
|
const reduce = useReducedMotion();
|
|
const v: Variants = {
|
|
hidden: { opacity: 0, y: reduce ? 0 : 24, filter: reduce ? "blur(0px)" : "blur(6px)" },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
filter: "blur(0px)",
|
|
transition: { duration: 0.9, ease: [0.16, 1, 0.3, 1] },
|
|
},
|
|
};
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex flex-col gap-6",
|
|
align === "center" ? "items-center text-center" : "items-start",
|
|
className
|
|
)}
|
|
>
|
|
{eyebrow ? (
|
|
<div className="flex items-center gap-3 text-[11px] font-medium uppercase tracking-[0.22em] text-mist">
|
|
{index ? <span className="text-gold/85">{index}</span> : null}
|
|
<span className="h-px w-8 bg-gold/40" />
|
|
<span className="text-mist/85">{eyebrow}</span>
|
|
</div>
|
|
) : null}
|
|
<motion.h2
|
|
variants={v}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, amount: 0.3 }}
|
|
className="font-display text-[clamp(2.4rem,5vw,4.6rem)] leading-[1.02] tracking-[-0.02em] text-bone text-balance"
|
|
>
|
|
{title}
|
|
{accent ? <> <span className="gold-text">{accent}</span></> : null}
|
|
</motion.h2>
|
|
{subtitle ? (
|
|
<motion.p
|
|
variants={v}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, amount: 0.3 }}
|
|
transition={{ delay: 0.1 }}
|
|
className="max-w-2xl text-pretty text-mist md:text-lg"
|
|
>
|
|
{subtitle}
|
|
</motion.p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|