3D Testimonial Wall
3D Testimonial Wall is a React and Tailwind CSS card component written for Uncode and yours to ship. Copy it and paste it into your project.
Uncode
cards
What it is
3D Testimonial Wall is a React and Tailwind CSS card component written for Uncode and yours to ship. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
No packages beyond the project baseline
Licence
This component was written for this catalogue. It is yours to ship in client work and in products you sell, under the terms of use.
3D Testimonial Wall
import React, { ComponentPropsWithoutRef, useRef } from 'react';
import { cn } from '@/lib/utils';
interface MarqueeProps extends ComponentPropsWithoutRef<'div'> {
/**
* Optional CSS class name to apply custom styles
*/
className?: string;
/**
* Whether to reverse the animation direction
* @default false
*/
reverse?: boolean;
/**
* Whether to pause the animation on hover
* @default false
*/
pauseOnHover?: boolean;
/**
* Content to be displayed in the marquee
*/
children: React.ReactNode;
/**
* Whether to animate vertically instead of horizontally
* @default false
*/
vertical?: boolean;
/**
* Number of times to repeat the content
* @default 4
*/
repeat?: number;
/**
* If true, automatically repeats children enough to fill the visible area
*/
autoFill?: boolean;
/**
* ARIA label for accessibility
*/
ariaLabel?: string;
/**
* ARIA live region politeness
*/
ariaLive?: 'off' | 'polite' | 'assertive';
/**
* ARIA role
*/
ariaRole?: string;
}
export function Marquee({
className,
reverse = false,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
ariaLabel,
ariaLive = 'off',
ariaRole = 'marquee',
...props
}: MarqueeProps) {
const marqueeRef = useRef<HTMLDivElement>(null);
return (
<div
{...props}
ref={marqueeRef}
data-slot="marquee"
className={cn(
'group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]',
{
'flex-row': !vertical,
'flex-col': vertical,
},
className,
)}
aria-label={ariaLabel}
aria-live={ariaLive}
role={ariaRole}
tabIndex={0}
>
{React.useMemo(
() => (
<>
{Array.from({ length: repeat }, (_, i) => (
<div
key={i}
className={cn(
!vertical ? 'flex-row [gap:var(--gap)]' : 'flex-col [gap:var(--gap)]',
'flex shrink-0 justify-around',
!vertical && 'animate-marquee flex-row',
vertical && 'animate-marquee-vertical flex-col',
pauseOnHover && 'group-hover:[animation-play-state:paused]',
reverse && '[animation-direction:reverse]',
)}
>
{children}
</div>
))}
</>
),
[repeat, children, vertical, pauseOnHover, reverse],
)}
</div>
);
}