agency-web/app/components/PillMark.tsx

97 lines
3 KiB
TypeScript

"use client";
/**
* PillMark — the brand imagotype as a living system.
*
* Reconstructs the 4-bar capsule grid (violet→blue gradient bar / ink bar +
* blue square / emerald bar) as inline SVG so each pill can be animated
* independently: it assembles on mount, then breathes. Used at hero scale and
* as a compact mark in dividers / footer.
*
* Decorative by default (aria-hidden). Pass `title` to expose it as an image.
*/
import { useEffect, useRef } from "react";
import { gsap } from "gsap";
type Props = {
className?: string;
/** Animate assembly on mount (hero). */
animate?: boolean;
/** Continuous breathing after assembly. */
breathe?: boolean;
title?: string;
};
export default function PillMark({
className = "",
animate = false,
breathe = false,
title,
}: Props) {
const ref = useRef<SVGSVGElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const ctx = gsap.context(() => {
const bars = gsap.utils.toArray<SVGElement>(".pillmark__bar");
if (animate) {
gsap.set(bars, { transformOrigin: "center center" });
gsap.from(bars, {
scaleX: 0,
scaleY: 0.2,
opacity: 0,
duration: 1.1,
ease: "elastic.out(1, 0.75)",
stagger: { each: 0.09, from: "start" },
delay: 0.2,
});
}
if (breathe) {
bars.forEach((bar, i) => {
gsap.to(bar, {
y: i % 2 === 0 ? "+=6" : "-=6",
duration: 2.6 + i * 0.25,
ease: "sine.inOut",
repeat: -1,
yoyo: true,
delay: 1.2 + i * 0.1,
});
});
}
}, el);
return () => ctx.revert();
}, [animate, breathe]);
return (
<svg
ref={ref}
className={`pillmark ${className}`}
viewBox="0 0 1192 1287"
role={title ? "img" : "presentation"}
aria-hidden={title ? undefined : "true"}
aria-label={title}
>
{title ? <title>{title}</title> : null}
<defs>
<linearGradient id="pm-grad" x1="0" y1="0" x2="1192" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stopColor="#8b5cf6" />
<stop offset="1" stopColor="#3b82f6" />
</linearGradient>
</defs>
{/* top: full-width violet→blue gradient capsule */}
<rect className="pillmark__bar" x="0" y="0" width="1192" height="348.64" rx="174.32" fill="url(#pm-grad)" />
{/* middle row: ink wide capsule + blue square capsule */}
<rect className="pillmark__bar" x="0.02" y="480.64" width="725.02" height="348.64" rx="174.32" fill="#111827" />
<rect className="pillmark__bar" x="843" y="480.28" width="349.04" height="348.98" rx="174.32" fill="#3b82f6" />
{/* bottom: full-width emerald capsule */}
<rect className="pillmark__bar" x="0.04" y="938.21" width="1191.97" height="348.64" rx="174.32" fill="#10b981" />
</svg>
);
}