14 / 25
All 25 in 25 CSS Testimonials & Reviews
- 01Wall of Love Testimonial Grid (CSS Columns)
- 02SaaS Trust Banner with Verified Badges
- 03E-Commerce Product Review Card with Star Ratings
- 04Testimonial Carousel with Snap Scroll & Dots
- 05Video Testimonial Play Card with Modal
- 06Trustpilot & Google Style Review Card
- 07Before & After Case Study Quote Card
- 08Mobile-First Swipeable Card Deck
- 09Expandable Read More Testimonial Card
- 10Single Featured Hero Quote Block
- 11Dark Mode Editorial Quote Card
- 12Neo-Brutalist Review Card
- 13Bento Box Testimonial Grid Layout
- 14Glassmorphism SaaS Testimonial Card
- 15Retro Food & Restaurant Review Card
- 16Minimalist Agency Client Testimonial
- 17Web3 Gradient Glow Review Card
- 18Student Course Review Card with Rating
- 19Real Estate Client Testimonial Card
- 20Esports Gamer Review Card
- 21High Fashion Editorial Quote Card
- 22Fintech Security Trust Review Badge
- 23Clean White Quote Card Grid
- 24Horizontal Sliding Testimonial Row
- 25Staggered Portrait Testimonial Grid
Glassmorphism SaaS Testimonial Card
Frosted glass done properly — which mostly means doing less of it. Real glass needs three things working together: a backdrop that is actually worth blurring, a backdrop-filter that saturates as well as blurs, and an edge that catches light on one side only. The floating aurora behind the cards is pure CSS, animated on the compositor, and the cards refract it as they sit.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="tr-14" aria-label="Glassmorphism SaaS testimonial card demo">
<div class="tr-14__stage">
<div class="tr-14__aurora" aria-hidden="true"><i></i><i></i><i></i></div>
<header class="tr-14__head">
<p class="tr-14__eyebrow">Customer voices</p>
<h2 class="tr-14__title">Frosted, not foggy</h2>
</header>
<div class="tr-14__row">
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>The onboarding flow they built for us converted 34% better than the one we spent two quarters on. I stopped defending the old one after week two.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1524504388940-b1c1722653e1?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Jessa Moreau" width="46" height="46" loading="lazy" decoding="async">
<span><b>Jessa Moreau</b>Growth Lead · Tidewell</span>
</figcaption>
</figure>
<figure class="tr-14__card tr-14__card--lift">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Every integration we asked about already existed. The one that did not shipped six weeks later, and they emailed to tell us.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Marcus Yeung" width="46" height="46" loading="lazy" decoding="async">
<span><b>Marcus Yeung</b>Director of Risk · Halden</span>
</figcaption>
</figure>
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Our security review is famously unpleasant. They cleared it in nine days without a single exception request.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Erin McAllister" width="46" height="46" loading="lazy" decoding="async">
<span><b>Erin McAllister</b>COO · Larkfield Health</span>
</figcaption>
</figure>
</div>
<p class="tr-14__chip" aria-hidden="true">backdrop-filter: blur() saturate() · masked gradient edge · compositor-only aurora drift</p>
</div>
</section><section class="tr-14" aria-label="Glassmorphism SaaS testimonial card demo">
<div class="tr-14__stage">
<div class="tr-14__aurora" aria-hidden="true"><i></i><i></i><i></i></div>
<header class="tr-14__head">
<p class="tr-14__eyebrow">Customer voices</p>
<h2 class="tr-14__title">Frosted, not foggy</h2>
</header>
<div class="tr-14__row">
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>The onboarding flow they built for us converted 34% better than the one we spent two quarters on. I stopped defending the old one after week two.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1524504388940-b1c1722653e1?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Jessa Moreau" width="46" height="46" loading="lazy" decoding="async">
<span><b>Jessa Moreau</b>Growth Lead · Tidewell</span>
</figcaption>
</figure>
<figure class="tr-14__card tr-14__card--lift">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Every integration we asked about already existed. The one that did not shipped six weeks later, and they emailed to tell us.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Marcus Yeung" width="46" height="46" loading="lazy" decoding="async">
<span><b>Marcus Yeung</b>Director of Risk · Halden</span>
</figcaption>
</figure>
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Our security review is famously unpleasant. They cleared it in nine days without a single exception request.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Erin McAllister" width="46" height="46" loading="lazy" decoding="async">
<span><b>Erin McAllister</b>COO · Larkfield Health</span>
</figcaption>
</figure>
</div>
<p class="tr-14__chip" aria-hidden="true">backdrop-filter: blur() saturate() · masked gradient edge · compositor-only aurora drift</p>
</div>
</section>.tr-14,
.tr-14 *,
.tr-14 *::before,
.tr-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tr-14 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
--tr-14-bg: oklch(0.19 0.04 290);
--tr-14-ink: oklch(0.98 0.005 290);
--tr-14-mut: oklch(0.8 0.02 290);
--tr-14-acc: oklch(0.82 0.15 200);
--tr-14-star: oklch(0.87 0.13 92);
background: var(--tr-14-bg);
color: var(--tr-14-ink);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
-webkit-font-smoothing: antialiased;
}
.tr-14__stage {
position: relative;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
align-content: center;
gap: clamp(20px,3vw,34px);
padding: clamp(20px,4vw,56px);
overflow: hidden;
isolation: isolate;
}
.tr-14__aurora {
position: absolute;
inset: -10%;
z-index: -1;
}
.tr-14__aurora i {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: .85;
animation: tr-14-drift 24s ease-in-out infinite alternate;
}
.tr-14__aurora i:nth-child(1) {
inline-size: 46vmax;
block-size: 46vmax;
inset-block-start: -8%;
inset-inline-start: -6%;
background: radial-gradient(circle,oklch(0.62 0.22 300),transparent 68%);
}
.tr-14__aurora i:nth-child(2) {
inline-size: 40vmax;
block-size: 40vmax;
inset-block-start: 12%;
inset-inline-end: -8%;
background: radial-gradient(circle,oklch(0.7 0.2 200),transparent 68%);
animation-delay: -8s;
}
.tr-14__aurora i:nth-child(3) {
inline-size: 44vmax;
block-size: 44vmax;
inset-block-end: -14%;
inset-inline-start: 26%;
background: radial-gradient(circle,oklch(0.66 0.21 340),transparent 68%);
animation-delay: -15s;
}
@keyframes tr-14-drift {
to {
translate: 6% -5%;
scale: 1.14;
}
}
.tr-14__head {
text-align: center;
display: grid;
gap: 9px;
}
.tr-14__eyebrow {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--tr-14-acc);
}
.tr-14__title {
font-size: clamp(26px,4.4vw,46px);
font-weight: 660;
letter-spacing: -.04em;
text-wrap: balance;
}
.tr-14__row {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(100%,272px),1fr));
gap: clamp(14px,2vw,20px);
width: min(100%,1060px);
align-items: start;
}
.tr-14__card {
position: relative;
display: grid;
gap: 16px;
padding: clamp(22px,2.6vw,28px);
border-radius: 20px;
background: oklch(1 0 0/.07);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
border: 1px solid oklch(1 0 0/.15);
box-shadow: 0 30px 60px -34px oklch(0.1 0.05 290/.85),inset 0 1px 0 oklch(1 0 0/.14);
animation: tr-14-in .8s cubic-bezier(.16,1,.3,1) both;
transition: translate .4s cubic-bezier(.16,1,.3,1),background .4s;
}
.tr-14__card:nth-child(2) {
animation-delay: .1s;
}
.tr-14__card:nth-child(3) {
animation-delay: .2s;
}
@keyframes tr-14-in {
from {
opacity: 0;
translate: 0 20px;
}
to {
opacity: 1;
translate: 0 0;
}
}
.tr-14__card--lift {
translate: 0 -18px;
}
.tr-14__card:hover {
translate: 0 -8px;
background: oklch(1 0 0/.11);
}
.tr-14__card--lift:hover {
translate: 0 -26px;
}
.tr-14__card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(140deg,oklch(1 0 0/.55),transparent 42%,transparent 62%,oklch(1 0 0/.14));
-webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.tr-14__stars {
font-size: 15px;
letter-spacing: 2.5px;
color: var(--tr-14-star);
text-shadow: 0 1px 6px oklch(0.7 0.15 92/.5);
}
.tr-14__card blockquote {
font-size: 15.5px;
line-height: 1.56;
letter-spacing: -.016em;
text-wrap: pretty;
text-shadow: 0 1px 2px oklch(0 0 0/.3);
}
.tr-14__card figcaption {
display: flex;
align-items: center;
gap: 11px;
padding-top: 16px;
border-top: 1px solid oklch(1 0 0/.13);
}
.tr-14__card figcaption img {
width: 46px;
height: 46px;
border-radius: 50%;
object-fit: cover;
flex: none;
box-shadow: 0 0 0 1px oklch(1 0 0/.28);
}
.tr-14__card figcaption span {
display: grid;
font-size: 12px;
line-height: 1.35;
color: var(--tr-14-mut);
}
.tr-14__card figcaption b {
font-size: 14.5px;
font-weight: 640;
color: var(--tr-14-ink);
letter-spacing: -.014em;
}
.tr-14__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
color: var(--tr-14-mut);
padding: 8px 15px;
border: 1px solid oklch(1 0 0/.18);
border-radius: 99px;
text-align: center;
background: oklch(1 0 0/.06);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
.tr-14__card {
background: oklch(0.28 0.05 290/.88);
}
}
@media (max-width: 760px) {
.tr-14__card--lift {
translate: 0 0;
}
.tr-14__card--lift:hover {
translate: 0 -8px;
}
}
@media (prefers-reduced-motion: reduce) {
.tr-14__aurora i,
.tr-14__card {
animation: none;
}
.tr-14__card,
.tr-14__card--lift {
transition: none;
}
}.tr-14,
.tr-14 *,
.tr-14 *::before,
.tr-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tr-14 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
--tr-14-bg: oklch(0.19 0.04 290);
--tr-14-ink: oklch(0.98 0.005 290);
--tr-14-mut: oklch(0.8 0.02 290);
--tr-14-acc: oklch(0.82 0.15 200);
--tr-14-star: oklch(0.87 0.13 92);
background: var(--tr-14-bg);
color: var(--tr-14-ink);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
-webkit-font-smoothing: antialiased;
}
.tr-14__stage {
position: relative;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
align-content: center;
gap: clamp(20px,3vw,34px);
padding: clamp(20px,4vw,56px);
overflow: hidden;
isolation: isolate;
}
.tr-14__aurora {
position: absolute;
inset: -10%;
z-index: -1;
}
.tr-14__aurora i {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: .85;
animation: tr-14-drift 24s ease-in-out infinite alternate;
}
.tr-14__aurora i:nth-child(1) {
inline-size: 46vmax;
block-size: 46vmax;
inset-block-start: -8%;
inset-inline-start: -6%;
background: radial-gradient(circle,oklch(0.62 0.22 300),transparent 68%);
}
.tr-14__aurora i:nth-child(2) {
inline-size: 40vmax;
block-size: 40vmax;
inset-block-start: 12%;
inset-inline-end: -8%;
background: radial-gradient(circle,oklch(0.7 0.2 200),transparent 68%);
animation-delay: -8s;
}
.tr-14__aurora i:nth-child(3) {
inline-size: 44vmax;
block-size: 44vmax;
inset-block-end: -14%;
inset-inline-start: 26%;
background: radial-gradient(circle,oklch(0.66 0.21 340),transparent 68%);
animation-delay: -15s;
}
@keyframes tr-14-drift {
to {
translate: 6% -5%;
scale: 1.14;
}
}
.tr-14__head {
text-align: center;
display: grid;
gap: 9px;
}
.tr-14__eyebrow {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--tr-14-acc);
}
.tr-14__title {
font-size: clamp(26px,4.4vw,46px);
font-weight: 660;
letter-spacing: -.04em;
text-wrap: balance;
}
.tr-14__row {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(100%,272px),1fr));
gap: clamp(14px,2vw,20px);
width: min(100%,1060px);
align-items: start;
}
.tr-14__card {
position: relative;
display: grid;
gap: 16px;
padding: clamp(22px,2.6vw,28px);
border-radius: 20px;
background: oklch(1 0 0/.07);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
border: 1px solid oklch(1 0 0/.15);
box-shadow: 0 30px 60px -34px oklch(0.1 0.05 290/.85),inset 0 1px 0 oklch(1 0 0/.14);
animation: tr-14-in .8s cubic-bezier(.16,1,.3,1) both;
transition: translate .4s cubic-bezier(.16,1,.3,1),background .4s;
}
.tr-14__card:nth-child(2) {
animation-delay: .1s;
}
.tr-14__card:nth-child(3) {
animation-delay: .2s;
}
@keyframes tr-14-in {
from {
opacity: 0;
translate: 0 20px;
}
to {
opacity: 1;
translate: 0 0;
}
}
.tr-14__card--lift {
translate: 0 -18px;
}
.tr-14__card:hover {
translate: 0 -8px;
background: oklch(1 0 0/.11);
}
.tr-14__card--lift:hover {
translate: 0 -26px;
}
.tr-14__card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(140deg,oklch(1 0 0/.55),transparent 42%,transparent 62%,oklch(1 0 0/.14));
-webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.tr-14__stars {
font-size: 15px;
letter-spacing: 2.5px;
color: var(--tr-14-star);
text-shadow: 0 1px 6px oklch(0.7 0.15 92/.5);
}
.tr-14__card blockquote {
font-size: 15.5px;
line-height: 1.56;
letter-spacing: -.016em;
text-wrap: pretty;
text-shadow: 0 1px 2px oklch(0 0 0/.3);
}
.tr-14__card figcaption {
display: flex;
align-items: center;
gap: 11px;
padding-top: 16px;
border-top: 1px solid oklch(1 0 0/.13);
}
.tr-14__card figcaption img {
width: 46px;
height: 46px;
border-radius: 50%;
object-fit: cover;
flex: none;
box-shadow: 0 0 0 1px oklch(1 0 0/.28);
}
.tr-14__card figcaption span {
display: grid;
font-size: 12px;
line-height: 1.35;
color: var(--tr-14-mut);
}
.tr-14__card figcaption b {
font-size: 14.5px;
font-weight: 640;
color: var(--tr-14-ink);
letter-spacing: -.014em;
}
.tr-14__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
color: var(--tr-14-mut);
padding: 8px 15px;
border: 1px solid oklch(1 0 0/.18);
border-radius: 99px;
text-align: center;
background: oklch(1 0 0/.06);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
.tr-14__card {
background: oklch(0.28 0.05 290/.88);
}
}
@media (max-width: 760px) {
.tr-14__card--lift {
translate: 0 0;
}
.tr-14__card--lift:hover {
translate: 0 -8px;
}
}
@media (prefers-reduced-motion: reduce) {
.tr-14__aurora i,
.tr-14__card {
animation: none;
}
.tr-14__card,
.tr-14__card--lift {
transition: none;
}
}Here's a working CSS Testimonials & Review from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Glassmorphism SaaS Testimonial Card
Source: https://codefronts.com/components/css-testimonials-reviews/neon-glass-saas/
Frosted glass done properly — which mostly means doing less of it. Real glass needs three things working together: a backdrop that is actually worth blurring, a backdrop-filter that saturates as well as blurs, and an edge that catches light on one side only. The floating aurora behind the cards is pure CSS, animated on the compositor, and the cards refract it as they sit.
## HTML
```html
<section class="tr-14" aria-label="Glassmorphism SaaS testimonial card demo">
<div class="tr-14__stage">
<div class="tr-14__aurora" aria-hidden="true"><i></i><i></i><i></i></div>
<header class="tr-14__head">
<p class="tr-14__eyebrow">Customer voices</p>
<h2 class="tr-14__title">Frosted, not foggy</h2>
</header>
<div class="tr-14__row">
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>The onboarding flow they built for us converted 34% better than the one we spent two quarters on. I stopped defending the old one after week two.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1524504388940-b1c1722653e1?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Jessa Moreau" width="46" height="46" loading="lazy" decoding="async">
<span><b>Jessa Moreau</b>Growth Lead · Tidewell</span>
</figcaption>
</figure>
<figure class="tr-14__card tr-14__card--lift">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Every integration we asked about already existed. The one that did not shipped six weeks later, and they emailed to tell us.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Marcus Yeung" width="46" height="46" loading="lazy" decoding="async">
<span><b>Marcus Yeung</b>Director of Risk · Halden</span>
</figcaption>
</figure>
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Our security review is famously unpleasant. They cleared it in nine days without a single exception request.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Erin McAllister" width="46" height="46" loading="lazy" decoding="async">
<span><b>Erin McAllister</b>COO · Larkfield Health</span>
</figcaption>
</figure>
</div>
<p class="tr-14__chip" aria-hidden="true">backdrop-filter: blur() saturate() · masked gradient edge · compositor-only aurora drift</p>
</div>
</section>
```
## CSS
```css
.tr-14,
.tr-14 *,
.tr-14 *::before,
.tr-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tr-14 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
--tr-14-bg: oklch(0.19 0.04 290);
--tr-14-ink: oklch(0.98 0.005 290);
--tr-14-mut: oklch(0.8 0.02 290);
--tr-14-acc: oklch(0.82 0.15 200);
--tr-14-star: oklch(0.87 0.13 92);
background: var(--tr-14-bg);
color: var(--tr-14-ink);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
-webkit-font-smoothing: antialiased;
}
.tr-14__stage {
position: relative;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
align-content: center;
gap: clamp(20px,3vw,34px);
padding: clamp(20px,4vw,56px);
overflow: hidden;
isolation: isolate;
}
.tr-14__aurora {
position: absolute;
inset: -10%;
z-index: -1;
}
.tr-14__aurora i {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: .85;
animation: tr-14-drift 24s ease-in-out infinite alternate;
}
.tr-14__aurora i:nth-child(1) {
inline-size: 46vmax;
block-size: 46vmax;
inset-block-start: -8%;
inset-inline-start: -6%;
background: radial-gradient(circle,oklch(0.62 0.22 300),transparent 68%);
}
.tr-14__aurora i:nth-child(2) {
inline-size: 40vmax;
block-size: 40vmax;
inset-block-start: 12%;
inset-inline-end: -8%;
background: radial-gradient(circle,oklch(0.7 0.2 200),transparent 68%);
animation-delay: -8s;
}
.tr-14__aurora i:nth-child(3) {
inline-size: 44vmax;
block-size: 44vmax;
inset-block-end: -14%;
inset-inline-start: 26%;
background: radial-gradient(circle,oklch(0.66 0.21 340),transparent 68%);
animation-delay: -15s;
}
@keyframes tr-14-drift {
to {
translate: 6% -5%;
scale: 1.14;
}
}
.tr-14__head {
text-align: center;
display: grid;
gap: 9px;
}
.tr-14__eyebrow {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--tr-14-acc);
}
.tr-14__title {
font-size: clamp(26px,4.4vw,46px);
font-weight: 660;
letter-spacing: -.04em;
text-wrap: balance;
}
.tr-14__row {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(100%,272px),1fr));
gap: clamp(14px,2vw,20px);
width: min(100%,1060px);
align-items: start;
}
.tr-14__card {
position: relative;
display: grid;
gap: 16px;
padding: clamp(22px,2.6vw,28px);
border-radius: 20px;
background: oklch(1 0 0/.07);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
border: 1px solid oklch(1 0 0/.15);
box-shadow: 0 30px 60px -34px oklch(0.1 0.05 290/.85),inset 0 1px 0 oklch(1 0 0/.14);
animation: tr-14-in .8s cubic-bezier(.16,1,.3,1) both;
transition: translate .4s cubic-bezier(.16,1,.3,1),background .4s;
}
.tr-14__card:nth-child(2) {
animation-delay: .1s;
}
.tr-14__card:nth-child(3) {
animation-delay: .2s;
}
@keyframes tr-14-in {
from {
opacity: 0;
translate: 0 20px;
}
to {
opacity: 1;
translate: 0 0;
}
}
.tr-14__card--lift {
translate: 0 -18px;
}
.tr-14__card:hover {
translate: 0 -8px;
background: oklch(1 0 0/.11);
}
.tr-14__card--lift:hover {
translate: 0 -26px;
}
.tr-14__card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(140deg,oklch(1 0 0/.55),transparent 42%,transparent 62%,oklch(1 0 0/.14));
-webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.tr-14__stars {
font-size: 15px;
letter-spacing: 2.5px;
color: var(--tr-14-star);
text-shadow: 0 1px 6px oklch(0.7 0.15 92/.5);
}
.tr-14__card blockquote {
font-size: 15.5px;
line-height: 1.56;
letter-spacing: -.016em;
text-wrap: pretty;
text-shadow: 0 1px 2px oklch(0 0 0/.3);
}
.tr-14__card figcaption {
display: flex;
align-items: center;
gap: 11px;
padding-top: 16px;
border-top: 1px solid oklch(1 0 0/.13);
}
.tr-14__card figcaption img {
width: 46px;
height: 46px;
border-radius: 50%;
object-fit: cover;
flex: none;
box-shadow: 0 0 0 1px oklch(1 0 0/.28);
}
.tr-14__card figcaption span {
display: grid;
font-size: 12px;
line-height: 1.35;
color: var(--tr-14-mut);
}
.tr-14__card figcaption b {
font-size: 14.5px;
font-weight: 640;
color: var(--tr-14-ink);
letter-spacing: -.014em;
}
.tr-14__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
color: var(--tr-14-mut);
padding: 8px 15px;
border: 1px solid oklch(1 0 0/.18);
border-radius: 99px;
text-align: center;
background: oklch(1 0 0/.06);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
.tr-14__card {
background: oklch(0.28 0.05 290/.88);
}
}
@media (max-width: 760px) {
.tr-14__card--lift {
translate: 0 0;
}
.tr-14__card--lift:hover {
translate: 0 -8px;
}
}
@media (prefers-reduced-motion: reduce) {
.tr-14__aurora i,
.tr-14__card {
animation: none;
}
.tr-14__card,
.tr-14__card--lift {
transition: none;
}
}
```Here's a working CSS Testimonials & Review from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Glassmorphism SaaS Testimonial Card
Source: https://codefronts.com/components/css-testimonials-reviews/neon-glass-saas/
Frosted glass done properly — which mostly means doing less of it. Real glass needs three things working together: a backdrop that is actually worth blurring, a backdrop-filter that saturates as well as blurs, and an edge that catches light on one side only. The floating aurora behind the cards is pure CSS, animated on the compositor, and the cards refract it as they sit.
## HTML
```html
<section class="tr-14" aria-label="Glassmorphism SaaS testimonial card demo">
<div class="tr-14__stage">
<div class="tr-14__aurora" aria-hidden="true"><i></i><i></i><i></i></div>
<header class="tr-14__head">
<p class="tr-14__eyebrow">Customer voices</p>
<h2 class="tr-14__title">Frosted, not foggy</h2>
</header>
<div class="tr-14__row">
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>The onboarding flow they built for us converted 34% better than the one we spent two quarters on. I stopped defending the old one after week two.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1524504388940-b1c1722653e1?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Jessa Moreau" width="46" height="46" loading="lazy" decoding="async">
<span><b>Jessa Moreau</b>Growth Lead · Tidewell</span>
</figcaption>
</figure>
<figure class="tr-14__card tr-14__card--lift">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Every integration we asked about already existed. The one that did not shipped six weeks later, and they emailed to tell us.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Marcus Yeung" width="46" height="46" loading="lazy" decoding="async">
<span><b>Marcus Yeung</b>Director of Risk · Halden</span>
</figcaption>
</figure>
<figure class="tr-14__card">
<div class="tr-14__stars" role="img" aria-label="Rated 5 out of 5">★★★★★</div>
<blockquote>Our security review is famously unpleasant. They cleared it in nine days without a single exception request.</blockquote>
<figcaption>
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=140&h=140&fit=crop&auto=format" alt="Portrait of Erin McAllister" width="46" height="46" loading="lazy" decoding="async">
<span><b>Erin McAllister</b>COO · Larkfield Health</span>
</figcaption>
</figure>
</div>
<p class="tr-14__chip" aria-hidden="true">backdrop-filter: blur() saturate() · masked gradient edge · compositor-only aurora drift</p>
</div>
</section>
```
## CSS
```css
.tr-14,
.tr-14 *,
.tr-14 *::before,
.tr-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tr-14 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
--tr-14-bg: oklch(0.19 0.04 290);
--tr-14-ink: oklch(0.98 0.005 290);
--tr-14-mut: oklch(0.8 0.02 290);
--tr-14-acc: oklch(0.82 0.15 200);
--tr-14-star: oklch(0.87 0.13 92);
background: var(--tr-14-bg);
color: var(--tr-14-ink);
font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
-webkit-font-smoothing: antialiased;
}
.tr-14__stage {
position: relative;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
align-content: center;
gap: clamp(20px,3vw,34px);
padding: clamp(20px,4vw,56px);
overflow: hidden;
isolation: isolate;
}
.tr-14__aurora {
position: absolute;
inset: -10%;
z-index: -1;
}
.tr-14__aurora i {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: .85;
animation: tr-14-drift 24s ease-in-out infinite alternate;
}
.tr-14__aurora i:nth-child(1) {
inline-size: 46vmax;
block-size: 46vmax;
inset-block-start: -8%;
inset-inline-start: -6%;
background: radial-gradient(circle,oklch(0.62 0.22 300),transparent 68%);
}
.tr-14__aurora i:nth-child(2) {
inline-size: 40vmax;
block-size: 40vmax;
inset-block-start: 12%;
inset-inline-end: -8%;
background: radial-gradient(circle,oklch(0.7 0.2 200),transparent 68%);
animation-delay: -8s;
}
.tr-14__aurora i:nth-child(3) {
inline-size: 44vmax;
block-size: 44vmax;
inset-block-end: -14%;
inset-inline-start: 26%;
background: radial-gradient(circle,oklch(0.66 0.21 340),transparent 68%);
animation-delay: -15s;
}
@keyframes tr-14-drift {
to {
translate: 6% -5%;
scale: 1.14;
}
}
.tr-14__head {
text-align: center;
display: grid;
gap: 9px;
}
.tr-14__eyebrow {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--tr-14-acc);
}
.tr-14__title {
font-size: clamp(26px,4.4vw,46px);
font-weight: 660;
letter-spacing: -.04em;
text-wrap: balance;
}
.tr-14__row {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(100%,272px),1fr));
gap: clamp(14px,2vw,20px);
width: min(100%,1060px);
align-items: start;
}
.tr-14__card {
position: relative;
display: grid;
gap: 16px;
padding: clamp(22px,2.6vw,28px);
border-radius: 20px;
background: oklch(1 0 0/.07);
-webkit-backdrop-filter: blur(22px) saturate(180%);
backdrop-filter: blur(22px) saturate(180%);
border: 1px solid oklch(1 0 0/.15);
box-shadow: 0 30px 60px -34px oklch(0.1 0.05 290/.85),inset 0 1px 0 oklch(1 0 0/.14);
animation: tr-14-in .8s cubic-bezier(.16,1,.3,1) both;
transition: translate .4s cubic-bezier(.16,1,.3,1),background .4s;
}
.tr-14__card:nth-child(2) {
animation-delay: .1s;
}
.tr-14__card:nth-child(3) {
animation-delay: .2s;
}
@keyframes tr-14-in {
from {
opacity: 0;
translate: 0 20px;
}
to {
opacity: 1;
translate: 0 0;
}
}
.tr-14__card--lift {
translate: 0 -18px;
}
.tr-14__card:hover {
translate: 0 -8px;
background: oklch(1 0 0/.11);
}
.tr-14__card--lift:hover {
translate: 0 -26px;
}
.tr-14__card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(140deg,oklch(1 0 0/.55),transparent 42%,transparent 62%,oklch(1 0 0/.14));
-webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.tr-14__stars {
font-size: 15px;
letter-spacing: 2.5px;
color: var(--tr-14-star);
text-shadow: 0 1px 6px oklch(0.7 0.15 92/.5);
}
.tr-14__card blockquote {
font-size: 15.5px;
line-height: 1.56;
letter-spacing: -.016em;
text-wrap: pretty;
text-shadow: 0 1px 2px oklch(0 0 0/.3);
}
.tr-14__card figcaption {
display: flex;
align-items: center;
gap: 11px;
padding-top: 16px;
border-top: 1px solid oklch(1 0 0/.13);
}
.tr-14__card figcaption img {
width: 46px;
height: 46px;
border-radius: 50%;
object-fit: cover;
flex: none;
box-shadow: 0 0 0 1px oklch(1 0 0/.28);
}
.tr-14__card figcaption span {
display: grid;
font-size: 12px;
line-height: 1.35;
color: var(--tr-14-mut);
}
.tr-14__card figcaption b {
font-size: 14.5px;
font-weight: 640;
color: var(--tr-14-ink);
letter-spacing: -.014em;
}
.tr-14__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
color: var(--tr-14-mut);
padding: 8px 15px;
border: 1px solid oklch(1 0 0/.18);
border-radius: 99px;
text-align: center;
background: oklch(1 0 0/.06);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
.tr-14__card {
background: oklch(0.28 0.05 290/.88);
}
}
@media (max-width: 760px) {
.tr-14__card--lift {
translate: 0 0;
}
.tr-14__card--lift:hover {
translate: 0 -8px;
}
}
@media (prefers-reduced-motion: reduce) {
.tr-14__aurora i,
.tr-14__card {
animation: none;
}
.tr-14__card,
.tr-14__card--lift {
transition: none;
}
}
```How this works
Glass is a recipe, not a single property. All three lines matter:
.card {
background: oklch(1 0 0 / .07); /* tint, not opacity */
backdrop-filter: blur(22px) saturate(180%);
border: 1px solid oklch(1 0 0 / .16);
}The saturate(180%) is what stops frosted glass looking like grey fog — it pulls the color out of the backdrop so the blur reads as translucency rather than a scrim. Blur alone is the single most common mistake in glassmorphism.
The specular edge is a masked gradient border, brighter at the top-left where the implied light source sits:
.card::before {
inset: 0; padding: 1px; border-radius: inherit;
background: linear-gradient(140deg, oklch(1 0 0 / .5), transparent 42%);
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
}The aurora behind is three blurred radial gradients on one absolutely positioned layer, drifting with a 24-second alternating keyframe on translate and scale only — both compositor properties, so it costs nothing per frame.
Because backdrop-filter is expensive, the demo blurs exactly three elements. If you find yourself putting glass on nested children, stop: the effect only works when there is a clear glass layer and a clear backdrop layer.
Make it yours
- Warm glass: tint the background with
color-mix(in oklch, var(--acc) 8%, transparent)instead of white — a coloured tint reads far more expensive than plain white at the same alpha. - Add a moving specular sheen with a
::afterlinear-gradient swept by a 6-secondtranslatekeyframe, clipped byoverflow: hidden. - Drop the blur radius to 10 px and raise the tint alpha for a lighter, faster variant suitable for mobile.
- Pair with
@supports not (backdrop-filter: blur(1px))to ship a solid translucent fallback — always define what the card looks like without glass.
Gotchas — read before shipping
backdrop-filteris a real GPU cost. Three or four glass surfaces per screen is the practical ceiling on mid-range mobile; a dozen will drop frames.- Glass over a flat background is invisible. If there is nothing textured behind it, you are just paying for a blur that does nothing.
- Text on glass fails contrast easily. Either raise the tint alpha under the text or add a
text-shadow: 0 1px 2px rgba(0,0,0,.35)— check the real ratio, do not eyeball it. - A parent with
overflow: hiddenandborder-radiuscan clip the backdrop sample in Safari, producing a hard edge. Test on WebKit specifically. backdrop-filtercreates a containing block for fixed descendants, which quietly breaks position: fixed inside glass panels.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), backdrop-filter, text-wrap as this demo is written. The core technique itself goes back further — Chrome 76+.
backdrop-filter is Chrome 76, Firefox 103 and Safari 9 with the -webkit- prefix (unprefixed from Safari 18). Always ship the -webkit-backdrop-filter line alongside, and provide a solid-background fallback inside @supports.