Texto rotativo
Una palabra de la frase cambia: sube, se desenfoca y la siguiente entra.
Hecho para
equipos ágiles, estudios creativos, fundadores, tiendas en línea
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
/**
* Palabra que cambia dentro de una frase: la actual sube y se desenfoca mientras entra la siguiente, y el ancho se ajusta con suavidad.
* Se pausa al pasar el puntero o enfocar. Con movimiento reducido solo cambia, sin deslizar.
*
* <h1>Hecho para <WordRotator words={["equipos", "estudios", "fundadores"]} /></h1>
*
* Los lectores de pantalla leen todas las palabras una sola vez, en lugar de oír cada cambio.
*/
import { useEffect, useRef, useState, type CSSProperties } from "react";
export type WordRotatorProps = {
words: string[];
/** Milisegundos entre palabras. */
interval?: number;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcrw-root { position: relative; display: inline-block; height: 1.3em; margin-bottom: -.12em; padding-bottom: .12em; overflow: hidden; vertical-align: bottom; line-height: 1.3; white-space: nowrap; transition: width .6s cubic-bezier(.16, 1, .3, 1); }
.gcrw-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.gcrw-word { position: absolute; top: 0; left: 0; display: inline-block; }
.gcrw-word[data-state="in"] { position: relative; transform: none; opacity: 1; filter: none; transition: transform .7s cubic-bezier(.16, 1, .3, 1), opacity .5s, filter .6s; }
.gcrw-word[data-state="out"] { transform: translateY(-105%); opacity: 0; filter: blur(7px); transition: transform .7s cubic-bezier(.16, 1, .3, 1), opacity .4s, filter .5s; }
.gcrw-word[data-state="idle"] { transform: translateY(105%); opacity: 0; filter: blur(7px); transition: none; }
@media (prefers-reduced-motion: reduce) { .gcrw-root { transition: none; } .gcrw-word[data-state] { transform: none !important; filter: none !important; transition: opacity .2s !important; } .gcrw-word[data-state="out"] { opacity: 0; } }
`;
export function WordRotator({ words, interval = 2400, className, style }: WordRotatorProps) {
const [state, setState] = useState({ index: 0, prev: -1 });
const [widths, setWidths] = useState<number[]>([]);
const [paused, setPaused] = useState(false);
const refs = useRef<(HTMLSpanElement | null)[]>([]);
// Measure every word, again whenever fonts load or the text size changes.
useEffect(() => {
const measure = () => setWidths(refs.current.map((element) => element?.scrollWidth ?? 0));
const observer = new ResizeObserver(measure);
refs.current.forEach((element) => element && observer.observe(element));
return () => observer.disconnect();
}, [words]);
useEffect(() => {
if (paused || words.length < 2) return;
const timer = window.setInterval(() => setState((current) => ({ index: (current.index + 1) % words.length, prev: current.index })), interval);
return () => window.clearInterval(timer);
}, [paused, words.length, interval]);
const width = widths[state.index];
return (
<>
<style>{CSS}</style>
<span
className={["gcrw-root", className].filter(Boolean).join(" ")}
style={{ ...style, width: width ? `${width}px` : undefined }}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
onFocus={() => setPaused(true)}
onBlur={() => setPaused(false)}
>
<span className="gcrw-sr">{words.join(", ")}</span>
{words.map((word, index) => (
<span
key={word}
ref={(element) => { refs.current[index] = element; }}
className="gcrw-word"
aria-hidden="true"
data-state={index === state.index ? "in" : index === state.prev ? "out" : "idle"}
>
{word}
</span>
))}
</span>
</>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/texto-rotativoEjemplo de uso de Texto rotativo
Después de copiar word-rotator.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { WordRotator } from "./word-rotator";
export default function Ejemplo() {
return <WordRotator />;
}Propiedades de WordRotator
WordRotator acepta 4 propiedades; 1 es obligatoria.
| Propiedad | Tipo | Descripción |
|---|---|---|
words | string[] | Obligatoria. |
interval? | number | Milisegundos entre palabras. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 81 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Texto rotativo por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Expone su estado a tecnologías de apoyo con atributos aria-*.
- Es un componente de cliente de Next.js: incluye la directiva "use client".
Acerca del producto
Una palabra que cambia dentro de una frase. La actual sube y se desenfoca mientras entra la siguiente, y el ancho se ajusta con suavidad para que el texto de alrededor no salte. Se pausa al pasar el puntero o enfocar.
Incluye
Un solo archivo React + TypeScript, con los estilos dentro. No usa Tailwind ni bibliotecas externas.
- Cualquier número de palabras
- Intervalo configurable
- Ancho animado según la palabra actual
- Los lectores de pantalla leen la lista una sola vez
Cómo usarlo
Úsalo dentro de un título: <h1>Hecho para <WordRotator words={[…]} /></h1>. Hereda tipografía y tamaño del texto que lo rodea.