Installation
Usage
import {
MultiMediaTestimonial,
type Testimonial,
} from "@/components/ruixen/multi-media-testimonial";
const testimonials: Testimonial[] = [
{
name: "Priya Raman",
profile: "/avatar-images/avatar-01.jpg",
title: "Replaced our in-house UI in a weekend",
designation: "Engineering Lead, Coinflect",
content:
"We had a stale Tailwind kit nobody wanted to touch. Two engineers swapped it out in a single sprint.",
mediaUrl: "https://example.com/clip.mp4",
thumbnail: "https://example.com/poster.png",
},
// ...more
];
export default function Page() {
return (
<MultiMediaTestimonial
heading="Teams ship faster on Ruixen"
description="Our clients keep coming back because we go beyond clean design."
items={testimonials}
/>
);
}Props
MultiMediaTestimonial
| Prop | Type | Default | Description |
|---|---|---|---|
items | Testimonial[] | Required | Array of testimonial entries rendered into a balanced masonry of 1–3 cols. |
heading | ReactNode | — | Optional centered headline above the grid. |
description | ReactNode | — | Optional subheading rendered under the heading. |
className | string | — | Extra classes appended to the outer <section>. |
Testimonial
| Field | Type | Description |
|---|---|---|
name | string | Author display name. First character is used as the avatar fallback. |
designation | string | Role / company subtitle rendered under the name. |
title | string | Optional headline shown at the top of the card. |
profile | string | Optional avatar image URL. |
content | string | Optional quote body. |
mediaUrl | string | Optional video URL (browser-playable, e.g. MP4). Renders a play-button overlay + lightbox. |
thumbnail | string | Used as the video poster when mediaUrl is set, or as a standalone image otherwise. |
Features
- Three card variants from one type — a card is text-only, image-only, or video-with-poster based purely on whether
mediaUrlandthumbnailare present. No flag enum to keep in sync with the data. - Lightbox video playback — clicking a video card opens a wide
Dialogwith native<video controls>andplaysInline. Closing the dialog pauses the clip and rewinds it tocurrentTime = 0so reopens always start fresh. - CSS multi-column masonry —
columns-1 sm:columns-2 lg:columns-3 [column-fill:_balance]plusbreak-inside-avoidon each card. Variable card heights pack tightly without a layout library. - Themed surfaces —
bg-card/60+backdrop-blur-sm+border-border/60matches the Ruixen wall-of-love treatment; hover lifts the border and shadow without shifting layout. - Accessible play affordance — the video trigger is a real
<button>with a labelledaria-label, keyboard focus ring (focus-visible:ring-2), and the play icon sits over a translucent foreground scrim so it remains visible on busy thumbnails. - Avatar fallback — when
profileis missing, theAvatarfalls back to the author's first initial via shadcn'sAvatarFallback, so the card layout never breaks on missing assets. - Quote glyph for text-only cards — text-only entries get a faint
Quoteicon in place of media, preserving vertical rhythm with image/video cards in the same row.

