plugins/react-animation-studio/skills/creative-effects/SKILL.md
Expert knowledge for artistic and creative animation effects - morphing shapes, liquid animations, glitch effects, distortion, and experimental visual techniques that push creative boundaries.
npx skillsauth add markus41/claude creative-effectsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert knowledge for artistic and creative animation effects - morphing shapes, liquid animations, glitch effects, distortion, and experimental visual techniques that push creative boundaries.
Activate this skill when:
**/*.tsx with creative components**/components/Creative*.tsx**/components/Effect*.tsx**/components/Liquid*.tsximport { motion, useAnimation } from 'framer-motion';
export function LiquidButton({ children }: { children: React.ReactNode }) {
const controls = useAnimation();
const liquidVariants = {
rest: {
d: "M0,20 Q25,0 50,20 T100,20 L100,80 Q75,100 50,80 T0,80 Z",
},
hover: {
d: "M0,30 Q25,10 50,30 T100,30 L100,70 Q75,90 50,70 T0,70 Z",
},
tap: {
d: "M0,25 Q25,15 50,25 T100,25 L100,75 Q75,85 50,75 T0,75 Z",
},
};
return (
<motion.button
className="relative px-8 py-4 text-white font-medium"
initial="rest"
whileHover="hover"
whileTap="tap"
>
<svg
className="absolute inset-0 w-full h-full"
viewBox="0 0 100 100"
preserveAspectRatio="none"
>
<motion.path
fill="url(#liquid-gradient)"
variants={liquidVariants}
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
/>
<defs>
<linearGradient id="liquid-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#667eea" />
<stop offset="100%" stopColor="#764ba2" />
</linearGradient>
</defs>
</svg>
<span className="relative z-10">{children}</span>
</motion.button>
);
}
export function MorphingBlob() {
const paths = [
"M45.3,-51.2C58.3,-40.8,68.4,-25.6,71.8,-8.4C75.2,8.8,71.9,28,61.6,41.8C51.3,55.6,34,64,15.6,68.9C-2.8,73.8,-22.3,75.2,-38.1,67.6C-53.9,60,-66,43.4,-71.4,24.9C-76.8,6.4,-75.5,-14,-67.4,-30.7C-59.3,-47.4,-44.4,-60.4,-28.6,-69.7C-12.8,-79,-0.1,-84.6,8.9,-78.5C17.9,-72.4,32.3,-54.6,45.3,-51.2Z",
"M39.9,-47.1C53.3,-36.9,66.7,-26.3,72.2,-11.9C77.7,2.5,75.4,20.7,66.4,35.1C57.4,49.5,41.7,60.1,24.6,66.2C7.5,72.3,-11,73.9,-27.8,68.4C-44.6,62.9,-59.7,50.3,-67.8,34.3C-75.9,18.3,-77,-1.1,-72.1,-18.7C-67.2,-36.3,-56.3,-52.1,-42.4,-62.1C-28.5,-72.1,-11.6,-76.3,1.8,-78.5C15.2,-80.7,26.5,-57.3,39.9,-47.1Z",
"M42.7,-49.7C55.5,-38.8,66.1,-25.3,69.7,-9.8C73.3,5.7,69.9,23.2,60.6,37.4C51.3,51.6,36.1,62.5,19.3,68.3C2.5,74.1,-15.9,74.8,-32.8,68.7C-49.7,62.6,-65.1,49.7,-72.6,33.4C-80.1,17.1,-79.7,-2.6,-73.3,-20C-66.9,-37.4,-54.5,-52.5,-40.1,-63C-25.7,-73.5,-9.3,-79.4,3.6,-83.7C16.5,-88,29.9,-60.6,42.7,-49.7Z",
];
return (
<motion.svg viewBox="-100 -100 200 200" className="w-64 h-64">
<motion.path
fill="url(#blob-gradient)"
animate={{
d: paths,
}}
transition={{
duration: 10,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
<defs>
<linearGradient id="blob-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#667eea" />
<stop offset="50%" stopColor="#764ba2" />
<stop offset="100%" stopColor="#f093fb" />
</linearGradient>
</defs>
</motion.svg>
);
}
export function GlitchText({ children }: { children: string }) {
return (
<div className="relative inline-block">
{/* Main text */}
<span className="relative z-10">{children}</span>
{/* Glitch layer 1 - Red */}
<motion.span
className="absolute inset-0 text-red-500 opacity-80"
style={{ clipPath: 'inset(0 0 0 0)' }}
animate={{
x: [-2, 2, -2],
clipPath: [
'inset(40% 0 61% 0)',
'inset(92% 0 1% 0)',
'inset(43% 0 1% 0)',
'inset(25% 0 58% 0)',
],
}}
transition={{
duration: 0.3,
repeat: Infinity,
repeatType: 'reverse',
}}
>
{children}
</motion.span>
{/* Glitch layer 2 - Cyan */}
<motion.span
className="absolute inset-0 text-cyan-500 opacity-80"
style={{ clipPath: 'inset(0 0 0 0)' }}
animate={{
x: [2, -2, 2],
clipPath: [
'inset(85% 0 7% 0)',
'inset(15% 0 80% 0)',
'inset(45% 0 50% 0)',
'inset(70% 0 25% 0)',
],
}}
transition={{
duration: 0.2,
repeat: Infinity,
repeatType: 'reverse',
}}
>
{children}
</motion.span>
</div>
);
}
export function GlitchImage({ src, alt }: { src: string; alt: string }) {
return (
<div className="relative overflow-hidden">
<img src={src} alt={alt} className="relative z-10" />
<motion.img
src={src}
alt=""
className="absolute inset-0 opacity-50"
style={{ mixBlendMode: 'multiply' }}
animate={{
x: [-5, 5, -5],
filter: ['hue-rotate(0deg)', 'hue-rotate(90deg)', 'hue-rotate(0deg)'],
}}
transition={{
duration: 0.2,
repeat: Infinity,
}}
/>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-red-500/20 to-cyan-500/20"
animate={{
opacity: [0, 0.3, 0],
scaleY: [1, 1.02, 1],
}}
transition={{
duration: 0.1,
repeat: Infinity,
repeatDelay: 2,
}}
/>
</div>
);
}
export function LiquidFill({ progress = 0.7 }: { progress?: number }) {
const fillHeight = progress * 100;
return (
<div className="relative w-32 h-48 bg-slate-800 rounded-2xl overflow-hidden">
{/* Liquid */}
<motion.div
className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-blue-600 to-blue-400"
initial={{ height: 0 }}
animate={{ height: `${fillHeight}%` }}
transition={{ duration: 2, ease: 'easeOut' }}
>
{/* Wave effect */}
<svg
className="absolute top-0 left-0 w-full"
viewBox="0 0 100 20"
preserveAspectRatio="none"
style={{ transform: 'translateY(-50%)' }}
>
<motion.path
fill="currentColor"
className="text-blue-400"
d="M0,10 Q25,0 50,10 T100,10 L100,20 L0,20 Z"
animate={{
d: [
"M0,10 Q25,0 50,10 T100,10 L100,20 L0,20 Z",
"M0,10 Q25,20 50,10 T100,10 L100,20 L0,20 Z",
"M0,10 Q25,0 50,10 T100,10 L100,20 L0,20 Z",
],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
</svg>
</motion.div>
{/* Bubbles */}
{Array.from({ length: 5 }).map((_, i) => (
<motion.div
key={i}
className="absolute w-2 h-2 bg-white/30 rounded-full"
style={{
left: `${20 + i * 15}%`,
bottom: 0,
}}
animate={{
y: [0, -fillHeight * 2],
opacity: [0, 1, 0],
scale: [0.5, 1, 0.5],
}}
transition={{
duration: 2 + Math.random(),
delay: i * 0.3,
repeat: Infinity,
}}
/>
))}
</div>
);
}
export function MagneticButton({ children }: { children: React.ReactNode }) {
const ref = useRef<HTMLButtonElement>(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const distanceX = e.clientX - centerX;
const distanceY = e.clientY - centerY;
setPosition({
x: distanceX * 0.3,
y: distanceY * 0.3,
});
};
const handleMouseLeave = () => {
setPosition({ x: 0, y: 0 });
};
return (
<motion.button
ref={ref}
className="px-8 py-4 bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl text-white font-medium"
animate={{ x: position.x, y: position.y }}
transition={{ type: 'spring', stiffness: 150, damping: 15 }}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
>
{children}
</motion.button>
);
}
export function ElasticContainer({ children }: { children: React.ReactNode }) {
const [isPressed, setIsPressed] = useState(false);
return (
<motion.div
className="cursor-pointer"
animate={{
scaleX: isPressed ? 1.1 : 1,
scaleY: isPressed ? 0.9 : 1,
}}
transition={{
type: 'spring',
stiffness: 400,
damping: 10,
}}
onMouseDown={() => setIsPressed(true)}
onMouseUp={() => setIsPressed(false)}
onMouseLeave={() => setIsPressed(false)}
>
{children}
</motion.div>
);
}
export function DNALoader() {
const dots = Array.from({ length: 10 });
return (
<div className="flex items-center justify-center h-20">
{dots.map((_, i) => (
<div key={i} className="relative w-4 mx-0.5">
<motion.div
className="absolute w-3 h-3 bg-blue-500 rounded-full"
animate={{
y: [-15, 15, -15],
scale: [1, 0.8, 1],
opacity: [1, 0.5, 1],
}}
transition={{
duration: 1,
delay: i * 0.1,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
<motion.div
className="absolute w-3 h-3 bg-purple-500 rounded-full"
animate={{
y: [15, -15, 15],
scale: [0.8, 1, 0.8],
opacity: [0.5, 1, 0.5],
}}
transition={{
duration: 1,
delay: i * 0.1,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
</div>
))}
</div>
);
}
export function OrbitingLoader() {
const orbits = [
{ radius: 20, duration: 1.5, color: '#667eea' },
{ radius: 35, duration: 2, color: '#764ba2' },
{ radius: 50, duration: 2.5, color: '#f093fb' },
];
return (
<div className="relative w-32 h-32">
{/* Center dot */}
<motion.div
className="absolute top-1/2 left-1/2 w-4 h-4 -ml-2 -mt-2 bg-white rounded-full"
animate={{ scale: [1, 1.2, 1] }}
transition={{ duration: 1, repeat: Infinity }}
/>
{/* Orbiting dots */}
{orbits.map((orbit, i) => (
<motion.div
key={i}
className="absolute top-1/2 left-1/2 w-3 h-3 -ml-1.5 -mt-1.5 rounded-full"
style={{ backgroundColor: orbit.color }}
animate={{
x: [orbit.radius, 0, -orbit.radius, 0, orbit.radius],
y: [0, orbit.radius, 0, -orbit.radius, 0],
}}
transition={{
duration: orbit.duration,
repeat: Infinity,
ease: 'linear',
}}
/>
))}
</div>
);
}
export function MaskReveal({
children,
delay = 0,
}: {
children: React.ReactNode;
delay?: number;
}) {
return (
<div className="relative overflow-hidden">
<motion.div
initial={{ y: '100%' }}
animate={{ y: '0%' }}
transition={{
duration: 0.8,
delay,
ease: [0.25, 0.1, 0.25, 1],
}}
>
{children}
</motion.div>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-purple-500 to-pink-500"
initial={{ x: '0%' }}
animate={{ x: '100%' }}
transition={{
duration: 0.6,
delay: delay + 0.2,
ease: [0.25, 0.1, 0.25, 1],
}}
/>
</div>
);
}
export function SplitReveal({ children }: { children: React.ReactNode }) {
return (
<div className="relative overflow-hidden">
<div className="relative z-10">{children}</div>
{/* Top curtain */}
<motion.div
className="absolute inset-x-0 top-0 h-1/2 bg-slate-900 origin-top"
initial={{ scaleY: 1 }}
animate={{ scaleY: 0 }}
transition={{ duration: 0.8, ease: [0.25, 0.1, 0.25, 1] }}
/>
{/* Bottom curtain */}
<motion.div
className="absolute inset-x-0 bottom-0 h-1/2 bg-slate-900 origin-bottom"
initial={{ scaleY: 1 }}
animate={{ scaleY: 0 }}
transition={{ duration: 0.8, ease: [0.25, 0.1, 0.25, 1] }}
/>
</div>
);
}
Created by Brookside BI as part of React Animation Studio
development
Enhanced plan-authoring skill with Pre-Writing context gathering, task metadata, non-TDD templates, Red Flags, telemetry, and an automated plan linter. Use when you have a spec or requirements for a multi-step task, before touching code.
tools
Documentation intelligence engine with graph-based API docs, algorithm library, and drift detection
tools
Ultraplan cloud planning — kick off a plan in the cloud from your terminal, review and revise in the browser, then execute remotely or send back to CLI
tools
--- name: mcp description: Configure MCP servers for Claude Code — stdio vs HTTP, authentication, Tools/Resources/Prompts distinction, channels (CI webhook, mobile relay, Discord bridge, fakechat), and cost of always-loaded tools. Use this skill whenever adding an MCP server, debugging connection issues, choosing between MCP Tools vs Prompts vs Resources, installing channel servers, or managing .mcp.json. Triggers on: "MCP server", "mcp config", "add Obsidian MCP", "install context7", "channels"