Ir al contenido
Evaluar mi proyecto
Componentes · Texto y animacionesDisponible

Palabra que se enfoca

Frase con una palabra que cambia saliendo del desenfoque, como una lente que busca el punto; se pausa fuera de pantalla.

Software paraordenar

Código completo

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

texto-enfoque.tsx60 líneas
Descargar
"use client";

import { useEffect, useRef, useState, type CSSProperties } from "react";

/** Palabra que se enfoca desde el desenfoque y cambia de una frase a otra, como una lente que busca el punto. Se pausa fuera de pantalla. React + TypeScript, sin dependencias. */
export type TextoEnfoqueProps = {
  prefix?: string;
  words?: string[];
  interval?: number;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gctef-root { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: .3em; max-width: 100%; color: var(--gctef-fg, #e9ecf2); font: 600 clamp(28px, 6vw, 60px)/1.2 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; letter-spacing: -.03em; }
.gctef-root *, .gctef-root *::before, .gctef-root *::after { box-sizing: border-box; }
.gctef-slot { position: relative; display: inline-grid; }
.gctef-word { grid-area: 1 / 1; color: var(--gctef-hi, #6fb1ff); animation: gctef-in 1s cubic-bezier(.16, 1, .3, 1) both; white-space: nowrap; }
.gctef-sizer { grid-area: 1 / 1; visibility: hidden; height: 0; overflow: hidden; white-space: nowrap; }
@keyframes gctef-in { from { filter: blur(14px); opacity: 0; letter-spacing: .25em; transform: scale(1.06); } to { filter: blur(0); opacity: 1; letter-spacing: inherit; transform: none; } }
@media (prefers-reduced-motion: reduce) { .gctef-word { animation: none; } }`;

export function TextoEnfoque({ prefix = "Software para", words = ["ordenar", "automatizar", "conectar", "escalar"], interval = 2600, className = "", style }: TextoEnfoqueProps) {
  const ref = useRef<HTMLSpanElement>(null);
  const [i, setI] = useState(0);
  const [visible, setVisible] = useState(true);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting), { threshold: 0.1 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  useEffect(() => {
    if (!visible || words.length < 2 || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const id = window.setInterval(() => {
      if (!document.hidden) setI((value) => (value + 1) % words.length);
    }, interval);
    return () => window.clearInterval(id);
  }, [visible, words.length, interval]);

  return (
    <span ref={ref} className={`gctef-root ${className}`} style={style}>
      <style>{CSS}</style>
      <span>{prefix}</span>
      <span className="gctef-slot">
        {words.map((word) => (
          <span key={word} className="gctef-sizer" aria-hidden="true">
            {word}
          </span>
        ))}
        <span key={i} className="gctef-word" aria-label={words[i]}>
          {words[i]}
        </span>
      </span>
    </span>
  );
}

Ú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-enfoque

Ejemplo de uso de Palabra que se enfoca

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

ejemplo.tsx5 líneas
import { TextoEnfoque } from "./texto-enfoque";

export default function Ejemplo() {
  return <TextoEnfoque prefix="Ejemplo" />;
}

Propiedades de TextoEnfoque

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

PropiedadTipoDescripción
prefix?stringOpcional.
words?string[]Opcional.
interval?numberOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Palabra que se enfoca 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
--gctef-fg#e9ecf2
--gctef-hi#6fb1ff

Accesibilidad y rendimiento

El archivo tiene 60 líneas (3 KB) y define 1 animación con @keyframes. Esto es lo que hace Palabra que se enfoca 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-*.
  • Se pausa cuando sale de la pantalla para no gastar batería ni CPU.
  • 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)