Befehl-kopieren-Button
Beim Kopieren wischt eine Markierung über den Befehl, und das Icon wird zu einem Häkchen, das sich selbst zeichnet.
Einstellungen
Code
import { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Copy } from "lucide-react";
interface CopyCommandProps {
command?: string;
accent?: string;
}
export default function CopyCommand({
command = "npm i @aimran/ui",
accent = "#22c55e",
}: CopyCommandProps) {
const [copied, setCopied] = useState(false);
const [sweep, setSweep] = useState(0);
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const reduce = useReducedMotion();
useEffect(() => () => clearTimeout(timer.current), []);
const copy = () => {
try {
navigator.clipboard?.writeText(command).catch(() => {});
} catch {
// Sin portapapeles disponible: la animación sigue funcionando
}
setCopied(true);
setSweep((s) => s + 1);
clearTimeout(timer.current);
timer.current = setTimeout(() => setCopied(false), 1800);
};
return (
<div className="flex w-full max-w-[19rem] items-center gap-3 rounded-2xl bg-white py-1.5 pl-4 pr-1.5 font-mono text-[13px] text-[#1d1d1f] shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_rgba(0,0,0,0.06)] dark:bg-[#1c1c1e] dark:text-[#f5f5f7] dark:shadow-[0_0_0_1px_rgba(255,255,255,0.08),0_8px_24px_rgba(0,0,0,0.4)]">
<span className="select-none text-[#86868b]" aria-hidden="true">
$
</span>
<span className="relative min-w-0 flex-1">
{sweep > 0 && !reduce && (
<motion.span
key={sweep}
aria-hidden="true"
className="absolute -inset-x-1 -inset-y-0.5 origin-left rounded-[5px]"
style={{ backgroundColor: accent }}
initial={{ scaleX: 0, opacity: 0.24 }}
animate={{ scaleX: 1, opacity: [0.24, 0.24, 0] }}
transition={{
scaleX: { duration: 0.34, ease: [0.22, 1, 0.36, 1] },
opacity: { duration: 1.1, times: [0, 0.55, 1] },
}}
/>
)}
<span className="relative block truncate">{command}</span>
</span>
<motion.button
type="button"
onClick={copy}
aria-label={copied ? "Kopiert" : "Befehl kopieren"}
whileTap={{ scale: 0.88 }}
transition={{ type: "spring", stiffness: 600, damping: 30 }}
className="relative grid h-9 w-9 shrink-0 place-items-center rounded-xl text-[#86868b] outline-none transition-colors hover:bg-black/5 hover:text-[#1d1d1f] focus-visible:ring-2 focus-visible:ring-black/20 dark:hover:bg-white/10 dark:hover:text-[#f5f5f7] dark:focus-visible:ring-white/30"
>
<motion.span
aria-hidden="true"
className="absolute inset-0 rounded-xl"
style={{ backgroundColor: accent }}
initial={false}
animate={{ opacity: copied ? 0.14 : 0 }}
transition={{ duration: 0.2 }}
/>
<AnimatePresence mode="popLayout" initial={false}>
{copied ? (
<motion.svg
key="check"
viewBox="0 0 24 24"
className="relative h-[18px] w-[18px]"
fill="none"
stroke={accent}
strokeWidth={2.25}
strokeLinecap="round"
strokeLinejoin="round"
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.5, filter: "blur(2px)" }}
transition={{ type: "spring", stiffness: 500, damping: 26 }}
>
<motion.path
d="M5 12.5l4.5 4.5L19 7.5"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 0.3, delay: 0.05, ease: [0.65, 0, 0.35, 1] }}
/>
</motion.svg>
) : (
<motion.span
key="copy"
className="relative grid place-items-center"
initial={{ opacity: 0, scale: 0.5, filter: "blur(2px)" }}
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
exit={{ opacity: 0, scale: 0.5, filter: "blur(2px)" }}
transition={{ type: "spring", stiffness: 500, damping: 30 }}
>
<Copy className="h-[18px] w-[18px]" strokeWidth={1.5} />
</motion.span>
)}
</AnimatePresence>
</motion.button>
<span className="sr-only" aria-live="polite">
{copied ? "In die Zwischenablage kopiert" : ""}
</span>
</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 Mikrointeraktionen
Wofür du ihn nutzen kannst
Ein Button zum Kopieren in die Zwischenablage gehört zu den Details, die erst auffallen, wenn sie fehlen. Er passt neben den Installationsbefehl in deiner Doku, einen API-Schlüssel im Dashboard, einen Rabattcode oder einen Einladungslink. Die Markierung zeigt genau, was kopiert wurde, und Screenreader sagen „Kopiert“ an.
Browser erlauben das Kopieren nur auf HTTPS-Seiten; unter reinem HTTP läuft die Animation, kopiert wird aber nichts. Lange Befehle werden mit Auslassungspunkten gekürzt; ist deiner lang, gib der Box mehr Breite. Das grüne Häkchen versteht fast jeder als Erfolg.