Ir al contenido
Evaluar mi proyecto
Componentes · Texto y animacionesDisponible

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.

texto-rodar.tsx42 líneas
Descargar
"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.

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

Ejemplo 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.

ejemplo.tsx5 líneas
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.

PropiedadTipoDescripción
items?{ label: string; href?: string }[]Opcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

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.

VariableValor 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".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)