๐จ
UI Library
A collection of beautiful, reusable React components and CSS snippets built with Tailwind CSS and Framer Motion.
Buttons
Interactive and accessible button components with various states and styles.
Primary Button
ButtonPrimary.tsxLanguage: tsx
import { motion } from 'framer-motion';
export function PrimaryButton() {
return (
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="px-6 py-3 rounded-xl bg-button text-background font-semibold hover:bg-button-hover transition-colors duration-300"
>
Click Me
</motion.button>
);
}Secondary Button
ButtonSecondary.tsxLanguage: tsx
import { motion } from 'framer-motion';
export function SecondaryButton() {
return (
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="px-6 py-3 rounded-xl border border-border bg-surface hover:bg-black/5 dark:hover:bg-white/5 text-foreground font-medium transition-colors duration-300"
>
Secondary Action
</motion.button>
);
}Loading State
ButtonLoading.tsxLanguage: tsx
import { motion } from 'framer-motion';
import { useState } from 'react';
export function LoadingButton() {
const [isLoading, setIsLoading] = useState(false);
const handleClick = () => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
};
return (
<motion.button
onClick={handleClick}
disabled={isLoading}
whileHover={{ scale: isLoading ? 1 : 1.02 }}
whileTap={{ scale: isLoading ? 1 : 0.98 }}
className="relative flex items-center justify-center px-6 py-3 min-w-[140px] rounded-xl bg-button text-white font-semibold disabled:opacity-80 transition-all duration-300"
>
{isLoading ? (
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
"Submit"
)}
</motion.button>
);
}Gradient & Glow Effect
ButtonGradient.tsxLanguage: tsx
import { motion } from 'framer-motion';
export function GradientButton() {
return (
<div className="relative group">
<div className="absolute -inset-0.5 bg-gradient-to-r from-accent to-purple-600 rounded-xl blur opacity-30 group-hover:opacity-100 transition duration-500 group-hover:duration-200" />
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="relative px-6 py-3 bg-surface border border-border rounded-xl font-bold flex items-center gap-2 hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
>
<span className="bg-gradient-to-r from-accent to-purple-500 bg-clip-text text-transparent">
Gradient Glow
</span>
<svg className="w-5 h-5 text-purple-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
</motion.button>
</div>
);
}Icon-only Variants
ButtonIcon.tsxLanguage: tsx
import { motion } from 'framer-motion';
export function IconOnlyButton() {
return (
<div className="flex gap-4">
{/* Primary Icon */}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="p-3 rounded-full bg-button text-white shadow-lg shadow-accent/20 hover:bg-button-hover transition-colors"
aria-label="Add new"
>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
</motion.button>
{/* Surface Icon */}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="p-3 rounded-xl border border-border bg-surface text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
aria-label="Settings"
>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37... (truncated visually)" /><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
</motion.button>
</div>
);
}Disabled State
ButtonDisabled.tsxLanguage: tsx
export function DisabledButton() {
return (
<button
disabled
className="px-6 py-3 rounded-xl bg-surface text-muted cursor-not-allowed border border-border opacity-60"
>
Not Allowed
</button>
);
}