Menú de texto que rueda
Enlaces grandes cuyas letras ruedan una a una hacia una copia en cursiva y de otro color al apuntarlos o enfocarlos.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import type { CSSProperties } from "react";
/** Texto que rueda letra por letra hacia una copia al apuntarlo o enfocarlo; sirve para enlaces de navegación grandes. React + TypeScript, sin dependencias. */
export type TextoRodarProps = {
items?: { label: string; href?: string }[];
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcthr-root { box-sizing: border-box; display: grid; gap: 4px; width: fit-content; max-width: 100%; color: var(--gcthr-fg, #f4f1ea); font: 700 clamp(34px, 8vw, 80px)/1 "Newsreader", Georgia, ui-serif, serif; letter-spacing: -.03em; }
.gcthr-root *, .gcthr-root *::before, .gcthr-root *::after { box-sizing: border-box; }
.gcthr-link { display: flex; overflow: hidden; width: fit-content; max-width: 100%; padding: 2px 0; color: inherit; text-decoration: none; cursor: pointer; }
.gcthr-link:focus-visible { outline: 2px solid var(--gcthr-hi, #ff6b3d); outline-offset: 6px; }
.gcthr-c { position: relative; display: inline-block; height: 1.05em; overflow: hidden; white-space: pre; }
.gcthr-c > span { display: block; transition: transform .6s cubic-bezier(.76, 0, .24, 1); transition-delay: calc(var(--i) * 22ms); }
.gcthr-c > span + span { position: absolute; left: 0; top: 100%; color: var(--gcthr-hi, #ff6b3d); font-style: italic; }
.gcthr-link:hover .gcthr-c > span, .gcthr-link:focus-visible .gcthr-c > span { transform: translateY(-100%); }
@media (prefers-reduced-motion: reduce) { .gcthr-c > span { transition: none; } .gcthr-link:hover .gcthr-c > span + span { color: var(--gcthr-hi, #ff6b3d); } .gcthr-link:hover .gcthr-c > span { transform: none; } .gcthr-link:hover .gcthr-c > span:first-child { color: var(--gcthr-hi, #ff6b3d); } .gcthr-c > span + span { display: none; } }`;
const DEFAULT_ITEMS = [{ label: "Proyectos" }, { label: "Servicios" }, { label: "Método" }, { label: "Contacto" }];
export function TextoRodar({ items = DEFAULT_ITEMS, className = "", style }: TextoRodarProps) {
return (
<nav className={`gcthr-root ${className}`} style={style} aria-label="Principal">
<style>{CSS}</style>
{items.map((item) => (
<a key={item.label} className="gcthr-link" href={item.href ?? "#"} onClick={(event) => !item.href && event.preventDefault()} aria-label={item.label}>
{Array.from(item.label).map((char, index) => (
<span key={index} className="gcthr-c" aria-hidden="true" style={{ "--i": index } as CSSProperties}>
<span>{char}</span>
<span>{char}</span>
</span>
))}
</a>
))}
</nav>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/texto-rodarEjemplo de uso de Menú de texto que rueda
Después de copiar texto-rodar.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { TextoRodar } from "./texto-rodar";
export default function Ejemplo() {
return <TextoRodar />;
}Propiedades de TextoRodar
TextoRodar acepta 3 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
items? | { label: string; href?: string }[] | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Menú de texto que rueda 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.
| Variable | Valor por defecto |
|---|---|
--gcthr-fg | #f4f1ea |
--gcthr-hi | #ff6b3d |
Accesibilidad y rendimiento
El archivo tiene 42 líneas (3 KB) y no necesita @keyframes. Esto es lo que hace Menú de texto que rueda 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".