shadcn.io is not affiliated with official shadcn/ui
Mosaic Testimonials Block for Radix UI
A checkerboard of monogram portrait tiles and quote tiles in cells that share their hairlines, each quote beside its speaker and the second row swapped, under a compact header with a Shine Button.
Installation
Overview
A testimonials section laid out as a mosaic, for React with Radix UI. A compact header names the product, Fernwick, a shared inbox for support teams, and carries a Shine Button to the full stories. Below it, four people, four quotes, eight tiles on a checkerboard: a portrait tile, then the quote its speaker gave, and in the second row the order swaps, so every portrait sits next to its own quote and the tiles lock together. The tiles share their hairlines, and the band's top rule runs a little past the page column with + marks on its ends. A portrait tile is a tinted graph-paper tile with a large monogram and the name under it, since the block ships no photos. A quote tile is a cell with a quote mark, a short quote and a caption. On a phone the mosaic stays a checkerboard of two columns, so the pairs stay side by side. It's built from Guides, Grid Pattern and Shine Button, with CSS transitions only and your theme's tokens. For landing pages that want proof with faces and have a few short, strong quotes to show.
FAQ
How do I change the people and quotes?
Edit PEOPLE at the top of the file. Each entry has the name, initials, role, company, quote and two placement strings: portraitAt and quoteAt, the grid cell of its two tiles. The default places make the checker: the first two people fill row one, and the next two swap sides in row two. To add a second pair of rows, copy the pattern with the next row numbers.
How is the checkerboard laid out?
The grid has two columns on a phone and four from lg. Each tile is placed by its col-start-* and row-start-* classes, with the lg: ones for the desktop cell. On a phone the portraits alternate sides row by row, and on a desktop two rows of four do the same. Rows have equal heights, and a 1 px gap on the border colour is the hairline between tiles, so they join cleanly.
How do I use real photos instead of monograms?
Replace the monogram span in a portrait tile with an img that fills the tile (absolute inset-0 size-full object-cover) and give it an empty alt, because the speaker's name is already in the quote's caption. Keep the name under the photo on a bg-background backing, so it stays readable over any picture.
How do screen readers read the mosaic?
The section's heading is an h2, and the quotes are the items of a list named "Customer quotes", each a figure with a blockquote and a figcaption with the name, role and company. The portrait tiles are decorative and hidden, because the same name is in the caption beside them, so nothing is read twice. The quote sits in a bdi, so its quotation marks stay put in right-to-left languages.
Does it fit at 320 px?
Yes. The grid keeps two columns, tiles have no fixed height and the quote text steps down to text-sm on phones, so each short quote wraps inside its tile and the row grows to fit the longest one. Keep quotes to about 90 characters; longer ones make the whole row tall on a phone. The Shine Button sits under the header text there.