Create a single-quote testimonial slider with Tailwind CSS on a light panel. Centre a small uppercase eyebrow, then a large serif-weight quote in text-2xl that has room for three lines, an attribution block with an avatar circle, name, role and company, and a company logotype line in muted type. Under it, a control row with a left arrow, a set of dots, and a right arrow. Everything that changes gets an id so it can be rewritten. Then add JavaScript that holds an array of quotes and cycles them every six seconds: fade the panel out and back in around each swap using opacity classes, rebuild the dots so the current one is a wide pill, let arrows and dot clicks jump around with the timer restarting after a manual move, pause the rotation while the pointer is over the panel or a control has keyboard focus, and wrap around at both ends.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
Create a single-quote testimonial slider with Tailwind CSS on a light panel. Centre a small uppercase eyebrow, then a large serif-weight quote in text-2xl that has room for three lines, an attribution block with an avatar circle, name, role and company, and a company logotype line in muted type. Under it, a control row with a left arrow, a set of dots, and a right arrow. Everything that changes gets an id so it can be rewritten. Then add JavaScript that holds an array of quotes and cycles them every six seconds: fade the panel out and back in around each swap using opacity classes, rebuild the dots so the current one is a wide pill, let arrows and dot clicks jump around with the timer restarting after a manual move, pause the rotation while the pointer is over the panel or a control has keyboard focus, and wrap around at both ends.
var INTERVAL = 6000;
var QUOTES = [
{
quote: "We replaced four internal tools with this and still ended the quarter with fewer incidents.",
initials: "AR", name: "Alex Rivera", role: "CTO · Northwind"
},
{
quote: "The migration took an afternoon. The part I did not expect was how quiet our on-call got.",
initials: "PK", name: "Priya Kapoor", role: "Head of Platform · Meridian"
},
{
quote: "Our designers open pull requests now. That sentence would have been a joke last year.",
initials: "JO", name: "Jonas Ohlsson", role: "Director of Engineering · Calla"
},
{
quote: "Procurement asked for the security review. It came back clean in three days.",
initials: "MT", name: "Mei Tanaka", role: "VP Engineering · Lantern"
}
];
var slider = document.getElementById("slider");
var slide = document.getElementById("slide");
var quote = document.getElementById("quote");
var avatar = document.getElementById("avatar");
var name = document.getElementById("name");
var role = document.getElementById("role");
var dots = document.getElementById("quoteDots");
var ACTIVE = "h-2 w-6 rounded-full bg-neutral-900 transition-all";
var IDLE = "h-2 w-2 rounded-full bg-neutral-300 transition-all";
var index = 0;
var timer = null;
QUOTES.forEach(function (item, i) {
var dot = document.createElement("button");
dot.className = IDLE;
dot.setAttribute("aria-label", "Quote " + (i + 1));
dot.addEventListener("click", function () { go(i); });
dots.appendChild(dot);
});
function paint() {
var item = QUOTES[index];
quote.textContent = '"' + item.quote + '"';
avatar.textContent = item.initials;
name.textContent = item.name;
role.textContent = item.role;
[].slice.call(dots.children).forEach(function (dot, i) {
dot.className = i === index ? ACTIVE : IDLE;
});
}
function go(next) {
index = (next + QUOTES.length) % QUOTES.length;
// Fade out, swap the text while it is invisible, fade back in.
slide.classList.add("opacity-0");
setTimeout(function () {
paint();
slide.classList.remove("opacity-0");
}, 300);
restart();
}
function restart() {
clearInterval(timer);
timer = setInterval(function () { go(index + 1); }, INTERVAL);
}
document.getElementById("quotePrev").addEventListener("click", function () { go(index - 1); });
document.getElementById("quoteNext").addEventListener("click", function () { go(index + 1); });
// Nobody wants a quote to slide away mid-sentence.
slider.addEventListener("mouseenter", function () { clearInterval(timer); });
slider.addEventListener("mouseleave", restart);
slider.addEventListener("focusin", function () { clearInterval(timer); });
slider.addEventListener("focusout", restart);
paint();
restart();