← The library

Testimonial hero

A two-column hero with a review card

Members only

What's included

The first screen of a site in two columns: eyebrow, heading, paragraph, call to action and social proof on the left; a photo with a bevelled corner on the right, with a review card pinned 8 px from its bottom right corner.

A single stylesheet, its three fonts and its images. No dependencies, no build step, no external requests, and not a line of JavaScript: the slide of the button's label fits into one CSS transition triggered by :hover et :focus-visible.

Features

  • A button that answers the keyboard too — its label rises out of a one-line window while a copy arrives from below, and its arrow leaves to the right while a copy enters from the left. 350 ms on cubic-bezier(.25,.46,.45,.94), on hover and on focus alike.
  • It folds to its column, not to the window — the narrow breakpoints are written as @containerqueries. Dropped into a 460 px column in the middle of a wide screen, it goes to one column as it would on a phone.
  • One variable for the scale — every measurement derives from --prh-font-size, the body text size: heading, spacing, corner radii, card and photo all follow together.
  • The review card is semantic markup<figure>, <blockquote> et <figcaption>, the rating as role="img" with its label, the stars as decoration.
  • The bevel is in the image — the photo's cut corner comes from its alpha channel, not from a clip-path: it survives the cover cropping at small widths.
  • Reduced motion and forced coloursprefers-reduced-motion switches the slide off, forced-colors gives the button and the card a visible outline.

Dropping it into a page

One stylesheet, then the markup:

<link rel="stylesheet" href="testimonial-hero.css">
<section class="testimonial-hero">
  <div class="testimonial-hero__grille">
    <div class="testimonial-hero__colonne">
      <div class="testimonial-hero__intro">
        <div class="testimonial-hero__entete">
          <p class="testimonial-hero__surtitre">Consultancy</p>
          <h1 class="testimonial-hero__titre">Measurable</h1>
        </div>
        <p class="testimonial-hero__texte">A sentence.</p>
      </div>
      <div class="testimonial-hero__rangee">
        <a class="testimonial-hero__bouton" href="#contact">
          <span class="testimonial-hero__libelles">
            <span class="testimonial-hero__libelle">Book a call</span>
            <span class="testimonial-hero__libelle"
                  aria-hidden="true">Book a call</span>
          </span>
          <span class="testimonial-hero__fleches" aria-hidden="true">
            <svg class="testimonial-hero__fleche">…</svg>
            <svg class="testimonial-hero__fleche
                        testimonial-hero__fleche--relais">…</svg>
          </span>
        </a>
        <p class="testimonial-hero__confiance">…</p>
      </div>
    </div>
    <div class="testimonial-hero__media">
      <img class="testimonial-hero__photo" src="hero.webp" alt="…">
      <figure class="testimonial-hero__avis">…</figure>
    </div>
  </div>
</section>

The full markup is in demo.html. To change the photo, replace hero.webp and give its aspect ratio to --prh-photo-ratio — that's what reserves the room before the load.

What you should know

  • The button's label is no longer cut off. In the original, the row is oversubscribed: the button and the note ask for 625.68 px in a 612.63 px column, the label wraps onto two lines and the one-line window cuts it off — you read “START A FREE” instead of “START A FREE MEETING”. Here the button no longer compresses and the note absorbs the overflow. data-prh-original restores the original behaviour exactly.
  • The component has no script. It renders the same with and without JavaScript, under a strict security policy, and has neither inline styles nor inline scripts.
  • The design comes from elsewhere. The proportions follow those of a Webflow template hero, measured on the live site — getBoundingClientRect for the boxes, getComputedStyle for the typography, and the durations read out of its animation engine. The code itself is original. The photo, the portraits and the star come from the template; the Cambo, Geist Mono and Aspekta fonts are under the SIL Open Font Licence.

Take it, open it, break it.

This component is free. The full library — the templates and the other components — comes with the subscription, alongside the course.

Immediate access · No commitment · Cancel in one click