Law Firm — Client Testimonials
An authoritative testimonials section with an auto-advancing carousel of client quotes carrying name, matter type and a five-star rating, manual previous and next controls, dot navigation, pause-on-hover, and a trust stats row showing years in practice, cases handled and client satisfaction.
MCP
Code
:root {
--navy: #1b2a4a;
--navy-d: #101b33;
--gold: #b08d57;
--gold-soft: #efe3cf;
--ink: #1a1f2b;
--muted: #5b6577;
--bg: #f5f3ee;
--white: #ffffff;
--serif: "Georgia", "Times New Roman", serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--radius: 4px;
--shadow: 0 18px 48px -28px rgba(16, 27, 51, 0.55);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--sans);
background: var(--bg);
color: var(--ink);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
padding: 48px 20px;
display: flex;
justify-content: center;
}
.testimonials {
width: 100%;
max-width: 760px;
}
/* ---- Header ---- */
.testimonials__head {
text-align: center;
margin-bottom: 36px;
}
.testimonials__eyebrow {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--gold);
}
.testimonials__title {
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.7rem, 4vw, 2.3rem);
color: var(--navy);
margin: 8px 0 14px;
letter-spacing: -0.01em;
}
.testimonials__rule {
display: inline-block;
width: 64px;
height: 2px;
background: var(--gold);
margin-bottom: 14px;
}
.testimonials__lede {
color: var(--muted);
font-size: 0.98rem;
max-width: 46ch;
margin: 0 auto;
}
/* ---- Carousel ---- */
.carousel {
position: relative;
background: var(--white);
border: 1px solid var(--gold-soft);
border-top: 3px solid var(--gold);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 44px 64px 30px;
}
.carousel__viewport {
overflow: hidden;
}
.carousel__track {
display: flex;
list-style: none;
transition: transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.slide {
flex: 0 0 100%;
min-width: 100%;
text-align: center;
padding: 4px 2px;
}
.slide__rating {
color: var(--gold);
font-size: 1.15rem;
letter-spacing: 0.18em;
margin-bottom: 18px;
}
.slide__quote {
font-family: var(--serif);
font-size: clamp(1.05rem, 2.4vw, 1.32rem);
line-height: 1.55;
color: var(--ink);
position: relative;
padding: 0 6px;
min-height: 7.5em;
}
.slide__quote::before {
content: "\201C";
font-family: var(--serif);
position: absolute;
top: -0.55em;
left: 50%;
transform: translateX(-50%);
font-size: 3.4rem;
line-height: 1;
color: var(--gold-soft);
z-index: 0;
}
.slide__client {
margin-top: 22px;
display: flex;
flex-direction: column;
gap: 3px;
}
.slide__name {
font-weight: 600;
color: var(--navy);
font-size: 1rem;
}
.slide__matter {
font-size: 0.78rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
/* ---- Nav arrows ---- */
.carousel__nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
border: 1px solid var(--gold-soft);
background: var(--white);
color: var(--navy);
font-size: 1.4rem;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.2s, color 0.2s, border-color 0.2s, transform 0.15s;
}
.carousel__nav:hover {
background: var(--navy);
color: var(--white);
border-color: var(--navy);
}
.carousel__nav:active {
transform: translateY(-50%) scale(0.94);
}
.carousel__nav:focus-visible {
outline: 2px solid var(--gold);
outline-offset: 2px;
}
.carousel__nav--prev { left: 12px; }
.carousel__nav--next { right: 12px; }
/* ---- Dots ---- */
.carousel__dots {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 26px;
}
.dot {
width: 9px;
height: 9px;
border-radius: 50%;
border: none;
padding: 0;
background: var(--gold-soft);
cursor: pointer;
transition: background 0.2s, transform 0.2s;
}
.dot:hover { background: var(--gold); }
.dot[aria-selected="true"] {
background: var(--gold);
transform: scale(1.35);
}
.dot:focus-visible {
outline: 2px solid var(--navy);
outline-offset: 3px;
}
/* ---- Stats ---- */
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid var(--gold-soft);
}
.stats__item {
text-align: center;
position: relative;
}
.stats__item + .stats__item::before {
content: "";
position: absolute;
left: 0;
top: 10%;
height: 80%;
width: 1px;
background: var(--gold-soft);
}
.stats__value {
font-family: var(--serif);
font-size: clamp(1.6rem, 5vw, 2.2rem);
font-weight: 600;
color: var(--navy);
line-height: 1;
}
.stats__label {
margin-top: 8px;
font-size: 0.72rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
/* ---- Responsive ---- */
@media (max-width: 520px) {
body { padding: 28px 14px; }
.carousel {
padding: 36px 18px 22px;
}
.carousel__nav--prev { left: 4px; }
.carousel__nav--next { right: 4px; }
.carousel__nav {
width: 34px;
height: 34px;
font-size: 1.2rem;
}
.slide__quote { min-height: 9.5em; }
.stats {
grid-template-columns: 1fr;
gap: 0;
}
.stats__item {
padding: 14px 0;
}
.stats__item + .stats__item::before {
left: 15%;
top: 0;
height: 1px;
width: 70%;
}
}
@media (prefers-reduced-motion: reduce) {
.carousel__track { transition: none; }
}(function () {
"use strict";
var carousel = document.getElementById("carousel");
var track = document.getElementById("track");
var prevBtn = document.getElementById("prev");
var nextBtn = document.getElementById("next");
var dotsWrap = document.getElementById("dots");
if (!carousel || !track) return;
var slides = Array.prototype.slice.call(track.querySelectorAll(".slide"));
var total = slides.length;
var index = 0;
var timer = null;
var INTERVAL = 6000;
var reduceMotion = window.matchMedia
? window.matchMedia("(prefers-reduced-motion: reduce)").matches
: false;
/* Build dot navigation */
var dots = [];
slides.forEach(function (slide, i) {
var dot = document.createElement("button");
dot.type = "button";
dot.className = "dot";
dot.setAttribute("role", "tab");
dot.setAttribute("aria-label", "Go to testimonial " + (i + 1));
dot.addEventListener("click", function () {
goTo(i, true);
});
dotsWrap.appendChild(dot);
dots.push(dot);
});
function render() {
track.style.transform = "translateX(" + (-index * 100) + "%)";
slides.forEach(function (slide, i) {
slide.setAttribute("aria-hidden", i === index ? "false" : "true");
});
dots.forEach(function (dot, i) {
dot.setAttribute("aria-selected", i === index ? "true" : "false");
});
}
function goTo(i, fromUser) {
index = (i + total) % total;
render();
if (fromUser) restart();
}
function next(fromUser) {
goTo(index + 1, fromUser);
}
function prev(fromUser) {
goTo(index - 1, fromUser);
}
function start() {
if (reduceMotion || timer) return;
timer = window.setInterval(function () {
next(false);
}, INTERVAL);
}
function stop() {
if (timer) {
window.clearInterval(timer);
timer = null;
}
}
function restart() {
stop();
start();
}
nextBtn.addEventListener("click", function () {
next(true);
});
prevBtn.addEventListener("click", function () {
prev(true);
});
/* Pause on hover / focus, resume on leave / blur */
carousel.addEventListener("mouseenter", stop);
carousel.addEventListener("mouseleave", start);
carousel.addEventListener("focusin", stop);
carousel.addEventListener("focusout", start);
/* Keyboard arrows when carousel has focus */
carousel.addEventListener("keydown", function (e) {
if (e.key === "ArrowRight") {
e.preventDefault();
next(true);
} else if (e.key === "ArrowLeft") {
e.preventDefault();
prev(true);
}
});
/* Pause when tab is hidden */
document.addEventListener("visibilitychange", function () {
if (document.hidden) stop();
else start();
});
/* Animated stat counters, triggered once on view */
function animateCounters() {
var values = document.querySelectorAll(".stats__value");
values.forEach(function (el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var suffix = el.getAttribute("data-suffix") || "";
if (reduceMotion) {
el.textContent = target + suffix;
return;
}
var startTime = null;
var duration = 1400;
function step(ts) {
if (!startTime) startTime = ts;
var p = Math.min((ts - startTime) / duration, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(eased * target) + suffix;
if (p < 1) window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
});
}
var statsEl = document.querySelector(".stats");
if (statsEl && "IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
animateCounters();
io.disconnect();
}
});
},
{ threshold: 0.4 }
);
io.observe(statsEl);
} else {
animateCounters();
}
render();
start();
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Client Testimonials — Hartwell & Crane LLP</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<section class="testimonials" aria-labelledby="ts-heading">
<header class="testimonials__head">
<p class="testimonials__eyebrow">Hartwell & Crane LLP</p>
<h2 id="ts-heading" class="testimonials__title">What our clients say</h2>
<span class="testimonials__rule" aria-hidden="true"></span>
<p class="testimonials__lede">
Three decades of advocacy, told in the words of the people we represented.
</p>
</header>
<div class="carousel" id="carousel" aria-roledescription="carousel" aria-label="Client testimonials">
<button class="carousel__nav carousel__nav--prev" id="prev" type="button" aria-label="Previous testimonial">
<span aria-hidden="true">‹</span>
</button>
<div class="carousel__viewport">
<ul class="carousel__track" id="track" aria-live="polite">
<li class="slide" role="group" aria-roledescription="slide" aria-label="1 of 5">
<div class="slide__rating" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</div>
<blockquote class="slide__quote">
They turned an overwhelming custody dispute into a clear, steady plan. I always knew
where my case stood, and the outcome protected what mattered most — time with my kids.
</blockquote>
<figcaption class="slide__client">
<span class="slide__name">Marguerite Doyle</span>
<span class="slide__matter">Family Law · Custody</span>
</figcaption>
</li>
<li class="slide" role="group" aria-roledescription="slide" aria-label="2 of 5">
<div class="slide__rating" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</div>
<blockquote class="slide__quote">
After my accident, the insurer offered almost nothing. The team built the case,
negotiated hard, and recovered far more than I thought possible — without ever pressuring
me to settle early.
</blockquote>
<figcaption class="slide__client">
<span class="slide__name">Desmond Achebe</span>
<span class="slide__matter">Personal Injury · Settlement</span>
</figcaption>
</li>
<li class="slide" role="group" aria-roledescription="slide" aria-label="3 of 5">
<div class="slide__rating" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</div>
<blockquote class="slide__quote">
We restructured the company through a difficult acquisition. Their counsel was precise,
calm and commercially minded — they protected the deal and our people at every turn.
</blockquote>
<figcaption class="slide__client">
<span class="slide__name">Priya Venkataraman</span>
<span class="slide__matter">Corporate · M&A</span>
</figcaption>
</li>
<li class="slide" role="group" aria-roledescription="slide" aria-label="4 of 5">
<div class="slide__rating" role="img" aria-label="Rated 4 out of 5 stars">★★★★☆</div>
<blockquote class="slide__quote">
Estate planning felt daunting until I sat with this firm. They explained every clause
in plain language and left my family with a will and trust we genuinely understand.
</blockquote>
<figcaption class="slide__client">
<span class="slide__name">Walter Brennan</span>
<span class="slide__matter">Estate Planning · Trusts</span>
</figcaption>
</li>
<li class="slide" role="group" aria-roledescription="slide" aria-label="5 of 5">
<div class="slide__rating" role="img" aria-label="Rated 5 out of 5 stars">★★★★★</div>
<blockquote class="slide__quote">
Facing a serious charge, I needed someone who would fight and tell me the truth. They
did both. The defense was meticulous and the result changed the course of my life.
</blockquote>
<figcaption class="slide__client">
<span class="slide__name">Yusuf Halloran</span>
<span class="slide__matter">Criminal Defense · Trial</span>
</figcaption>
</li>
</ul>
</div>
<button class="carousel__nav carousel__nav--next" id="next" type="button" aria-label="Next testimonial">
<span aria-hidden="true">›</span>
</button>
<div class="carousel__dots" id="dots" role="tablist" aria-label="Choose testimonial"></div>
</div>
<dl class="stats" aria-label="Firm record">
<div class="stats__item">
<dt class="stats__label">Years in practice</dt>
<dd class="stats__value" data-count="34" data-suffix="">0</dd>
</div>
<div class="stats__item">
<dt class="stats__label">Cases handled</dt>
<dd class="stats__value" data-count="2800" data-suffix="+">0</dd>
</div>
<div class="stats__item">
<dt class="stats__label">Client satisfaction</dt>
<dd class="stats__value" data-count="98" data-suffix="%">0</dd>
</div>
</dl>
</section>
<script src="script.js"></script>
</body>
</html>Client Testimonials
A trust-first testimonials block in an authoritative law-firm style — navy and warm paper with a thin gold rule and serif headings. The centerpiece is a quote carousel: each slide shows a client name, the matter type (Family Law, Personal Injury, Estate Planning…) and a five-star rating above a generous pull quote.
The carousel auto-advances every few seconds, pauses on hover or keyboard focus, and resumes when you leave. Visitors can step through manually with the previous / next arrows or jump straight to a slide via the dot navigation. A stats row underneath reinforces credibility with years in practice, cases handled and client satisfaction.
Everything is vanilla JS — no libraries — with accessible markup: a labelled region, aria-live
slide announcements, real button controls and visible focus styles. A responsive breakpoint stacks
the stats and tightens spacing on small screens.
Illustrative UI only — not legal advice and not a real attorney endorsement.