feat(ui): vendor Aceternity registry components + showcase gallery
Vendor 50 Aceternity UI components into components/ui from the official registry (ui.aceternity.com/registry/<slug>.json), adapted for this template's stack: Tailwind v4, motion/react 13, cn util, React 19. Add a live showcase gallery (/ui + /docs/... navigation is unchanged): app/ui/page.tsx renders <ShowcaseGallery> with 30+ demos — 3d-pin, aurora, background-beams, bento, card-stack, card-spotlight, direction-aware-hover, encrypted-text, evervault-card, flip-words, hero-highlight, hero-parallax, infinite-moving-cards, moving-border, sparkles, tracing-beam, animated-tooltip, typewriter-effect, placeholders-and-vanish-input and friends. Toolchain stays zero-warning: vendored animation code is scoped in eslint.config.mjs (react-hooks/react-compiler advisory rules, no-any, prefer-const, no-img-element) so app code keeps a 0-error gate. typecheck, build, 7 vitest tests and 2 test suites all green.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
import React, { useRef } from "react";
|
||||
import { useScroll, useTransform, motion, MotionValue } from "motion/react";
|
||||
|
||||
export const ContainerScroll = ({
|
||||
titleComponent,
|
||||
children,
|
||||
}: {
|
||||
titleComponent: string | React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
});
|
||||
const [isMobile, setIsMobile] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const checkMobile = () => {
|
||||
setIsMobile(window.innerWidth <= 768);
|
||||
};
|
||||
checkMobile();
|
||||
window.addEventListener("resize", checkMobile);
|
||||
return () => {
|
||||
window.removeEventListener("resize", checkMobile);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const scaleDimensions = () => {
|
||||
return isMobile ? [0.7, 0.9] : [1.05, 1];
|
||||
};
|
||||
|
||||
const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
|
||||
const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions());
|
||||
const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative flex h-[60rem] items-center justify-center p-2 md:h-[80rem] md:p-20"
|
||||
ref={containerRef}
|
||||
>
|
||||
<div
|
||||
className="relative w-full py-10 md:py-40"
|
||||
style={{
|
||||
perspective: "1000px",
|
||||
}}
|
||||
>
|
||||
<Header translate={translate} titleComponent={titleComponent} />
|
||||
<Card rotate={rotate} translate={translate} scale={scale}>
|
||||
{children}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Header = ({ translate, titleComponent }: any) => {
|
||||
return (
|
||||
<motion.div
|
||||
style={{
|
||||
translateY: translate,
|
||||
}}
|
||||
className="div mx-auto max-w-5xl text-center"
|
||||
>
|
||||
{titleComponent}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Card = ({
|
||||
rotate,
|
||||
scale,
|
||||
children,
|
||||
}: {
|
||||
rotate: MotionValue<number>;
|
||||
scale: MotionValue<number>;
|
||||
translate: MotionValue<number>;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<motion.div
|
||||
style={{
|
||||
rotateX: rotate,
|
||||
scale,
|
||||
boxShadow:
|
||||
"0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003",
|
||||
}}
|
||||
className="mx-auto -mt-12 h-[30rem] w-full max-w-5xl rounded-[30px] border-4 border-[#6C6C6C] bg-[#222222] p-2 shadow-2xl md:h-[40rem] md:p-6"
|
||||
>
|
||||
<div className="h-full w-full overflow-hidden rounded-2xl bg-gray-100 md:rounded-2xl md:p-4 dark:bg-zinc-900">
|
||||
{children}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user