---
title: "Testimonial section HTML & CSS — responsive grid — Testymo"
description: "A responsive testimonials section in plain HTML and CSS: one grid line, no media queries. Copy the code, see it running, and read when masonry beats a grid."
url: https://testymo.com/free/testimonial-section-html
---

[Free tools](https://testymo.com/free) ★ Testimonial section HTML

# Testimonial section in HTML and CSS

Six quotes in a grid that reflows on its own — as many columns as the space allows, down to one on a phone, and not one media query anywhere in it. Copy the block below and the section is done.

A testimonial section is the block of customer quotes a page sets out together, usually as a responsive grid of cards under a heading. This page hands you a working one as plain HTML and CSS, for freelancers, agencies and small business owners who edit their own site and want every quote read rather than swiped past.

- **What it costs** — Nothing. No account, no email, no watermark, no limit on how often you use it.
- **Where your text goes** — Nowhere. The whole tool runs in your browser — nothing you type is sent, stored, or seen by us. The page-view measurement this site carries counts the visit, never the text.

Live preview — resize the window and watch the columns change

Sample quotes written for the demo — no customer wrote them.

The whole section — HTML and CSS, one block

3.9 KB · no JavaScript

```
<!-- Testimonial section — generated free at testymo.com/free/testimonial-html-generator -->
<section class="tm-testimonials">
  <style>
  .tm-testimonials { font: inherit; }
  .tm-list { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
  .tm-card { margin: 0; }
  .tm-card { background: #ffffff; border-radius: 8px; padding: 22px 24px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.09); }
  .tm-stars { margin: 0; color: #1233d4; letter-spacing: 3px; }
  .tm-quote { margin: 10px 0 16px; line-height: 1.55; }
  .tm-who { display: flex; align-items: center; gap: 10px; }
  .tm-avatar { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: #1233d4; color: #ffffff; font-size: 0.8em; font-weight: 700; }
  .tm-name { display: block; font-weight: 700; }
  .tm-role { display: block; font-size: 0.85em; opacity: 0.72; }
  </style>
  <div class="tm-list">
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</p>
      <blockquote class="tm-quote">They rebuilt our checkout in a week and conversions doubled. I keep waiting for the catch.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">JP</span>
        <span>
          <span class="tm-name">June Park</span>
          <span class="tm-role">COO, Fernwell</span>
        </span>
      </figcaption>
    </figure>
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</p>
      <blockquote class="tm-quote">Quietly the best agency we have worked with — no theatrics, no re-scoping, just the thing we asked for, early.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">AS</span>
        <span>
          <span class="tm-name">Amrit Sahani</span>
          <span class="tm-role">Founder, Loam</span>
        </span>
      </figcaption>
    </figure>
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</p>
      <blockquote class="tm-quote">They just get it.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">RD</span>
        <span>
          <span class="tm-name">Rafael Duarte</span>
          <span class="tm-role">CTO, Solstice</span>
        </span>
      </figcaption>
    </figure>
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</p>
      <blockquote class="tm-quote">The follow-up alone deserves five stars.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">NH</span>
        <span>
          <span class="tm-name">Nina Halvorsen</span>
          <span class="tm-role">Operations, Kestrel Freight</span>
        </span>
      </figcaption>
    </figure>
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 4 out of 5 stars">★★★★☆</p>
      <blockquote class="tm-quote">Truly lovely people. The invoice, less so.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">IF</span>
        <span>
          <span class="tm-name">Idris Farah</span>
          <span class="tm-role">Finance, Brindle</span>
        </span>
      </figcaption>
    </figure>
    <figure class="tm-card">
      <p class="tm-stars" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</p>
      <blockquote class="tm-quote">Nobody has ever complimented our checkout before. Two people did this month, unprompted, in the form.</blockquote>
      <figcaption class="tm-who">
        <span class="tm-avatar" aria-hidden="true">IF</span>
        <span>
          <span class="tm-name">Isabel Ferreira</span>
          <span class="tm-role">Ecommerce, Two Rivers</span>
        </span>
      </figcaption>
    </figure>
  </div>
</section>
```

## 01 The section is one grid line

Everything responsive about the preview above is in these two rules. There are no breakpoints in this snippet, because there is nothing left for a breakpoint to do:

```
.tm-list { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.tm-card { margin: 0; }
```

Read `repeat(auto-fit, minmax(240px, 1fr))` from the inside out. `minmax(240px, 1fr)` says every column is at least 240px wide and otherwise takes an equal share of whatever is left. `repeat(auto-fit, …)` says: fit as many of those columns as the container can hold. The result is that the layout responds to **the space the section is actually in**, not to the width of the browser window — drop this into a narrow sidebar and it goes to one column on a 27-inch monitor, which is the correct behaviour and the thing a media query gets wrong.

The `gap: 20px` is the whole gutter system. Cards carry `margin: 0`, so there is no final card’s margin poking out of the bottom of the section and no `:last-child` rule to write.

## 02 auto-fit, auto-fill, and the 240px floor

Two details in that one line decide whether the section looks right or looks broken, and both are easy to get wrong because they only misbehave in specific conditions.

**`auto-fit` versus `auto-fill`.** They are identical until you have fewer cards than there is room for columns. `auto-fill` keeps the empty columns: two testimonials in a four-column space stay 240px wide and huddle on the left of a mostly empty row. `auto-fit` collapses the empty tracks to zero width, so those two cards stretch and fill the row. For testimonials you almost always want `auto-fit` — a half-filled row of narrow cards reads as a rendering fault, and the number of testimonials you have is exactly the thing that changes over time.

**240px is a hard floor.** `minmax()` will not go below its minimum, so if the container is ever narrower than 240px the track overflows it and the page scrolls sideways. At the top level of a page that never happens; inside a builder column, a sidebar or a narrow modal it does. The one-character fix is to let the floor collapse:

```
grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
```

`min(240px, 100%)` is 240px while there is room for 240px, and the container width when there is not. Use the plain version for a full-width section and this one when you do not control where the block ends up.

## 03 Where a grid is wrong: uneven quotes, and masonry

A grid row is as tall as its tallest card. That is fine when your testimonials are roughly the same length and awkward when they are not — look at the preview at the top of this page on a wide screen and you can see it: the four-word quote is stretched to the height of the longest card in its row, and pays for the difference in empty space.

The fix is CSS multi-column, which is what “masonry” means once you take the JavaScript out of it. Two rules again, and the same markup:

```
.tm-list { columns: 3 240px; column-gap: 20px; }
.tm-card { break-inside: avoid; margin: 0 0 20px; }
```

`columns: 3 240px` is shorthand for at most three columns, each wanting 240px, so it also reflows on its own; `break-inside: avoid` is what stops a card being cut in half at the foot of a column. The same six quotes, same HTML:

Sample quotes written for the demo — no customer wrote them.

**The cost is reading order, and it is a real one.** Multi-column fills each column from top to bottom before it starts the next, so the eye goes down column one, back up, down column two. The document order is untouched — a screen reader and a crawler still get your quotes in the order you wrote them — but a sighted reader does not. If your strongest testimonial is meant to be read first, masonry will bury it in the middle of the left-hand column. And because the column count changes with width, which quote sits where changes with it: the arrangement you approved on a laptop is a different arrangement on a tablet.

So: grid when the quotes are of a similar length or their order matters, masonry when they are wildly uneven and any order will do. Copy whichever of the two CSS blocks you want into the same markup — [the generator](https://testymo.com/free/testimonial-html-generator) emits both, along with three other card looks.

## 04 The heading is yours, and the grid needs no ARIA

The snippet emits the cards, not the headline, because the headline is the one part that should be in your voice. Put a real heading above it at the level the page calls for:

```
<h2>What our customers say</h2>
<section class="tm-testimonials">
  ...
</section>
```

Everything below that is already carrying its own semantics. Each testimonial is a `<figure>` holding a real `<blockquote>` and a `<figcaption>`, so it is announced as a quotation with an attribution rather than as three unrelated lines of text. The star row is `role="img"` with an `aria-label`, which is the difference between hearing “Rated 5 out of 5 stars” and hearing ten bullet characters read out one by one.

What you should *not* add is a role on the grid itself. `display: grid` is a layout, and `role="grid"` means a data grid with rows, columns and keyboard cell navigation — putting it here tells a screen reader to announce a spreadsheet. The container needs no ARIA at all, which is the usual answer when a pattern is built out of the right elements.

## 05 Layout shift: text is what moves, not images

This is the pattern where layout shift is worth thinking about, because a grid’s height is decided entirely by how its text wraps — and the snippet deliberately owns as little of that as possible.

- **No images.** The avatar is a circle of initials, so there is no headshot arriving late at an unreserved size. The usual hand-built testimonials section ships an `<img>` per card with no `width` and `height`, and every one of those pushes its row down the moment it lands.
- **No font of its own.** `font: inherit` means the cards are set in the type your page already loaded, so the section adds no second swap.
- **Your font swap is the remaining risk.** If your page loads a webfont with `font-display: swap`, the fallback and the real face have different metrics, the tallest card in each row changes when the swap happens, and everything under the section moves. A size-adjusted fallback or `font-display: optional` fixes it for the whole page, not just here.

## 06 Make it yours, or stop re-pasting it

Colours, corner radius, card design and the quotes themselves are all editable in [the testimonial HTML generator](https://testymo.com/free/testimonial-html-generator), which emits this exact snippet from your own text. The two sibling patterns are [a scroll-snap slider](https://testymo.com/free/testimonial-slider) when you have more quotes than vertical space, and [a single testimonial card](https://testymo.com/free/testimonial-card-html) when one quote is doing the work. On a Tailwind project the grid line above has no utility of its own, and [the Tailwind page](https://testymo.com/free/testimonial-tailwind#grid) covers what to write instead. Where a section like this earns its place on a page — under the hero, beside the price, at the end — is worked through in [social proof sections](https://testymo.com/social-proof-examples).

Pasted HTML is frozen the moment you copy it, and that is the right trade for a handful of quotes that will not change for a year. It stops being the right trade when testimonials keep arriving: every new one is an edit to your site’s code, and the section quietly goes stale. That is where [a live widget](https://testymo.com/examples) earns its script tag — approve a quote and it is on the page at the next load.

## 07 Questions this page gets

- **How do I force exactly three columns?** — Replace `auto-fit` with a number: `repeat(3, minmax(0, 1fr))`. Use `minmax(0, 1fr)` rather than plain `1fr`, or a long unbroken word in a quote can push a column wider than its share. You will then need your own media query for narrow screens, which is the thing `auto-fit` was saving you.
- **Can I make all the cards the same height?** — They already are, within a row — grid items stretch by default. If you want the opposite, so each card hugs its own quote, that is the masonry version [above](#masonry), or `align-items: start` on the grid if you are happy with ragged rows.
- **Will it work in WordPress, Webflow, Wix or Squarespace?** — Yes, in any custom-HTML block: the CSS travels inside the snippet, so there is no stylesheet to upload and nothing to enqueue, and the output holds no JavaScript to be stripped. [The integrations pages](https://testymo.com/integrations) show where that block lives on each platform.
- **How many testimonials should a section have?** — Enough to fill whole rows and few enough to be read — three, six or nine works out neatly against a three-column grid. Past about nine, a visitor stops reading and starts counting, and you are better off with a [slider](https://testymo.com/free/testimonial-slider) or a page of its own.
- **Do I need a container or a reset for it?** — No. It is one `<section>` with its own `<style>` inside, and every class is prefixed `tm-` so nothing collides with your site’s styles. If your own global rules put margins on `blockquote`, that is the one thing worth checking after you paste.
- **Can I use these sample testimonials?** — No — they are invented for the demo and nobody said them. Swap in words a real customer wrote and agreed you could publish; [permission to use testimonials](https://testymo.com/blog/permission-to-use-testimonials) covers what to ask for and what to keep.

More free tools

- [Testimonial HTML generator I want to change the quotes and colors before I paste it.](https://testymo.com/free/testimonial-html-generator)
- [Testimonial slider How do I build a testimonial slider without a carousel library?](https://testymo.com/free/testimonial-slider)
- [Testimonial card HTML How do I style one testimonial card properly?](https://testymo.com/free/testimonial-card-html)
- [Tailwind testimonials How do I build these with Tailwind classes instead of CSS?](https://testymo.com/free/testimonial-tailwind)
- [Testimonial request generator How do I actually word the ask?](https://testymo.com/free/testimonial-request-generator)
- [Testimonial question picker What should I actually ask my customers?](https://testymo.com/free/testimonial-question-picker)
- [Testimonial image generator How do I turn this quote into something postable?](https://testymo.com/free/testimonial-image-generator)
- [Google review link generator How do I send someone straight to my Google review box?](https://testymo.com/free/google-review-link-generator)
- [Star rating calculator What's my average rating — and how many 5-star reviews until it moves?](https://testymo.com/free/star-rating-calculator)
- [Star rating HTML generator How do I show my star rating on my site — as code or an image?](https://testymo.com/free/star-rating-html-generator)
- [Review schema generator How do I mark up the testimonials on my page so Google can read them?](https://testymo.com/free/review-schema-generator)

The part no tool does

A tool can shape what you already have. Testymo is for getting it: a testimonial form people actually finish, your approval on every word before it goes public, and the permission recorded alongside it. Free to start — no card, no view meters.

[Collect your first testimonial →](https://testymo.com/app)
