Page Sections

Numbered Quote Slider

Large 01/02/03 numerals on the left roll digit by digit while the quote on the right slides in from the direction of travel, with drag, arrow-key navigation and a progress fraction.

A one-quote-per-slide testimonial carousel. The left column shows a huge zero-padded numeral; on every change each digit rolls along its own vertical strip to the new value — the tens digit stays put when it doesn't change, and the units digit rolls up or down with the direction of travel. Below it sit a "02 / 05" fraction and a thin progress track whose filled segment slides to the new position. The right column holds a small portrait placeholder tile, the quote, and the person's name and title; the content slides in from the left or right, and you can also drag it sideways to change slides. The index works in controlled or uncontrolled mode, with optional looping and autoplay.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add numbered-quote-slider.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

300
500
Active edited usageDefault props
<NumberedQuoteSlider/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/numbered-quote-slider.en.json

Or, once registries are configured in components.json, install it as @webberui/numbered-quote-slider.

Usage

import { NumberedQuoteSlider } from "@/components/ui/numbered-quote-slider";

<NumberedQuoteSlider
  eyebrow="客戶怎麼說"
  title="用過的團隊,留下的一句話"
  testimonials={[
    {
      quote: "尖峰時段的出餐時間縮短了將近一半。",
      name: "林語晴",
      role: "營運主管",
      company: "青嶼咖啡",
      highlight: "縮短了將近一半",
    },
    {
      quote: "調度畫面一眼看得出哪台車卡住。",
      name: "陳柏宇",
      role: "調度組長",
      company: "北辰物流",
      rating: 5,
    },
  ]}
  onIndexChange={(i) => console.log(`切到第 ${i + 1} 則`)}
/>

The sample strings above are the Chinese originals: "What customers say", "One line from the teams who use it", and two fictional quotes ("Peak-hour wait times dropped by almost half." / "The dispatch board shows at a glance which truck is stuck.").

Props

PropTypeDefaultDescription
testimonialsNumberedQuoteSliderItem[]—Testimonials to show (required)
eyebrowstring—Small label above the title
titlestring—Section title; used as the section's aria-labelledby when provided
subtitlestring—Supporting text below the title
indexnumber—Controlled current index (zero-based); managed internally when omitted
defaultIndexnumber0Initial index in uncontrolled mode
onIndexChange(index: number) => void—Called when the index changes
loopbooleantrueWrap from the last testimonial to the first (and back); when off, the arrows disable at either end
autoplaybooleanfalseAuto-advance; shows a pause button when on
intervalnumber6000Autoplay interval (ms)
padnumber2Minimum digits for the zero-padded numeral
stiffnessnumber300Spring stiffness of the numeral wheels; written to --wb-numbered-quote-slider-stiffness
durationnumber500Slide-in duration of the quote column (ms); written to --wb-numbered-quote-slider-duration
outlinedbooleanfalseAdd an accent-colored stroke to the large numeral
classNamestring—Passed through to the root section

NumberedQuoteSliderItem

FieldTypeDefaultDescription
quotestring—Quote text
namestring—Person's name
rolestring—Job title
companystring—Company
ratingnumber—Rating (0–5), shown as stars
avatarHuenumber—Hue of the placeholder portrait; derived from the index via the golden angle (137.508°) when omitted
highlightstring—Fragment of the quote to mark with the accent color
metric{ value: string; label: string }—Key figure shown with the quote
photoSrcstring—Portrait photo URL; falls back to a gradient + abstract silhouette placeholder

How it works

  • Digit-by-digit wheels: each digit is a vertical strip of 0–9 repeated three times, resting on the middle copy. Next always rolls up and previous always rolls down, so the units digit of 09 → 10 rolls forward one cell to 0 instead of back nine; afterwards it quietly snaps back to the middle copy.
  • Timing variables: --wb-numbered-quote-slider-stiffness (default 300, damping fixed at 30) and --wb-numbered-quote-slider-duration (default 500ms). Override them in CSS on an ancestor, or set them through the matching props.
  • The accent color comes only from --wb-numbered-quote-slider-accent (defaults #0d9488 light, #2dd4bf dark) and is used for the progress track, stars, highlight and focus ring.
  • Dragging the right column: release past 80px or faster than 400px/s to change slides, otherwise it springs back; the arrow buttons sit outside the drag area so they never trigger a drag.
  • Autoplay pauses on hover, while focus is inside, when the tab is hidden, or via the pause button; with loop off it stops at the last testimonial.

Accessibility

  • The root is a <section>, labelled by the title via aria-labelledby when title is set; the carousel is role="region" with aria-roledescription="carousel" and is focusable
  • With focus on the carousel, ←/→ move to the previous/next testimonial; the arrow buttons are type="button" with "Previous testimonial" / "Next testimonial" aria-labels and a focus-visible ring
  • The large numeral, fraction and progress track are hidden from assistive tech; an sr-only "Testimonial 2 of 5" is announced via aria-live="polite" instead, and stays silent while autoplay is running to avoid repeated interruptions
  • Each quote is rendered as <figure>, <blockquote> and <figcaption>; star ratings are role="img" with a "Rated 5 out of 5" label
  • Autoplay is off by default; when on, a visible pause button is always present (WCAG 2.2.2)
  • When the user prefers reduce motion: autoplay is always off, the numeral fades in over 150ms instead of rolling, the quote fades in over 150ms without moving, and the progress track jumps straight to the new position; content can still be changed with the buttons, arrow keys and drag