Back to Dictionary
Status & Confirmation
●●○Customize
Context dependentError Cross Draw
The inverse of a checkmark draw; a red X SVG that sharply draws its two crossing lines to confirm a failure or rejection.
Use
Failed payments
Avoid
Form validation (too dramatic for a simple typo)
Safer Alternative
No safer default listed.
Risk Level
Low risk
Live Demo
What It Is
The inverse of a checkmark draw; a red X SVG that sharply draws its two crossing lines to confirm a failure or rejection.
✓When to Use
- Failed payments
- Access denied screens
- Processing failures
✕When NOT to Use
- Form validation (too dramatic for a simple typo)
Configuration Tips
- 01Draw the first diagonal, then the second diagonal staggered slightly
- 02Use a sharper, faster ease than the checkmark (e.g., easeIn)
You've Seen It In
Apple iOSStripe
AI Implementation Prompts
Move from a fast scaffold to production details, then tune timing and edge states.
Animate a red X drawing itself like a pen when a payment fails.
Reference Implementation
The same implementation approach used by the live preview, kept compact enough to inspect and adapt.
"use client";
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
export function ErrorCrossDrawDemo({ isPlaying = false }: { isPlaying?: boolean }) {
const [isError, setIsError] = useState(false);
return (
<div className="flex h-[400px] w-full items-center justify-center p-8 bg-stone-50 rounded-xl relative">
<div className="flex flex-col items-center">
<button
onClick={() => setIsError(true)}
className="mb-12 px-6 py-2.5 bg-white border border-stone-200 shadow-sm rounded-xl text-stone-700 font-medium hover:bg-stone-50 hover:text-stone-900 transition-colors focus:outline-none focus:ring-4 focus:ring-stone-200"
>
Trigger Error
</button>
<div className="w-32 h-32 bg-white rounded-full shadow-lg flex items-center justify-center relative overflow-hidden border-[4px] border-white">
<AnimatePresence>
{isError && (
<motion.div
className="absolute inset-0 bg-red-500"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3, ease: "easeOut" }}
/>
)}
</AnimatePresence>
<AnimatePresence onExitComplete={() => setIsError(false)}>
{isError && (
<svg className="w-16 h-16 z-10 text-white" viewBox="0 0 50 50">
<motion.path
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3, delay: 0.1, ease: "easeOut" }}
stroke="currentColor"
strokeWidth="5"
strokeLinecap="round"
fill="transparent"
d="M16 16 L34 34"
/>
<motion.path
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3, delay: 0.2, ease: "easeOut" }}
stroke="currentColor"
strokeWidth="5"
strokeLinecap="round"
fill="transparent"
d="M34 16 L16 34"
/>
</svg>
)}
</AnimatePresence>
{!isError && (
<div className="text-stone-300 w-16 h-16 rounded-full border-[3px] border-dashed border-stone-200" />
)}
</div>
<AnimatePresence>
{isError && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ delay: 0.4 }}
className="mt-6 flex flex-col items-center gap-4"
>
<h3 className="text-xl font-bold text-stone-900">Payment Failed</h3>
<button
onClick={() => setIsError(false)}
className="text-stone-500 hover:text-stone-900 text-sm font-medium transition-colors px-4 py-2 rounded-lg hover:bg-stone-200/50"
>
Reset
</button>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
}