Magic UI Shiny Button
21st.dev magicui/shiny-button: looping CSS mask shimmer across violet highlight, hover lift + ring glow. “Copy free TSX” primary CTA.
Dependencies
react
framer-motion
npm install framer-motion"use client";
import { motion, type AnimationProps } from "framer-motion";
function cn(...inputs: (string | undefined | null | false)[]) {
return inputs.filter(Boolean).join(" ");
}
const shineProps: AnimationProps = {
initial: { "--x": "100%", scale: 0.96 },
animate: { "--x": "-100%", scale: 1 },
whileHover: { scale: 1.04, y: -2, boxShadow: "0 12px 40px rgba(139,92,246,0.25)" },
whileTap: { scale: 0.95 },
transition: {
repeat: Infinity,
repeatType: "loop",
repeatDelay: 1.2,
type: "spring",
stiffness: 20,
damping: 15,
mass: 2,
},
};
export function ShinyCtaButton({ children = "Copy free TSX" }: { children?: React.ReactNode }) {
return (
<motion.button
{...shineProps}
style={{ "--x": "100%" } as React.CSSProperties}
className={cn(
"relative rounded-xl px-7 py-3 font-semibold",
"bg-zinc-900 shadow-lg ring-1 ring-white/10",
"transition-shadow duration-300 hover:ring-violet-500/30"
)}
>
<span
className="relative block text-sm tracking-wide text-white/90"
style={{
maskImage:
"linear-gradient(-75deg,#a78bfa calc(var(--x) + 20%),transparent calc(var(--x) + 30%),#c4b5fd calc(var(--x) + 100%))",
}}
>
{children}
</span>
<span
style={{
mask: "linear-gradient(#000,#000) content-box, linear-gradient(#000,#000)",
maskComposite: "exclude",
}}
className="absolute inset-0 z-10 block rounded-[inherit] bg-[linear-gradient(-75deg,rgba(167,139,250,0.15)_calc(var(--x)+20%),rgba(167,139,250,0.55)_calc(var(--x)+25%),rgba(167,139,250,0.15)_calc(var(--x)+100%))] p-px"
/>
</motion.button>
);
}
Please implement this code in the current project and add it to (Your Desired Section, e.g. homepage hero, pricing page, dashboard). Change the primary accent color to (YOUR HEX CODE COLOR, e.g. #6366F1).
Requirements:
- Use React + Tailwind CSS (match the project's existing setup)
- Install dependencies if needed: npm install framer-motion
- Keep the layout responsive on mobile and desktop
- Replace placeholder copy with content that fits the project
The UI code:
"use client";
import { motion, type AnimationProps } from "framer-motion";
function cn(...inputs: (string | undefined | null | false)[]) {
return inputs.filter(Boolean).join(" ");
}
const shineProps: AnimationProps = {
initial: { "--x": "100%", scale: 0.96 },
animate: { "--x": "-100%", scale: 1 },
whileHover: { scale: 1.04, y: -2, boxShadow: "0 12px 40px rgba(139,92,246,0.25)" },
whileTap: { scale: 0.95 },
transition: {
repeat: Infinity,
repeatType: "loop",
repeatDelay: 1.2,
type: "spring",
stiffness: 20,
damping: 15,
mass: 2,
},
};
export function ShinyCtaButton({ children = "Copy free TSX" }: { children?: React.ReactNode }) {
return (
<motion.button
{...shineProps}
style={{ "--x": "100%" } as React.CSSProperties}
className={cn(
"relative rounded-xl px-7 py-3 font-semibold",
"bg-zinc-900 shadow-lg ring-1 ring-white/10",
"transition-shadow duration-300 hover:ring-violet-500/30"
)}
>
<span
className="relative block text-sm tracking-wide text-white/90"
style={{
maskImage:
"linear-gradient(-75deg,#a78bfa calc(var(--x) + 20%),transparent calc(var(--x) + 30%),#c4b5fd calc(var(--x) + 100%))",
}}
>
{children}
</span>
<span
style={{
mask: "linear-gradient(#000,#000) content-box, linear-gradient(#000,#000)",
maskComposite: "exclude",
}}
className="absolute inset-0 z-10 block rounded-[inherit] bg-[linear-gradient(-75deg,rgba(167,139,250,0.15)_calc(var(--x)+20%),rgba(167,139,250,0.55)_calc(var(--x)+25%),rgba(167,139,250,0.15)_calc(var(--x)+100%))] p-px"
/>
</motion.button>
);
}
Replace (Your Desired Section) and (YOUR HEX CODE COLOR) before pasting into Cursor, Copilot, or ChatGPT.