12 CSS Masonry Layouts08 / 12

CSS onlyMIT licensed

CSS Masonry Testimonial Wall Review Cards

The SaaS 'wall of love': one-line raves and paragraph-long reviews packed gap-free by CSS columns, star strips, a highlighted pull-quote card, and a big stat card sitting inside the flow. The wall fades out through a mask-image gradient into a 'Read all 2,314 reviews' call-to-action — the tasteful way to truncate social proof — and where scroll-driven animations are supported, cards float in as they enter the viewport. All CSS.

Published

Live Demo
Try it

The code

<section class="msn-08" aria-label="CSS masonry testimonial wall demo">
  <div class="msn-08__stage" tabindex="0">
    <header class="msn-08__head">
      <span class="msn-08__eyebrow">Wall of love</span>
      <h2>Teams ship faster on Lattice CI</h2>
      <p>2,314 verified reviews · 4.9 average</p>
    </header>
    <div class="msn-08__wallWrap">
      <div class="msn-08__wall">
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Cut our pipeline from 22 minutes to 6. The team thought the dashboard was lying.</blockquote><footer><i style="--a:oklch(0.78 0.1 40);--b:oklch(0.55 0.13 20)"></i><div><strong>Maya Lindqvist</strong><span>Platform lead, Fathom</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Onboarded 40 repos in an afternoon.</blockquote><footer><i style="--a:oklch(0.75 0.11 300);--b:oklch(0.5 0.13 330)"></i><div><strong>Dev Anand</strong><span>CTO, Brightline</span></div></footer></article>
        <article class="msn-08__card msn-08__card--accent"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>The flaky-test quarantine alone paid for the year. Our on-call channel went from a horror feed to weather smalltalk. I have never seen a tool change team mood this fast.</blockquote><footer><i style="--a:oklch(0.85 0.08 90);--b:oklch(0.6 0.1 70)"></i><div><strong>Grace Obi</strong><span>Eng director, Northwind</span></div></footer></article>
        <article class="msn-08__stat"><strong>4.9</strong><p class="msn-08__stars" style="--r:98%" aria-hidden="true"></p><span class="msn-08__sr">4.9 out of 5 average</span><div class="msn-08__bars"><i style="--w:88%"></i><i style="--w:9%"></i><i style="--w:2%"></i><i style="--w:1%"></i><i style="--w:0%"></i></div><p>from 2,314 reviews on G2 &amp; Capterra</p></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:90%" aria-hidden="true"></p><span class="msn-08__sr">4.5 out of 5</span><blockquote>Migrated off Jenkins in a week. The PR annotations are so good our juniors stopped asking 'why did CI fail' in standup — the answer is already on the diff line.</blockquote><footer><i style="--a:oklch(0.72 0.09 220);--b:oklch(0.48 0.11 240)"></i><div><strong>Tomás Rivera</strong><span>Staff eng, Kelp</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Support answered at 2am on a Saturday. With the fix.</blockquote><footer><i style="--a:oklch(0.8 0.1 150);--b:oklch(0.55 0.12 170)"></i><div><strong>Hana Sato</strong><span>SRE, Loom &amp; Field</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>We stopped writing 'CI is red, not my PR' in Slack. That sentence used to cost us an hour a day across the org.</blockquote><footer><i style="--a:oklch(0.7 0.12 330);--b:oklch(0.46 0.13 350)"></i><div><strong>Jonah Weiss</strong><span>Tech lead, Parcel</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:95%" aria-hidden="true"></p><span class="msn-08__sr">4.7 out of 5</span><blockquote>Pricing that doesn't punish you for growing the team. Refreshing.</blockquote><footer><i style="--a:oklch(0.76 0.1 190);--b:oklch(0.5 0.11 210)"></i><div><strong>Amelia Clarke</strong><span>VP Eng, Statement</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>The audit log export made our SOC 2 auditor smile. I didn't know they could.</blockquote><footer><i style="--a:oklch(0.82 0.07 60);--b:oklch(0.58 0.1 40)"></i><div><strong>Ravi Kapoor</strong><span>Security, Alpenglow</span></div></footer></article>
      </div>
    </div>
    <div class="msn-08__readAll"><a href="#reviews">Read all 2,314 reviews</a><span>Independently verified · updated weekly</span></div>
  </div>
</section>
.msn-08,
.msn-08 *,
.msn-08 *::before,
.msn-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.msn-08 {
  --bg: oklch(0.975 0.008 85);
  --card: oklch(0.995 0.003 85);
  --ink: oklch(0.25 0.02 60);
  --mut: oklch(0.53 0.02 60);
  --line: oklch(0.9 0.01 85);
  --acc: oklch(0.55 0.16 155);
  --star: oklch(0.72 0.15 75);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.msn-08__stage {
  width: 100%;
  container: msn08/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.msn-08__head {
  text-align: center;
  padding: clamp(28px,6cqi,52px) 20px 8px;
  display: grid;
  gap: 8px;
  justify-items: center;
}

.msn-08__eyebrow {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--acc);
}

.msn-08__head h2 {
  font-size: clamp(22px,3.6cqi,34px);
  letter-spacing: -.025em;
  text-wrap: balance;
  max-width: 22ch;
}

.msn-08__head p {
  font-size: 13px;
  color: var(--mut);
}

.msn-08__wallWrap {
  mask-image: linear-gradient(180deg,#000 72%,transparent 97%);
}

.msn-08__wall {
  columns: 280px;
  column-gap: 14px;
  padding: clamp(18px,3cqi,30px);
}

.msn-08__card,
.msn-08__stat {
  break-inside: avoid;
  margin-bottom: 14px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--card);
  padding: 18px 18px 16px;
  display: grid;
  gap: 11px;
  transition: translate .25s,box-shadow .25s;
}

.msn-08__card:hover {
  translate: 0 -3px;
  box-shadow: 0 12px 26px -14px oklch(0.4 0.05 60/.3);
}

.msn-08__stars {
  --pct: var(--r,100%);
  font-size: 13px;
  letter-spacing: 2.5px;
  line-height: 1;
}

.msn-08__stars::before {
  content: "★★★★★";
  background: linear-gradient(90deg,var(--star) var(--pct),oklch(0.88 0.01 85) var(--pct));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.msn-08__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.msn-08__card {
  position: relative;
}

.msn-08__card blockquote {
  font-size: 14px;
  line-height: 1.6;
  text-wrap: pretty;
}

.msn-08__card blockquote::before {
  content: "“";
  display: block;
  font-family: Georgia,serif;
  font-size: 30px;
  line-height: .4;
  color: var(--acc);
  margin-bottom: 8px;
}

.msn-08__card footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}

.msn-08__card footer i {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex: none;
  background: linear-gradient(140deg,var(--a),var(--b));
}

.msn-08__card footer strong {
  display: block;
  font-size: 12.5px;
}

.msn-08__card footer span {
  display: block;
  font-size: 11.5px;
  color: var(--mut);
}

.msn-08__card--accent {
  background: var(--ink);
  border-color: var(--ink);
  color: oklch(0.97 0.005 85);
}

.msn-08__card--accent blockquote::before {
  color: oklch(0.8 0.12 155);
}

.msn-08__card--accent footer span {
  color: oklch(0.72 0.015 60);
}

.msn-08__stat {
  position: relative;
  text-align: left;
}

.msn-08__stat>strong {
  font-size: 44px;
  letter-spacing: -.04em;
  line-height: 1;
}

.msn-08__bars {
  display: grid;
  gap: 4px;
}

.msn-08__bars i {
  height: 7px;
  border-radius: 99px;
  background: linear-gradient(90deg,var(--acc) var(--w),oklch(0.92 0.01 85) var(--w));
}

.msn-08__stat p:last-child {
  font-size: 12px;
  color: var(--mut);
}

.msn-08__readAll {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 0 20px clamp(28px,5cqi,48px);
}

.msn-08__readAll a {
  display: grid;
  place-items: center;
  min-height: 46px;
  padding: 0 24px;
  border-radius: 99px;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  color: oklch(0.99 0 0);
  background: var(--acc);
  transition: background .2s,translate .2s;
}

.msn-08__readAll a:hover,
.msn-08__readAll a:focus-visible {
  background: oklch(0.48 0.16 155);
  translate: 0 -1px;
}

.msn-08__readAll a:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.msn-08__readAll span {
  font-size: 11.5px;
  color: var(--mut);
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes msn08-in {
      from {
        opacity: 0;
        translate: 0 22px;
      }

      to {
        opacity: 1;
        translate: 0 0;
      }
    }

    .msn-08__card,
        .msn-08__stat {
      animation: msn08-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

@container msn08 (width < 480px) {
  .msn-08__wall {
    columns: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .msn-08 * {
    transition-duration: .01ms !important;
    animation: none !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Masonry Layout 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: CSS Masonry Testimonial Wall Review Cards
Source: https://codefronts.com/layouts/css-masonry-layouts/css-masonry-testimonial-wall-review-cards/

The SaaS 'wall of love': one-line raves and paragraph-long reviews packed gap-free by CSS columns, star strips, a highlighted pull-quote card, and a big stat card sitting inside the flow. The wall fades out through a mask-image gradient into a 'Read all 2,314 reviews' call-to-action — the tasteful way to truncate social proof — and where scroll-driven animations are supported, cards float in as they enter the viewport. All CSS.
## HTML
```html
<section class="msn-08" aria-label="CSS masonry testimonial wall demo">
  <div class="msn-08__stage" tabindex="0">
    <header class="msn-08__head">
      <span class="msn-08__eyebrow">Wall of love</span>
      <h2>Teams ship faster on Lattice CI</h2>
      <p>2,314 verified reviews · 4.9 average</p>
    </header>
    <div class="msn-08__wallWrap">
      <div class="msn-08__wall">
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Cut our pipeline from 22 minutes to 6. The team thought the dashboard was lying.</blockquote><footer><i style="--a:oklch(0.78 0.1 40);--b:oklch(0.55 0.13 20)"></i><div><strong>Maya Lindqvist</strong><span>Platform lead, Fathom</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Onboarded 40 repos in an afternoon.</blockquote><footer><i style="--a:oklch(0.75 0.11 300);--b:oklch(0.5 0.13 330)"></i><div><strong>Dev Anand</strong><span>CTO, Brightline</span></div></footer></article>
        <article class="msn-08__card msn-08__card--accent"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>The flaky-test quarantine alone paid for the year. Our on-call channel went from a horror feed to weather smalltalk. I have never seen a tool change team mood this fast.</blockquote><footer><i style="--a:oklch(0.85 0.08 90);--b:oklch(0.6 0.1 70)"></i><div><strong>Grace Obi</strong><span>Eng director, Northwind</span></div></footer></article>
        <article class="msn-08__stat"><strong>4.9</strong><p class="msn-08__stars" style="--r:98%" aria-hidden="true"></p><span class="msn-08__sr">4.9 out of 5 average</span><div class="msn-08__bars"><i style="--w:88%"></i><i style="--w:9%"></i><i style="--w:2%"></i><i style="--w:1%"></i><i style="--w:0%"></i></div><p>from 2,314 reviews on G2 &amp; Capterra</p></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:90%" aria-hidden="true"></p><span class="msn-08__sr">4.5 out of 5</span><blockquote>Migrated off Jenkins in a week. The PR annotations are so good our juniors stopped asking 'why did CI fail' in standup — the answer is already on the diff line.</blockquote><footer><i style="--a:oklch(0.72 0.09 220);--b:oklch(0.48 0.11 240)"></i><div><strong>Tomás Rivera</strong><span>Staff eng, Kelp</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>Support answered at 2am on a Saturday. With the fix.</blockquote><footer><i style="--a:oklch(0.8 0.1 150);--b:oklch(0.55 0.12 170)"></i><div><strong>Hana Sato</strong><span>SRE, Loom &amp; Field</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>We stopped writing 'CI is red, not my PR' in Slack. That sentence used to cost us an hour a day across the org.</blockquote><footer><i style="--a:oklch(0.7 0.12 330);--b:oklch(0.46 0.13 350)"></i><div><strong>Jonah Weiss</strong><span>Tech lead, Parcel</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:95%" aria-hidden="true"></p><span class="msn-08__sr">4.7 out of 5</span><blockquote>Pricing that doesn't punish you for growing the team. Refreshing.</blockquote><footer><i style="--a:oklch(0.76 0.1 190);--b:oklch(0.5 0.11 210)"></i><div><strong>Amelia Clarke</strong><span>VP Eng, Statement</span></div></footer></article>
        <article class="msn-08__card"><p class="msn-08__stars" style="--r:100%" aria-hidden="true"></p><span class="msn-08__sr">5 out of 5</span><blockquote>The audit log export made our SOC 2 auditor smile. I didn't know they could.</blockquote><footer><i style="--a:oklch(0.82 0.07 60);--b:oklch(0.58 0.1 40)"></i><div><strong>Ravi Kapoor</strong><span>Security, Alpenglow</span></div></footer></article>
      </div>
    </div>
    <div class="msn-08__readAll"><a href="#reviews">Read all 2,314 reviews</a><span>Independently verified · updated weekly</span></div>
  </div>
</section>
```
## CSS
```css
.msn-08,
.msn-08 *,
.msn-08 *::before,
.msn-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.msn-08 {
  --bg: oklch(0.975 0.008 85);
  --card: oklch(0.995 0.003 85);
  --ink: oklch(0.25 0.02 60);
  --mut: oklch(0.53 0.02 60);
  --line: oklch(0.9 0.01 85);
  --acc: oklch(0.55 0.16 155);
  --star: oklch(0.72 0.15 75);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.msn-08__stage {
  width: 100%;
  container: msn08/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.msn-08__head {
  text-align: center;
  padding: clamp(28px,6cqi,52px) 20px 8px;
  display: grid;
  gap: 8px;
  justify-items: center;
}

.msn-08__eyebrow {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--acc);
}

.msn-08__head h2 {
  font-size: clamp(22px,3.6cqi,34px);
  letter-spacing: -.025em;
  text-wrap: balance;
  max-width: 22ch;
}

.msn-08__head p {
  font-size: 13px;
  color: var(--mut);
}

.msn-08__wallWrap {
  mask-image: linear-gradient(180deg,#000 72%,transparent 97%);
}

.msn-08__wall {
  columns: 280px;
  column-gap: 14px;
  padding: clamp(18px,3cqi,30px);
}

.msn-08__card,
.msn-08__stat {
  break-inside: avoid;
  margin-bottom: 14px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--card);
  padding: 18px 18px 16px;
  display: grid;
  gap: 11px;
  transition: translate .25s,box-shadow .25s;
}

.msn-08__card:hover {
  translate: 0 -3px;
  box-shadow: 0 12px 26px -14px oklch(0.4 0.05 60/.3);
}

.msn-08__stars {
  --pct: var(--r,100%);
  font-size: 13px;
  letter-spacing: 2.5px;
  line-height: 1;
}

.msn-08__stars::before {
  content: "★★★★★";
  background: linear-gradient(90deg,var(--star) var(--pct),oklch(0.88 0.01 85) var(--pct));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.msn-08__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.msn-08__card {
  position: relative;
}

.msn-08__card blockquote {
  font-size: 14px;
  line-height: 1.6;
  text-wrap: pretty;
}

.msn-08__card blockquote::before {
  content: "“";
  display: block;
  font-family: Georgia,serif;
  font-size: 30px;
  line-height: .4;
  color: var(--acc);
  margin-bottom: 8px;
}

.msn-08__card footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}

.msn-08__card footer i {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex: none;
  background: linear-gradient(140deg,var(--a),var(--b));
}

.msn-08__card footer strong {
  display: block;
  font-size: 12.5px;
}

.msn-08__card footer span {
  display: block;
  font-size: 11.5px;
  color: var(--mut);
}

.msn-08__card--accent {
  background: var(--ink);
  border-color: var(--ink);
  color: oklch(0.97 0.005 85);
}

.msn-08__card--accent blockquote::before {
  color: oklch(0.8 0.12 155);
}

.msn-08__card--accent footer span {
  color: oklch(0.72 0.015 60);
}

.msn-08__stat {
  position: relative;
  text-align: left;
}

.msn-08__stat>strong {
  font-size: 44px;
  letter-spacing: -.04em;
  line-height: 1;
}

.msn-08__bars {
  display: grid;
  gap: 4px;
}

.msn-08__bars i {
  height: 7px;
  border-radius: 99px;
  background: linear-gradient(90deg,var(--acc) var(--w),oklch(0.92 0.01 85) var(--w));
}

.msn-08__stat p:last-child {
  font-size: 12px;
  color: var(--mut);
}

.msn-08__readAll {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 0 20px clamp(28px,5cqi,48px);
}

.msn-08__readAll a {
  display: grid;
  place-items: center;
  min-height: 46px;
  padding: 0 24px;
  border-radius: 99px;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  color: oklch(0.99 0 0);
  background: var(--acc);
  transition: background .2s,translate .2s;
}

.msn-08__readAll a:hover,
.msn-08__readAll a:focus-visible {
  background: oklch(0.48 0.16 155);
  translate: 0 -1px;
}

.msn-08__readAll a:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.msn-08__readAll span {
  font-size: 11.5px;
  color: var(--mut);
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes msn08-in {
      from {
        opacity: 0;
        translate: 0 22px;
      }

      to {
        opacity: 1;
        translate: 0 0;
      }
    }

    .msn-08__card,
        .msn-08__stat {
      animation: msn08-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

@container msn08 (width < 480px) {
  .msn-08__wall {
    columns: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .msn-08 * {
    transition-duration: .01ms !important;
    animation: none !important;
  }
}
```

How this works

Review copy is the most height-unpredictable content on the web, which makes it multicol's home game. Two techniques carry the design. First, the fade-to-CTA bottom:

.wallWrap{
  mask-image: linear-gradient(180deg, #000 72%, transparent 97%);
}
.readAll{ /* sits AFTER the masked wall, not over it */ }

Masking the WRAPPER (not a white overlay div) means the fade works over any page background — gradients, photos, dark mode — because it erases the wall itself rather than painting an approximation of the backdrop on top. The CTA then sits in normal flow; no absolute positioning, no z-index, no magic heights.

Second, entrance choreography with zero JavaScript and zero scroll listeners:

@supports (animation-timeline: view()){
  .card{ animation: msn08-in linear both;
         animation-timeline: view();
         animation-range: entry 0% entry 60%; }
}

view() ties the keyframes to the card's own journey across the viewport — the browser drives the animation off the compositor thread, so it stays smooth mid-scroll where IntersectionObserver handlers jank. Unsupported browsers simply show settled cards, and prefers-reduced-motion turns the whole thing off. The stat card (4.9 average, bar-chart of star distribution drawn as gradient strips) and the accent pull-quote card break the visual monotony — masonry walls need landmarks or they read as noise.

Make it yours

  • Fade depth: the mask's 72%…97% stops control how much of the last row ghosts out; 60% is a heavier tease, 85% barely a hint.
  • Landmark cadence: one accent card + one stat card per ~10 reviews is the rhythm; the accent card is just a class flipping four tokens to the brand color.
  • Star strips reuse demo 05's gradient-clip technique with a per-card --r; wire it to your review score field and fractional fills come free.
  • Kill the fade for a full archive page: delete the mask line and the CTA — the same wall IS the /reviews route; columns count adapts to the wider main column by itself.

Gotchas — read before shipping

  • Never fake the fade with a white gradient overlay — it breaks on any non-white background and blocks clicks on the cards beneath it. mask-image erases, overlays paint.
  • Quote marks belong in CSS (q::before or a pseudo on the card), not in the copy — CMS-entered smart quotes double up and typography dies.
  • animation-range: entry needs both fill or cards flash unstyled for one frame before the timeline engages in Chrome.
  • Anonymous reviews sink trust walls: the demo's name + role + company line is the minimum viable attribution; avatarless is fine, nameless is not.

Browser support

ChromeSafariFirefoxEdge
115+16.4+113+115+

Scroll-driven animations gate on Chrome 115+/Firefox 144 and are wrapped in @supports — every other feature (mask-image, columns, oklch) meets the 16.4/113 floor. Safari shows the wall settled.

Techniques used in this demo

Search CodeFronts

Loading…