Ir al contenido
Evaluar mi proyecto
Componentes · SeccionesDisponible

Testimonios en marquesina

Dos filas de citas que corren en sentidos opuestos y se pausan al apuntar o enfocar. Textos de muestra.

Lo que dicen quienes ya trabajaron con nosotros

“Pasamos de hojas de cálculo sueltas a un solo sistema. Lo notamos en la primera semana.”
Persona de muestraDirección de operaciones
“Explicaron todo sin tecnicismos. Siempre supimos qué venía y cuánto costaba.”
Persona de muestraDueña de negocio
“Las entregas semanales nos dejaron corregir el rumbo a tiempo.”
Persona de muestraGerencia de ventas
“El soporte después del lanzamiento fue lo que nos hizo confiar.”
Persona de muestraAdministración
“Cada decisión quedó por escrito. Eso en auditoría vale oro.”
Persona de muestraCumplimiento
“Por fin una herramienta que se adapta a cómo trabajamos.”
Persona de muestraLogística

Testimonios de muestra para la demostración.

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

seccion-testimonios.tsx76 líneas
Descargar
"use client";

import type { CSSProperties } from "react";

/** Sección de testimonios con dos filas en marquesina que corren en sentidos opuestos y se pausan al apuntar o enfocar. Textos y nombres de muestra. React + TypeScript, sin dependencias. */
export type SeccionTestimoniosItem = { quote: string; name: string; role: string };

export type SeccionTestimoniosProps = {
  title?: string;
  items?: SeccionTestimoniosItem[];
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcstm-root { box-sizing: border-box; container-type: inline-size; width: 100%; padding: clamp(32px, 6cqi, 80px) 0; background: #f6f3ec; color: #1c1a17; font: 400 15px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; overflow: hidden; }
.gcstm-root *, .gcstm-root *::before, .gcstm-root *::after { box-sizing: border-box; }
.gcstm-root :focus-visible { outline: 2px solid #1c1a17; outline-offset: 3px; }
.gcstm-title { margin: 0 auto 36px; max-width: 18ch; padding: 0 20px; text-align: center; font: 500 clamp(30px, 5.4cqi, 60px)/1.04 "Newsreader", Georgia, ui-serif, serif; letter-spacing: -.03em; text-wrap: balance; }
.gcstm-rows { display: grid; gap: 16px; -webkit-mask: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.gcstm-row { display: flex; width: max-content; gap: 16px; animation: gcstm-run var(--t) linear infinite; animation-direction: var(--dir, normal); }
.gcstm-rows:hover .gcstm-row, .gcstm-rows:focus-within .gcstm-row { animation-play-state: paused; }
.gcstm-card { flex: none; width: 340px; padding: 22px; border: 1px solid #e3ddd0; border-radius: 16px; background: #fff; }
.gcstm-q { margin: 0 0 18px; font: 400 17px/1.45 "Newsreader", Georgia, ui-serif, serif; }
.gcstm-who { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.gcstm-av { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: #1c1a17; color: #f6f3ec; font-weight: 700; }
.gcstm-who span { display: block; color: #6e685b; }
.gcstm-note { margin: 22px 0 0; text-align: center; font-size: 12px; color: #8a8374; }
@keyframes gcstm-run { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .gcstm-row { animation: none; width: auto; flex-wrap: wrap; justify-content: center; padding: 0 16px; } .gcstm-row [data-dup="true"] { display: none; } .gcstm-rows { -webkit-mask: none; mask: none; } }`;

const DEFAULT: SeccionTestimoniosItem[] = [
  { quote: "Pasamos de hojas de cálculo sueltas a un solo sistema. Lo notamos en la primera semana.", name: "Persona de muestra", role: "Dirección de operaciones" },
  { quote: "Explicaron todo sin tecnicismos. Siempre supimos qué venía y cuánto costaba.", name: "Persona de muestra", role: "Dueña de negocio" },
  { quote: "Las entregas semanales nos dejaron corregir el rumbo a tiempo.", name: "Persona de muestra", role: "Gerencia de ventas" },
  { quote: "El soporte después del lanzamiento fue lo que nos hizo confiar.", name: "Persona de muestra", role: "Administración" },
  { quote: "Cada decisión quedó por escrito. Eso en auditoría vale oro.", name: "Persona de muestra", role: "Cumplimiento" },
  { quote: "Por fin una herramienta que se adapta a cómo trabajamos.", name: "Persona de muestra", role: "Logística" },
];

function Row({ items, dir, t }: { items: SeccionTestimoniosItem[]; dir: "normal" | "reverse"; t: string }) {
  return (
    <div className="gcstm-row" style={{ "--dir": dir, "--t": t } as CSSProperties}>
      {[0, 1].map((n) =>
        items.map((item, i) => (
          <figure key={`${n}-${i}`} className="gcstm-card" data-dup={n === 1} aria-hidden={n === 1 || undefined} tabIndex={n === 1 ? -1 : 0} style={{ margin: 0 }}>
            <blockquote className="gcstm-q" style={{ margin: "0 0 18px" }}>“{item.quote}”</blockquote>
            <figcaption className="gcstm-who">
              <span className="gcstm-av" aria-hidden="true">{item.name[0]}</span>
              <div>
                {item.name}
                <span>{item.role}</span>
              </div>
            </figcaption>
          </figure>
        )),
      )}
    </div>
  );
}

export function SeccionTestimonios({ title = "Lo que dicen quienes ya trabajaron con nosotros", items = DEFAULT, className = "", style }: SeccionTestimoniosProps) {
  const half = Math.ceil(items.length / 2);
  return (
    <section className={`gcstm-root ${className}`} style={style} aria-label="Testimonios">
      <style>{CSS}</style>
      <h2 className="gcstm-title">{title}</h2>
      <div className="gcstm-rows">
        <Row items={items.slice(0, half)} dir="normal" t="46s" />
        <Row items={items.slice(half)} dir="reverse" t="52s" />
      </div>
      <p className="gcstm-note">Testimonios de muestra para la demostración.</p>
    </section>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/seccion-testimonios

Ejemplo de uso de Testimonios en marquesina

Después de copiar seccion-testimonios.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { SeccionTestimonios } from "./seccion-testimonios";

export default function Ejemplo() {
  return <SeccionTestimonios title="Ejemplo" />;
}

Propiedades de SeccionTestimonios

SeccionTestimonios acepta 4 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
title?stringOpcional.
items?SeccionTestimoniosItem[]Opcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Testimonios en marquesina con variables CSS

Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.

VariableValor por defecto
--dirnormal

Accesibilidad y rendimiento

El archivo tiene 76 líneas (5 KB) y define 1 animación con @keyframes. Esto es lo que hace Testimonios en marquesina por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
  • Expone su estado a tecnologías de apoyo con atributos aria-*.
  • Es un componente de cliente de Next.js: incluye la directiva "use client".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)