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 asrole="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 thecovercropping at small widths. -
Reduced motion and forced colours —
prefers-reduced-motionswitches the slide off,forced-colorsgives 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-originalrestores 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 —
getBoundingClientRectfor the boxes,getComputedStylefor 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