Ir al contenido
Evaluar mi proyecto
Componentes · Texto y animacionesDisponible

Titular que sube de una máscara

Titular editorial que sube línea por línea desde una máscara al entrar en pantalla, con inclinación inicial y cursiva.

Demostración interactiva

Código completo

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

texto-cortina.tsx47 líneas
Descargar
"use client";

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

/** Titular que sube línea por línea desde una máscara al entrar en pantalla. Editorial, con una sola entrada y sin repetirse. React + TypeScript, sin dependencias. */
export type TextoCortinaProps = {
  lines?: string[];
  /** Milisegundos entre una línea y la siguiente. */
  stagger?: number;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gctct-root { display: block; color: var(--gctct-fg, #151515); font: 500 clamp(36px, 8.5vw, 92px)/1.02 "Newsreader", Georgia, ui-serif, serif; letter-spacing: -.035em; margin: 0; }
.gctct-root *, .gctct-root *::before, .gctct-root *::after { box-sizing: border-box; }
.gctct-line { display: block; overflow: hidden; padding: .06em 0 .12em; margin: -.06em 0 -.12em; }
.gctct-in { display: block; transform: translateY(108%) skewY(4deg); transform-origin: 0 100%; transition: transform 1.05s cubic-bezier(.16, 1, .3, 1); transition-delay: calc(var(--i) * var(--gap, 110ms)); }
.gctct-root[data-in="true"] .gctct-in { transform: none; }
.gctct-line:nth-child(2) .gctct-in { font-style: italic; }
@media (prefers-reduced-motion: reduce) { .gctct-in { transform: none; transition: none; } }
@media (scripting: none) { .gctct-in { transform: none; } }`;

export function TextoCortina({ lines = ["Software hecho", "a la medida de", "tu operación."], stagger = 110, className = "", style }: TextoCortinaProps) {
  const ref = useRef<HTMLHeadingElement>(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([entry]) => entry.isIntersecting && (setSeen(true), io.disconnect()), { threshold: 0.3 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <h2 ref={ref} className={`gctct-root ${className}`} style={{ "--gap": `${stagger}ms`, ...style } as CSSProperties} data-in={seen} aria-label={lines.join(" ")}>
      <style>{CSS}</style>
      {lines.map((line, index) => (
        <span key={line} className="gctct-line" aria-hidden="true">
          <span className="gctct-in" style={{ "--i": index } as CSSProperties}>
            {line}
          </span>
        </span>
      ))}
    </h2>
  );
}

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

Ejemplo de uso de Titular que sube de una máscara

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

ejemplo.tsx5 líneas
import { TextoCortina } from "./texto-cortina";

export default function Ejemplo() {
  return <TextoCortina />;
}

Propiedades de TextoCortina

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

PropiedadTipoDescripción
lines?string[]Opcional.
stagger?numberMilisegundos entre una línea y la siguiente.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Titular que sube de una máscara 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
--gctct-fg#151515
--gap110ms

Accesibilidad y rendimiento

El archivo tiene 47 líneas (2 KB) y no necesita @keyframes. Esto es lo que hace Titular que sube de una máscara 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)