Eine Bibliothek für Designer mit wenig Zeit

React + MotionText & Scroll

Headline, die sich umschreibt

Eine große Headline tippt ihr Ende Buchstabe für Buchstabe, markiert es wie im Texteditor und wechselt zur nächsten Phrase.

Einstellungen

65ms

Code

import { useEffect, useMemo, useState } from "react";
import { motion, useReducedMotion } from "framer-motion";

interface TypewriterRotatorProps {
  prefix?: string;
  /** Frases separadas por comas. */
  phrases?: string;
  /** Milisegundos por letra al escribir. */
  speed?: number;
  caret?: string;
}

export default function TypewriterRotator({
  prefix = "Eine Bibliothek für",
  phrases = "Designer mit wenig Zeit, kleine Teams, deinen nächsten Launch, Leute mit Liebe zum Detail",
  speed = 65,
  caret = "#7c5cff",
}: TypewriterRotatorProps) {
  const list = useMemo(
    () =>
      phrases
        .split(",")
        .map((s) => s.trim())
        .filter(Boolean),
    [phrases],
  );
  const [index, setIndex] = useState(0);
  const [length, setLength] = useState(() => list[0]?.length ?? 0);
  const [selecting, setSelecting] = useState(false);
  const [paused, setPaused] = useState(false);
  const reduce = useReducedMotion();
  const word = list.length ? (list[index % list.length] ?? "") : "";

  useEffect(() => {
    if (paused || !word) return;
    let delay: number;
    let next: () => void;
    if (reduce) {
      delay = 2600;
      next = () => setIndex((i) => i + 1);
    } else if (selecting) {
      delay = 480;
      next = () => {
        setSelecting(false);
        setLength(0);
        setIndex((i) => i + 1);
      };
    } else if (length < word.length) {
      delay = length === 0 ? 320 : speed * (0.6 + Math.random() * 0.8);
      next = () => setLength(length + 1);
    } else {
      delay = 1900;
      next = () => setSelecting(true);
    }
    const t = setTimeout(next, delay);
    return () => clearTimeout(t);
  }, [paused, word, reduce, selecting, length, speed]);

  const shown = reduce ? word : word.slice(0, length);
  const typing = !paused && !reduce && !selecting && length < word.length;

  return (
    <div className="w-full max-w-md">
      <p className="sr-only">
        {prefix} {word}
      </p>
      <button
        type="button"
        onClick={() => setPaused((p) => !p)}
        aria-label={paused ? "Animation fortsetzen" : "Animation pausieren"}
        aria-pressed={paused}
        className="block w-full cursor-pointer text-left text-[34px] font-semibold leading-[1.06] tracking-[-0.035em] sm:text-[42px]"
      >
        <span aria-hidden="true" className="block text-balance text-[#1d1d1f] dark:text-[#f5f5f7]">
          {prefix}
        </span>
        <span aria-hidden="true" className="block min-h-[2.12em]" style={{ color: caret }}>
          <span
            className="rounded-[0.1em] transition-colors duration-150"
            style={{
              background: selecting ? "color-mix(in srgb, " + caret + " 22%, transparent)" : "transparent",
            }}
          >
            {shown}
          </span>
          <motion.span
            className="ml-[0.05em] inline-block h-[0.86em] w-[0.075em] translate-y-[0.12em] rounded-full"
            style={{ background: caret }}
            animate={{ opacity: paused ? 0.3 : typing ? 1 : [1, 1, 0, 0] }}
            transition={
              typing || paused
                ? { duration: 0.15 }
                : { duration: 1, repeat: Infinity, times: [0, 0.5, 0.5, 1] }
            }
          />
        </span>
      </button>
    </div>
  );
}

Prompt für KI

Füg ihn in ChatGPT, Claude oder Cursor ein und der Baustein wird mit diesen Einstellungen an dein Projekt angepasst, auch ohne React.

Mehr aus Text & Scroll

Wofür du ihn nutzen kannst

Ein Schreibmaschinen-Effekt in React passt in den Hero einer Seite, die mehrere Zielgruppen gleichzeitig anspricht: ein Tool „gemacht für Freelancer, kleine Teams, deinen nächsten Launch“ oder ein Portfolio, das „Ich gestalte für…“ sagt. Der feste Anfang bleibt stehen, während das Ende getippt, markiert und ersetzt wird, wie beim echten Überarbeiten.

Ähnlich lange Phrasen verhindern, dass die Headline hin und her springt. Drei bis fünf reichen völlig. Ein Tipp auf den Text pausiert ihn, das hilft allen, die in Ruhe lesen wollen. Mit reduzierter Bewegung erscheint jede Phrase einfach komplett.