Portrait Grid
3-column masonry-style grid where a full square portrait leads each card. Stars float directly on the portrait with a gradient overlay. On hover the portrait subtly zooms. Alternates between white, dark navy, and warm sand backgrounds for visual rhythm. DM Serif Display + DM Sans. Use case: product / startup / general-purpose.
Portrait Grid the 15th of 15 designs in the 15 CSS Testimonials & Reviews collection. The design is implemented in pure CSS — no JavaScript required. Copy the HTML and CSS panels below into your project. Because the demo is pure CSS, it works in any framework or templating engine you happen to use. The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<section class="tm-pg" aria-label="Customer testimonials">
<header class="tm-pg-top-bar">
<h2 class="tm-pg-top-title">Hear it from the people who matter most</h2>
<div class="tm-pg-top-stat">
<div class="tm-pg-ts-num">4.9 ★</div>
<div class="tm-pg-ts-lbl">Average across 9,200+ reviews</div>
</div>
</header>
<div class="tm-pg-grid">
<article class="tm-pg-card">
<div class="tm-pg-card-photo tm-pg-photo-blue" aria-hidden="true">
<span class="tm-pg-photo-initials">EL</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span aria-hidden="true">★</span><span aria-hidden="true">★</span><span aria-hidden="true">★</span><span aria-hidden="true">★</span><span aria-hidden="true">★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"I've recommended this to every founder I know. The time I've saved is <em>genuinely hard to quantify.</em>"</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-blue" aria-hidden="true"><span class="tm-pg-a-initials">EL</span></span>
<span><cite class="tm-pg-a-name">Emma Larson</cite><span class="tm-pg-a-role">Founder, Northfield</span></span>
<span class="tm-pg-a-badge">Pro</span>
</figcaption>
</div>
</article>
<article class="tm-pg-card tm-pg-dark">
<div class="tm-pg-card-photo tm-pg-photo-teal" aria-hidden="true">
<span class="tm-pg-photo-initials">JO</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"Our sales cycle shortened by <em>three weeks</em> after switching. The clarity in our pitch improved the moment we had the right data."</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-teal" aria-hidden="true"><span class="tm-pg-a-initials">JO</span></span>
<span><cite class="tm-pg-a-name">James Owusu</cite><span class="tm-pg-a-role">VP Sales, Bridgemark</span></span>
<span class="tm-pg-a-badge">Enterprise</span>
</figcaption>
</div>
</article>
<article class="tm-pg-card">
<div class="tm-pg-card-photo tm-pg-photo-amber" aria-hidden="true">
<span class="tm-pg-photo-initials">MZ</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"Simple enough that my non-technical co-founder uses it daily. Powerful enough that I rely on it for <em>every strategic decision.</em>"</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-amber" aria-hidden="true"><span class="tm-pg-a-initials">MZ</span></span>
<span><cite class="tm-pg-a-name">Mei Zhang</cite><span class="tm-pg-a-role">Co-founder, Fenix Labs</span></span>
<span class="tm-pg-a-badge">Growth</span>
</figcaption>
</div>
</article>
<article class="tm-pg-card tm-pg-sand">
<div class="tm-pg-card-photo tm-pg-photo-indigo" aria-hidden="true">
<span class="tm-pg-photo-initials">TR</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"Migrated our entire workflow in a weekend. Monday morning the team didn't miss a beat. <em>Zero disruption, full adoption.</em>"</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-indigo" aria-hidden="true"><span class="tm-pg-a-initials">TR</span></span>
<span><cite class="tm-pg-a-name">Thomas Reeves</cite><span class="tm-pg-a-role">Ops Lead, Slate Co.</span></span>
<span class="tm-pg-a-badge">Team</span>
</figcaption>
</div>
</article>
<article class="tm-pg-card">
<div class="tm-pg-card-photo tm-pg-photo-green" aria-hidden="true">
<span class="tm-pg-photo-initials">AP</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"The reporting alone is worth the subscription. I present to investors every quarter and the <em>visuals do half the work.</em>"</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-green" aria-hidden="true"><span class="tm-pg-a-initials">AP</span></span>
<span><cite class="tm-pg-a-name">Aisha Patel</cite><span class="tm-pg-a-role">CEO, Vantage Health</span></span>
<span class="tm-pg-a-badge">Enterprise</span>
</figcaption>
</div>
</article>
<article class="tm-pg-card tm-pg-dark">
<div class="tm-pg-card-photo tm-pg-photo-purple" aria-hidden="true">
<span class="tm-pg-photo-initials">CM</span>
<div class="tm-pg-photo-stars" aria-label="5 out of 5 stars">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
<div class="tm-pg-card-body">
<blockquote class="tm-pg-quote">"We've tried four competitors. None had this level of <em>attention to the details</em> that slow teams down. It shows in the product."</blockquote>
<figcaption class="tm-pg-author-strip">
<span class="tm-pg-a-photo tm-pg-photo-purple" aria-hidden="true"><span class="tm-pg-a-initials">CM</span></span>
<span><cite class="tm-pg-a-name">Carlos Mendes</cite><span class="tm-pg-a-role">CPO, Ironhaven</span></span>
<span class="tm-pg-a-badge">Pro</span>
</figcaption>
</div>
</article>
</div>
</section> /* Portrait Grid — 3-col masonry-feel grid where a 1:1 portrait
leads each card. Cards alternate background (white / dark navy /
warm sand) for visual rhythm. Portrait subtly zooms on card hover.
The photo-stars badge sits on the bottom-left of the portrait above
a gradient overlay for legibility. */
.tm-pg {
--tm-pg-bg: #ffffff;
--tm-pg-ink: #0d0d0d;
--tm-pg-muted: #8c8c8c;
--tm-pg-line: #efefef;
--tm-pg-sand: #f5f2ed;
--tm-pg-navy: #0f2d4a;
--tm-pg-blue: #1a56db;
padding: clamp(32px, 4vw, 52px) clamp(20px, 3vw, 56px);
background: var(--tm-pg-bg);
font-family: 'DM Sans', system-ui, sans-serif;
min-height: 520px;
}
.tm-pg-top-bar {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 36px;
padding-bottom: 20px;
border-bottom: 1px solid var(--tm-pg-line);
flex-wrap: wrap;
gap: 18px;
}
.tm-pg-top-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: clamp(1.5rem, 2.5vw, 2rem);
color: var(--tm-pg-ink);
line-height: 1.2;
max-width: 400px;
margin: 0;
font-weight: 400;
}
.tm-pg-top-stat { text-align: right; }
.tm-pg-ts-num {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 2rem;
color: var(--tm-pg-navy);
line-height: 1;
}
.tm-pg-ts-lbl {
font-size: 11px;
color: var(--tm-pg-muted);
margin-top: 3px;
letter-spacing: 0.04em;
}
.tm-pg-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
align-items: start;
}
.tm-pg-card {
background: var(--tm-pg-bg);
border: 1px solid var(--tm-pg-line);
border-radius: 14px;
overflow: hidden;
transition: box-shadow 0.3s, transform 0.3s;
animation: tm-pg-appear 0.65s ease both;
}
.tm-pg-card:hover {
box-shadow: 0 10px 30px rgba(0,0,0,0.07);
transform: translateY(-3px);
}
.tm-pg-card:nth-child(1) { animation-delay: 0.04s; }
.tm-pg-card:nth-child(2) { animation-delay: 0.10s; }
.tm-pg-card:nth-child(3) { animation-delay: 0.16s; }
.tm-pg-card:nth-child(4) { animation-delay: 0.22s; }
.tm-pg-card:nth-child(5) { animation-delay: 0.28s; }
.tm-pg-card:nth-child(6) { animation-delay: 0.34s; }
@keyframes tm-pg-appear {
from { opacity: 0; transform: scale(.97); }
to { opacity: 1; transform: scale(1); }
}
.tm-pg-card-photo {
width: 100%;
aspect-ratio: 1;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: transform 0.5s ease;
transform: scale(1.05);
}
.tm-pg-card:hover .tm-pg-card-photo {
transform: scale(1.1);
}
.tm-pg-photo-initials {
font-family: 'DM Serif Display', Georgia, serif;
font-size: clamp(2.5rem, 7vw, 4rem);
font-weight: 400;
color: rgba(255,255,255,0.85);
letter-spacing: 0.05em;
}
.tm-pg-photo-blue { background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); }
.tm-pg-photo-teal { background: linear-gradient(135deg, #14b8a6 0%, #0f766e 100%); }
.tm-pg-photo-amber { background: linear-gradient(135deg, #f59e0b 0%, #b45309 100%); }
.tm-pg-photo-indigo { background: linear-gradient(135deg, #6366f1 0%, #4338ca 100%); }
.tm-pg-photo-green { background: linear-gradient(135deg, #22c55e 0%, #15803d 100%); }
.tm-pg-photo-purple { background: linear-gradient(135deg, #a78bfa 0%, #7c3aed 100%); }
.tm-pg-card-photo::after {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 50%;
background: linear-gradient(to bottom, transparent, rgba(0,0,0,0.25));
pointer-events: none;
}
.tm-pg-photo-stars {
position: absolute;
bottom: 12px;
left: 14px;
display: flex;
gap: 2px;
z-index: 1;
}
.tm-pg-photo-stars span {
color: #f5c518;
font-size: 11px;
text-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.tm-pg-card-body {
padding: 22px 20px 20px;
}
.tm-pg-quote {
font-family: 'DM Serif Display', Georgia, serif;
font-style: italic;
font-size: 1rem;
line-height: 1.6;
color: var(--tm-pg-ink);
margin: 0 0 16px;
}
.tm-pg-quote em {
font-style: normal;
color: var(--tm-pg-blue);
}
.tm-pg-author-strip {
display: flex;
align-items: center;
gap: 10px;
padding-top: 14px;
border-top: 1px solid var(--tm-pg-line);
}
.tm-pg-a-photo {
width: 34px;
height: 34px;
border-radius: 50%;
flex-shrink: 0;
border: 2px solid var(--tm-pg-line);
display: flex;
align-items: center;
justify-content: center;
}
.tm-pg-a-initials {
font-family: 'DM Sans', system-ui, sans-serif;
font-size: 11px;
font-weight: 600;
color: #fff;
}
.tm-pg-a-name {
display: block;
font-size: 12px;
font-weight: 600;
font-style: normal;
color: var(--tm-pg-ink);
line-height: 1.3;
}
.tm-pg-a-role {
font-size: 10px;
color: var(--tm-pg-muted);
margin-top: 1px;
}
.tm-pg-a-badge {
margin-left: auto;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--tm-pg-blue);
background: #eff6ff;
border: 1px solid #bfdbfe;
padding: 2px 7px;
border-radius: 4px;
flex-shrink: 0;
}
/* Dark variant */
.tm-pg-dark {
background: var(--tm-pg-navy);
border-color: var(--tm-pg-navy);
}
.tm-pg-dark .tm-pg-card-body { background: var(--tm-pg-navy); }
.tm-pg-dark .tm-pg-quote { color: rgba(255,255,255,0.88); }
.tm-pg-dark .tm-pg-quote em { color: #93c5fd; }
.tm-pg-dark .tm-pg-author-strip { border-top-color: rgba(255,255,255,0.1); }
.tm-pg-dark .tm-pg-a-name { color: #fff; }
.tm-pg-dark .tm-pg-a-role { color: rgba(255,255,255,0.45); }
.tm-pg-dark .tm-pg-a-photo { border-color: rgba(255,255,255,0.15); }
.tm-pg-dark .tm-pg-a-badge {
background: rgba(147,197,253,0.1);
border-color: rgba(147,197,253,0.3);
color: #93c5fd;
}
/* Sand variant */
.tm-pg-sand { background: var(--tm-pg-sand); border-color: rgba(0,0,0,0.06); }
.tm-pg-sand .tm-pg-card-body { background: var(--tm-pg-sand); }
.tm-pg-sand .tm-pg-author-strip { border-top-color: rgba(0,0,0,0.08); }
@media (max-width: 820px) {
.tm-pg-grid { grid-template-columns: 1fr; }
}
@media (hover: none) {
.tm-pg-card-photo { transform: scale(1); }
.tm-pg-card:hover .tm-pg-card-photo { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
.tm-pg-card { animation: none; transition: none; }
.tm-pg-card:hover { transform: none; }
.tm-pg-card-photo, .tm-pg-card:hover .tm-pg-card-photo { transform: scale(1); transition: none; }
}