Máquina de escribir
Escribe una frase, la borra y sigue con la siguiente, con cursor parpadeante.
Nosotros diseñamos sistemas.. automatizamos procesos.. integramos tus datos.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
/**
* Máquina de escribir: escribe una frase, la borra y pasa a la siguiente, con un cursor que parpadea.
* Con movimiento reducido muestra la primera frase completa y quieta.
*
* <Typewriter phrases={["Diseñamos sistemas.", "Automatizamos procesos.", "Integramos tus datos."]} />
*
* Los lectores de pantalla reciben todas las frases una vez, sin oír cada letra.
*/
import { useEffect, useState, type CSSProperties } from "react";
export type TypewriterProps = {
phrases: string[];
/** Milisegundos por letra al escribir. */
typeSpeed?: number;
/** Milisegundos por letra al borrar. */
deleteSpeed?: number;
/** Pausa con la frase completa. */
pause?: number;
loop?: boolean;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gctw-root { display: inline; }
.gctw-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.gctw-caret { display: inline-block; width: .09em; height: 1em; margin-left: .06em; vertical-align: -.1em; background: currentColor; animation: gctw-blink 1.05s steps(1) infinite; }
.gctw-caret[data-typing="true"] { animation: none; }
@keyframes gctw-blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .gctw-caret { animation: none; } }
`;
export function Typewriter({ phrases, typeSpeed = 55, deleteSpeed = 28, pause = 1500, loop = true, className, style }: TypewriterProps) {
const [reduced, setReduced] = useState(false);
const [view, setView] = useState({ phrase: 0, length: 0, deleting: false });
useEffect(() => {
const frame = requestAnimationFrame(() => setReduced(window.matchMedia("(prefers-reduced-motion: reduce)").matches));
return () => cancelAnimationFrame(frame);
}, []);
useEffect(() => {
if (reduced || !phrases.length) return;
const text = phrases[view.phrase];
let delay = view.deleting ? deleteSpeed : typeSpeed;
let next = view;
if (!view.deleting && view.length === text.length) {
if (!loop && view.phrase === phrases.length - 1) return;
delay = pause;
next = { ...view, deleting: true };
} else if (view.deleting && view.length === 0) {
delay = 380;
next = { phrase: (view.phrase + 1) % phrases.length, length: 0, deleting: false };
} else {
next = { ...view, length: view.length + (view.deleting ? -1 : 1) };
}
const timer = window.setTimeout(() => setView(next), delay);
return () => window.clearTimeout(timer);
}, [view, reduced, phrases, typeSpeed, deleteSpeed, pause, loop]);
const shown = reduced ? phrases[0] ?? "" : (phrases[view.phrase] ?? "").slice(0, view.length);
const typing = !reduced && (!view.deleting ? view.length < (phrases[view.phrase] ?? "").length : view.length > 0);
return (
<>
<style>{CSS}</style>
<span className={["gctw-root", className].filter(Boolean).join(" ")} style={style}>
<span className="gctw-sr">{phrases.join(". ")}</span>
<span aria-hidden="true">{shown}</span>
<span className="gctw-caret" data-typing={typing} aria-hidden="true" />
</span>
</>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/texto-maquinaEjemplo de uso de Máquina de escribir
Después de copiar typewriter.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { Typewriter } from "./typewriter";
export default function Ejemplo() {
return <Typewriter loop />;
}Propiedades de Typewriter
Typewriter acepta 7 propiedades; 1 es obligatoria.
| Propiedad | Tipo | Descripción |
|---|---|---|
phrases | string[] | Obligatoria. |
typeSpeed? | number | Milisegundos por letra al escribir. |
deleteSpeed? | number | Milisegundos por letra al borrar. |
pause? | number | Pausa con la frase completa. |
loop? | boolean | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 80 líneas (3 KB) y define 1 animación con @keyframes. Esto es lo que hace Máquina de escribir 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-*.
- Anima con requestAnimationFrame, sincronizado con el refresco de pantalla.
- Es un componente de cliente de Next.js: incluye la directiva "use client".
Acerca del producto
Escribe frases con un cursor que parpadea, las borra y pasa a la siguiente en bucle. Las velocidades y la pausa son configurables. Con movimiento reducido muestra la primera frase completa y quieta.
Incluye
Un solo archivo React + TypeScript, con los estilos dentro. No usa Tailwind ni bibliotecas externas.
- Varias frases en bucle (o una sola vez)
- Velocidad de escritura, borrado y pausa configurables
- Cursor que no parpadea mientras escribe
- Los lectores de pantalla reciben todas las frases una vez
Cómo usarlo
Pasa phrases. El cursor usa el color del texto (currentColor).