Initial commit: Kit landing page
- Next.js 16 with Turbopack - React 19 - Tailwind CSS 4 with CSS-first config - Framer Motion animations - Animated logo and hero section - Tool cards for Vert and Paint - Glassmorphism effects and gradient animations - Fully responsive design - Docker support with Nginx - Static export ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
34
components/AnimatedBackground.tsx
Normal file
34
components/AnimatedBackground.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
export default function AnimatedBackground() {
|
||||
return (
|
||||
<div className="fixed inset-0 -z-10 overflow-hidden">
|
||||
{/* Animated gradient background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-50"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 25%, #f093fb 50%, #4facfe 75%, #667eea 100%)',
|
||||
backgroundSize: '400% 400%',
|
||||
animation: 'gradient 15s ease infinite',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Grid pattern overlay */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-10"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '50px 50px',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Floating orbs */}
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-purple-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-float" />
|
||||
<div className="absolute top-1/3 right-1/4 w-96 h-96 bg-cyan-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-float" style={{ animationDelay: '2s' }} />
|
||||
<div className="absolute bottom-1/4 left-1/3 w-96 h-96 bg-pink-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-float" style={{ animationDelay: '4s' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
components/Footer.tsx
Normal file
43
components/Footer.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="relative py-12 px-4 border-t border-gray-800">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<motion.div
|
||||
className="text-center"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
{/* Brand */}
|
||||
<div className="mb-4">
|
||||
<h3 className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-cyan-400">
|
||||
Kit
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div className="flex justify-center gap-8 mb-6 text-sm">
|
||||
<a
|
||||
href="https://pivoine.art"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-400 hover:text-purple-400 transition-colors"
|
||||
>
|
||||
pivoine.art
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Copyright */}
|
||||
<p className="text-gray-500 text-sm">
|
||||
© {new Date().getFullYear()} Kit. Built with Next.js & Tailwind CSS.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
70
components/Hero.tsx
Normal file
70
components/Hero.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import Logo from './Logo';
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className="relative min-h-screen flex flex-col items-center justify-center px-4 py-20">
|
||||
<div className="max-w-6xl mx-auto text-center">
|
||||
{/* Logo */}
|
||||
<motion.div
|
||||
className="mb-8 flex justify-center"
|
||||
initial={{ opacity: 0, y: -50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<Logo size={160} />
|
||||
</motion.div>
|
||||
|
||||
{/* Main heading */}
|
||||
<motion.h1
|
||||
className="text-6xl md:text-8xl font-bold mb-6 bg-clip-text text-transparent bg-gradient-to-r from-purple-400 via-pink-400 to-cyan-400"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
>
|
||||
Kit
|
||||
</motion.h1>
|
||||
|
||||
{/* Subtitle */}
|
||||
<motion.p
|
||||
className="text-xl md:text-2xl text-gray-300 mb-4 max-w-2xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
>
|
||||
Your Creative Toolkit
|
||||
</motion.p>
|
||||
|
||||
{/* Description */}
|
||||
<motion.p
|
||||
className="text-base md:text-lg text-gray-400 mb-12 max-w-xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
>
|
||||
A curated collection of creative and utility tools for developers and creators.
|
||||
Simple, powerful, and always at your fingertips.
|
||||
</motion.p>
|
||||
|
||||
{/* Scroll indicator */}
|
||||
<motion.div
|
||||
className="flex flex-col items-center gap-2"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8, delay: 1 }}
|
||||
>
|
||||
<span className="text-sm text-gray-500">Explore Tools</span>
|
||||
<motion.div
|
||||
className="w-6 h-10 border-2 border-gray-600 rounded-full p-1"
|
||||
animate={{ y: [0, 10, 0] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity }}
|
||||
>
|
||||
<div className="w-1 h-2 bg-gradient-to-b from-purple-400 to-cyan-400 rounded-full mx-auto" />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
119
components/Logo.tsx
Normal file
119
components/Logo.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export default function Logo({ className = '', size = 120 }: { className?: string; size?: number }) {
|
||||
return (
|
||||
<motion.svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 200 200"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
initial={{ opacity: 0, scale: 0.5, rotate: -180 }}
|
||||
animate={{ opacity: 1, scale: 1, rotate: 0 }}
|
||||
transition={{ duration: 1, ease: 'easeOut' }}
|
||||
>
|
||||
{/* Toolbox base */}
|
||||
<motion.rect
|
||||
x="40"
|
||||
y="80"
|
||||
width="120"
|
||||
height="80"
|
||||
rx="8"
|
||||
stroke="url(#gradient1)"
|
||||
strokeWidth="4"
|
||||
fill="rgba(139, 92, 246, 0.1)"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 1.5, ease: 'easeInOut' }}
|
||||
/>
|
||||
|
||||
{/* Toolbox handle */}
|
||||
<motion.path
|
||||
d="M 80 80 Q 100 40, 120 80"
|
||||
stroke="url(#gradient1)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 1.2, delay: 0.3, ease: 'easeInOut' }}
|
||||
/>
|
||||
|
||||
{/* Wrench */}
|
||||
<motion.g
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.8 }}
|
||||
>
|
||||
<path
|
||||
d="M 70 110 L 70 130"
|
||||
stroke="url(#gradient2)"
|
||||
strokeWidth="5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<circle cx="70" cy="105" r="6" fill="url(#gradient2)" />
|
||||
</motion.g>
|
||||
|
||||
{/* Pencil */}
|
||||
<motion.g
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 1 }}
|
||||
>
|
||||
<path
|
||||
d="M 100 110 L 100 135"
|
||||
stroke="url(#gradient3)"
|
||||
strokeWidth="5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M 95 110 L 100 105 L 105 110"
|
||||
fill="url(#gradient3)"
|
||||
/>
|
||||
</motion.g>
|
||||
|
||||
{/* Brush */}
|
||||
<motion.g
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 1.2 }}
|
||||
>
|
||||
<rect
|
||||
x="127"
|
||||
y="110"
|
||||
width="6"
|
||||
height="20"
|
||||
fill="url(#gradient4)"
|
||||
rx="1"
|
||||
/>
|
||||
<path
|
||||
d="M 125 110 L 130 105 L 135 110"
|
||||
fill="url(#gradient4)"
|
||||
/>
|
||||
</motion.g>
|
||||
|
||||
{/* Gradient definitions */}
|
||||
<defs>
|
||||
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#8b5cf6" />
|
||||
<stop offset="100%" stopColor="#6366f1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#2dd4bf" />
|
||||
<stop offset="100%" stopColor="#06b6d4" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient3" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#f59e0b" />
|
||||
<stop offset="100%" stopColor="#ef4444" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient4" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#10b981" />
|
||||
<stop offset="100%" stopColor="#14b8a6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</motion.svg>
|
||||
);
|
||||
}
|
||||
83
components/ToolCard.tsx
Normal file
83
components/ToolCard.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface ToolCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
url: string;
|
||||
gradient: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export default function ToolCard({ title, description, icon, url, gradient, index }: ToolCardProps) {
|
||||
return (
|
||||
<motion.a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group relative block"
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
whileHover={{ y: -10 }}
|
||||
>
|
||||
<div className="glass relative overflow-hidden rounded-2xl p-8 h-full transition-all duration-300 hover:shadow-2xl">
|
||||
{/* Gradient overlay on hover */}
|
||||
<div
|
||||
className={`absolute inset-0 opacity-0 group-hover:opacity-10 transition-opacity duration-300 ${gradient}`}
|
||||
/>
|
||||
|
||||
{/* Glow effect */}
|
||||
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300 blur-xl -z-10">
|
||||
<div className={`w-full h-full ${gradient}`} />
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<motion.div
|
||||
className="mb-6 flex justify-center"
|
||||
whileHover={{ scale: 1.1, rotate: 5 }}
|
||||
transition={{ type: 'spring', stiffness: 300 }}
|
||||
>
|
||||
<div className={`p-4 rounded-xl ${gradient}`}>
|
||||
{icon}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-2xl font-bold mb-3 text-white group-hover:text-transparent group-hover:bg-clip-text group-hover:bg-gradient-to-r group-hover:from-purple-400 group-hover:to-cyan-400 transition-all duration-300">
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-gray-400 group-hover:text-gray-300 transition-colors duration-300">
|
||||
{description}
|
||||
</p>
|
||||
|
||||
{/* Arrow icon */}
|
||||
<motion.div
|
||||
className="absolute bottom-8 right-8 text-gray-600 group-hover:text-purple-400"
|
||||
initial={{ x: 0 }}
|
||||
whileHover={{ x: 5 }}
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13 7l5 5m0 0l-5 5m5-5H6"
|
||||
/>
|
||||
</svg>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.a>
|
||||
);
|
||||
}
|
||||
81
components/ToolsGrid.tsx
Normal file
81
components/ToolsGrid.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import ToolCard from './ToolCard';
|
||||
|
||||
const tools = [
|
||||
{
|
||||
title: 'Vert',
|
||||
description: 'Privacy-focused file converter that processes images, audio, and documents locally on your device. No file size limits, completely open source.',
|
||||
url: 'https://vert.kit.pivoine.art',
|
||||
gradient: 'gradient-purple-blue',
|
||||
icon: (
|
||||
<svg className="w-12 h-12 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Paint',
|
||||
description: 'An advanced image editor running in your browser. Edit photos, create graphics, and more.',
|
||||
url: 'https://paint.kit.pivoine.art',
|
||||
gradient: 'gradient-cyan-purple',
|
||||
icon: (
|
||||
<svg className="w-12 h-12 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default function ToolsGrid() {
|
||||
return (
|
||||
<section className="relative py-20 px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Section heading */}
|
||||
<motion.div
|
||||
className="text-center mb-16"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-cyan-400">
|
||||
Available Tools
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
Explore our collection of carefully crafted tools designed to boost your productivity and creativity.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Tools grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{tools.map((tool, index) => (
|
||||
<ToolCard
|
||||
key={tool.title}
|
||||
title={tool.title}
|
||||
description={tool.description}
|
||||
icon={tool.icon}
|
||||
url={tool.url}
|
||||
gradient={tool.gradient}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Coming soon hint */}
|
||||
<motion.div
|
||||
className="mt-16 text-center"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
<p className="text-gray-500 text-sm">
|
||||
More tools coming soon...
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user