mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
updage
This commit is contained in:
+20
-17
@@ -1,3 +1,4 @@
|
||||
import { ThemeProvider } from "@/contexts/ThemeContext"
|
||||
import { ParticleBackground } from "@/components/ui/magicui"
|
||||
import { Hero } from "@/components/sections/Hero"
|
||||
import { Features } from "@/components/sections/Features"
|
||||
@@ -9,23 +10,25 @@ import { Footer } from "@/components/sections/Footer"
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="relative min-h-screen bg-background text-foreground overflow-x-hidden">
|
||||
{/* Particle Background */}
|
||||
<ParticleBackground />
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="relative z-10">
|
||||
<Hero />
|
||||
<Features />
|
||||
<CodeShowcase />
|
||||
<Architecture />
|
||||
<Languages />
|
||||
<QuickStart />
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<Footer />
|
||||
</div>
|
||||
<ThemeProvider defaultTheme="dark" storageKey="devour-ui-theme">
|
||||
<div className="relative min-h-screen bg-background text-foreground overflow-x-hidden">
|
||||
{/* Particle Background */}
|
||||
<ParticleBackground />
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="relative z-10">
|
||||
<Hero />
|
||||
<Features />
|
||||
<CodeShowcase />
|
||||
<Architecture />
|
||||
<Languages />
|
||||
<QuickStart />
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<Footer />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export function Architecture() {
|
||||
{/* Central Flow */}
|
||||
<div className="relative rounded-2xl border border-white/10 bg-white/5 backdrop-blur-sm p-8 md:p-12">
|
||||
{/* Data Sources */}
|
||||
<div className="flex justify-center gap-4 mb-8">
|
||||
<div className="flex flex-wrap justify-center gap-2 sm:gap-4 mb-6 sm:mb-8">
|
||||
{sources.map((source, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
@@ -45,8 +45,8 @@ export function Architecture() {
|
||||
transition={{ delay: 0.3 + i * 0.1 }}
|
||||
className="flex flex-col items-center gap-2"
|
||||
>
|
||||
<div className={`p-3 rounded-lg bg-white/5 border border-white/10`}>
|
||||
<source.icon className={`w-5 h-5 ${source.color}`} />
|
||||
<div className={`p-2 sm:p-3 rounded-lg bg-white/5 border border-white/10`}>
|
||||
<source.icon className={`w-4 h-4 sm:w-5 sm:h-5 ${source.color}`} />
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground">{source.label}</span>
|
||||
</motion.div>
|
||||
@@ -64,7 +64,7 @@ export function Architecture() {
|
||||
</div>
|
||||
|
||||
{/* Main Components Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-3 sm:gap-4 mb-6 sm:mb-8">
|
||||
{/* Scraper */}
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.02 }}
|
||||
@@ -123,7 +123,7 @@ export function Architecture() {
|
||||
</div>
|
||||
|
||||
{/* Server & Query */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
{/* Server */}
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.02 }}
|
||||
|
||||
@@ -125,7 +125,7 @@ export function Features() {
|
||||
</div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6" staggerDelay={0.05}>
|
||||
<StaggerContainer className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6" staggerDelay={0.05}>
|
||||
{features.map((feature, index) => (
|
||||
<StaggerItem key={index}>
|
||||
<motion.div
|
||||
|
||||
@@ -28,8 +28,8 @@ export function Footer() {
|
||||
{/* Background */}
|
||||
<div className="absolute inset-0 grid-background opacity-20" />
|
||||
|
||||
<div className="container relative z-10 px-4 md:px-6 py-12 md:py-16">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8 lg:gap-12">
|
||||
<div className="container relative z-10 px-4 md:px-6 py-8 sm:py-12 md:py-16">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-6 lg:gap-8 lg:gap-12">
|
||||
{/* Brand */}
|
||||
<div className="lg:col-span-2">
|
||||
<FadeIn>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { motion } from "framer-motion"
|
||||
import { Github, Terminal, Sparkles, ArrowRight } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { ThemeToggle } from "@/components/ui/theme-toggle"
|
||||
import { GradientText, FadeIn } from "@/components/ui/magicui"
|
||||
|
||||
export function Hero() {
|
||||
@@ -12,6 +13,11 @@ export function Hero() {
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-teal-500/20 rounded-full blur-3xl animate-pulse delay-1000" />
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-cyan-500/10 rounded-full blur-3xl" />
|
||||
|
||||
<div className="absolute top-0 left-0 right-0 z-20 flex justify-between items-center p-6">
|
||||
<div></div>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
<div className="container relative z-10 px-4 md:px-6">
|
||||
<div className="flex flex-col items-center text-center space-y-8">
|
||||
{/* Badge */}
|
||||
@@ -34,21 +40,21 @@ export function Hero() {
|
||||
<img
|
||||
src="/devour_logo.svg"
|
||||
alt="Devour Logo"
|
||||
className="relative w-32 h-32 md:w-40 md:h-40 drop-shadow-2xl"
|
||||
className="relative w-24 h-24 sm:w-32 sm:h-32 md:w-40 md:h-40 drop-shadow-2xl"
|
||||
/>
|
||||
</motion.div>
|
||||
</FadeIn>
|
||||
|
||||
{/* Title */}
|
||||
<FadeIn delay={0.3}>
|
||||
<h1 className="text-5xl md:text-7xl font-bold tracking-tight">
|
||||
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight">
|
||||
<GradientText>Devour</GradientText>
|
||||
</h1>
|
||||
</FadeIn>
|
||||
|
||||
{/* Subtitle */}
|
||||
<FadeIn delay={0.4}>
|
||||
<p className="text-xl md:text-2xl text-muted-foreground max-w-2xl">
|
||||
<p className="text-lg sm:text-xl md:text-2xl text-muted-foreground max-w-2xl">
|
||||
Context Ingestion & Management for{" "}
|
||||
<span className="text-cyan-400">AI</span>
|
||||
</p>
|
||||
@@ -56,7 +62,7 @@ export function Hero() {
|
||||
|
||||
{/* Description */}
|
||||
<FadeIn delay={0.5}>
|
||||
<p className="text-base md:text-lg text-muted-foreground/80 max-w-3xl leading-relaxed">
|
||||
<p className="text-sm sm:text-base md:text-lg text-muted-foreground/80 max-w-3xl leading-relaxed px-4 sm:px-0">
|
||||
Scrape, index, and serve documentation from multiple sources.
|
||||
Feed structured, relevant context to AI models for generating
|
||||
accurate, fully working code.
|
||||
@@ -104,30 +110,30 @@ export function Hero() {
|
||||
|
||||
{/* Terminal Preview */}
|
||||
<FadeIn delay={0.8}>
|
||||
<div className="w-full max-w-3xl mt-12">
|
||||
<div className="w-full max-w-3xl mt-8 sm:mt-12">
|
||||
<div className="relative rounded-xl border border-white/10 bg-black/40 backdrop-blur-sm overflow-hidden">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 border-b border-white/10 bg-white/5">
|
||||
<div className="flex items-center gap-2 px-3 sm:px-4 py-2 sm:py-3 border-b border-white/10 bg-white/5">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
||||
<div className="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-full bg-yellow-500/80" />
|
||||
<div className="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-full bg-green-500/80" />
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground ml-2">terminal</span>
|
||||
</div>
|
||||
{/* Terminal Content */}
|
||||
<div className="p-4 font-mono text-sm">
|
||||
<div className="p-3 sm:p-4 font-mono text-xs sm:text-sm">
|
||||
<div className="text-muted-foreground">$ devour get go http</div>
|
||||
<div className="mt-2 text-cyan-400">
|
||||
<div className="mt-1 sm:mt-2 text-cyan-400">
|
||||
<span className="text-green-400">✓</span> Fetching Go net/http documentation...
|
||||
</div>
|
||||
<div className="mt-1 text-muted-foreground/80">
|
||||
<div className="mt-0.5 sm:mt-1 text-muted-foreground/80">
|
||||
<span className="text-cyan-500">→</span> Indexing 47 documents
|
||||
</div>
|
||||
<div className="mt-1 text-muted-foreground/80">
|
||||
<div className="mt-0.5 sm:mt-1 text-muted-foreground/80">
|
||||
<span className="text-cyan-500">→</span> Creating vector embeddings
|
||||
</div>
|
||||
<div className="mt-2 text-green-400">
|
||||
<div className="mt-1 sm:mt-2 text-green-400">
|
||||
Ready! Query with: devour query "How to create a server?"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,89 +1,114 @@
|
||||
import { motion } from "framer-motion"
|
||||
import {
|
||||
Code,
|
||||
Terminal,
|
||||
Cpu,
|
||||
BookOpen,
|
||||
Atom,
|
||||
Heart,
|
||||
Rocket,
|
||||
Cloud,
|
||||
Coffee,
|
||||
Leaf,
|
||||
Container
|
||||
} from "lucide-react"
|
||||
import { GradientText, FadeIn, StaggerContainer, StaggerItem } from "@/components/ui/magicui"
|
||||
|
||||
const languages = [
|
||||
{
|
||||
name: "Go",
|
||||
alias: "golang",
|
||||
icon: "🐹",
|
||||
color: "from-cyan-400 to-cyan-600",
|
||||
icon: Cpu,
|
||||
color: "text-cyan-500",
|
||||
bgColor: "bg-cyan-500/10",
|
||||
docs: "pkg.go.dev",
|
||||
},
|
||||
{
|
||||
name: "Python",
|
||||
alias: "py",
|
||||
icon: "🐍",
|
||||
color: "from-yellow-400 to-green-500",
|
||||
icon: Terminal,
|
||||
color: "text-green-500",
|
||||
bgColor: "bg-green-500/10",
|
||||
docs: "docs.python.org",
|
||||
},
|
||||
{
|
||||
name: "Rust",
|
||||
alias: "rust",
|
||||
icon: "🦀",
|
||||
color: "from-orange-400 to-red-500",
|
||||
icon: Code,
|
||||
color: "text-orange-500",
|
||||
bgColor: "bg-orange-500/10",
|
||||
docs: "docs.rs",
|
||||
},
|
||||
{
|
||||
name: "TypeScript",
|
||||
alias: "ts",
|
||||
icon: "📘",
|
||||
color: "from-blue-400 to-blue-600",
|
||||
icon: BookOpen,
|
||||
color: "text-blue-500",
|
||||
bgColor: "bg-blue-500/10",
|
||||
docs: "typescriptlang.org",
|
||||
},
|
||||
{
|
||||
name: "React",
|
||||
alias: "react",
|
||||
icon: "⚛️",
|
||||
color: "from-cyan-400 to-purple-500",
|
||||
icon: Atom,
|
||||
color: "text-cyan-500",
|
||||
bgColor: "bg-cyan-500/10",
|
||||
docs: "react.dev",
|
||||
},
|
||||
{
|
||||
name: "Vue",
|
||||
alias: "vue",
|
||||
icon: "💚",
|
||||
color: "from-green-400 to-emerald-500",
|
||||
icon: Heart,
|
||||
color: "text-green-500",
|
||||
bgColor: "bg-green-500/10",
|
||||
docs: "vuejs.org",
|
||||
},
|
||||
{
|
||||
name: "Nuxt",
|
||||
alias: "nuxt",
|
||||
icon: "💚",
|
||||
color: "from-green-400 to-teal-500",
|
||||
icon: Leaf,
|
||||
color: "text-teal-500",
|
||||
bgColor: "bg-teal-500/10",
|
||||
docs: "nuxt.com",
|
||||
},
|
||||
{
|
||||
name: "Docker",
|
||||
alias: "docker",
|
||||
icon: "🐳",
|
||||
color: "from-blue-400 to-cyan-500",
|
||||
icon: Container,
|
||||
color: "text-blue-500",
|
||||
bgColor: "bg-blue-500/10",
|
||||
docs: "docs.docker.com",
|
||||
},
|
||||
{
|
||||
name: "Java",
|
||||
alias: "java",
|
||||
icon: "☕",
|
||||
color: "from-red-400 to-orange-500",
|
||||
icon: Coffee,
|
||||
color: "text-red-500",
|
||||
bgColor: "bg-red-500/10",
|
||||
docs: "docs.oracle.com",
|
||||
},
|
||||
{
|
||||
name: "Spring",
|
||||
alias: "spring",
|
||||
icon: "🍃",
|
||||
color: "from-green-500 to-green-600",
|
||||
icon: Leaf,
|
||||
color: "text-green-600",
|
||||
bgColor: "bg-green-600/10",
|
||||
docs: "docs.spring.io",
|
||||
},
|
||||
{
|
||||
name: "Astro",
|
||||
alias: "astro",
|
||||
icon: "🚀",
|
||||
color: "from-purple-400 to-pink-500",
|
||||
icon: Rocket,
|
||||
color: "text-purple-500",
|
||||
bgColor: "bg-purple-500/10",
|
||||
docs: "docs.astro.build",
|
||||
},
|
||||
{
|
||||
name: "Cloudflare",
|
||||
alias: "cf",
|
||||
icon: "☁️",
|
||||
color: "from-orange-400 to-yellow-500",
|
||||
icon: Cloud,
|
||||
color: "text-orange-500",
|
||||
bgColor: "bg-orange-500/10",
|
||||
docs: "developers.cloudflare.com",
|
||||
},
|
||||
]
|
||||
@@ -111,7 +136,7 @@ export function Languages() {
|
||||
|
||||
{/* Languages Grid */}
|
||||
<StaggerContainer
|
||||
className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4"
|
||||
className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-3 sm:gap-4"
|
||||
staggerDelay={0.05}
|
||||
>
|
||||
{languages.map((lang, index) => (
|
||||
@@ -122,7 +147,9 @@ export function Languages() {
|
||||
className="group relative p-4 rounded-xl border border-white/10 bg-white/5 backdrop-blur-sm hover:border-cyan-500/30 hover:bg-white/[0.07] transition-all duration-300 cursor-pointer"
|
||||
>
|
||||
{/* Icon */}
|
||||
<div className="text-3xl mb-3">{lang.icon}</div>
|
||||
<div className={`flex items-center justify-center mb-3 p-3 rounded-lg ${lang.bgColor}`}>
|
||||
<lang.icon className={`w-6 h-6 ${lang.color}`} />
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<h3 className="font-semibold mb-1 group-hover:text-cyan-400 transition-colors">
|
||||
|
||||
@@ -63,16 +63,16 @@ export function QuickStart() {
|
||||
|
||||
{/* Steps */}
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-4 sm:gap-6">
|
||||
{steps.map((step, index) => (
|
||||
<FadeIn key={index} delay={0.1 + index * 0.1}>
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.01 }}
|
||||
className="group relative flex flex-col md:flex-row md:items-center gap-4 p-6 rounded-xl border border-white/10 bg-white/5 backdrop-blur-sm hover:border-cyan-500/30 hover:bg-white/[0.07] transition-all duration-300"
|
||||
className="group relative flex flex-col lg:flex-row lg:items-center gap-4 p-4 sm:p-6 rounded-xl border border-white/10 bg-white/5 backdrop-blur-sm hover:border-cyan-500/30 hover:bg-white/[0.07] transition-all duration-300"
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex-shrink-0 w-16 h-16 flex items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500/20 to-teal-500/20 border border-cyan-500/30">
|
||||
<span className="text-2xl font-bold text-cyan-400">{step.number}</span>
|
||||
<div className="flex-shrink-0 w-12 h-12 sm:w-16 sm:h-16 flex items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500/20 to-teal-500/20 border border-cyan-500/30">
|
||||
<span className="text-lg sm:text-2xl font-bold text-cyan-400">{step.number}</span>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
@@ -85,8 +85,8 @@ export function QuickStart() {
|
||||
</p>
|
||||
|
||||
{/* Command */}
|
||||
<div className="relative flex items-center gap-2 p-3 rounded-lg bg-black/40 border border-white/10 font-mono text-sm">
|
||||
<Terminal className="w-4 h-4 text-cyan-400 flex-shrink-0" />
|
||||
<div className="relative flex items-center gap-2 p-2 sm:p-3 rounded-lg bg-black/40 border border-white/10 font-mono text-xs sm:text-sm">
|
||||
<Terminal className="w-3 h-3 sm:w-4 sm:h-4 text-cyan-400 flex-shrink-0" />
|
||||
<code className="flex-1 text-muted-foreground overflow-x-auto">
|
||||
<span className="text-green-400">$</span> {step.command}
|
||||
</code>
|
||||
@@ -95,9 +95,9 @@ export function QuickStart() {
|
||||
className="flex-shrink-0 p-1.5 rounded hover:bg-white/10 transition-colors"
|
||||
>
|
||||
{copiedStep === index ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
<Check className="w-3 h-3 sm:w-4 sm:h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-muted-foreground hover:text-foreground" />
|
||||
<Copy className="w-3 h-3 sm:w-4 sm:h-4 text-muted-foreground hover:text-foreground" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@@ -105,8 +105,8 @@ export function QuickStart() {
|
||||
|
||||
{/* Arrow (except last) */}
|
||||
{index < steps.length - 1 && (
|
||||
<div className="hidden md:flex absolute -bottom-6 left-1/2 -translate-x-1/2 z-10">
|
||||
<ArrowRight className="w-4 h-4 text-cyan-500/50 rotate-90" />
|
||||
<div className="hidden lg:flex absolute -bottom-4 sm:-bottom-6 left-1/2 -translate-x-1/2 z-10">
|
||||
<ArrowRight className="w-3 h-3 sm:w-4 sm:h-4 text-cyan-500/50 rotate-90" />
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
@@ -4,18 +4,18 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80 shadow-sm",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
glow: "border-transparent bg-primary/20 text-primary border border-primary/30",
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80 shadow-sm",
|
||||
outline: "text-foreground border border-input hover:bg-accent hover:text-accent-foreground",
|
||||
glow: "border-transparent bg-gradient-to-r from-primary/20 to-primary/10 text-primary border border-primary/30 shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -5,26 +5,26 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-lg hover:shadow-primary/25",
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-lg hover:shadow-primary/25 hover:-translate-y-0.5 active:translate-y-0",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90 hover:shadow-lg hover:shadow-destructive/25",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:shadow-md hover:-translate-y-0.5 active:translate-y-0",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80 hover:shadow-md hover:-translate-y-0.5 active:translate-y-0",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground hover:shadow-sm",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
glow: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg shadow-primary/25 hover:shadow-xl hover:shadow-primary/30",
|
||||
glow: "bg-gradient-to-r from-primary to-primary/90 text-primary-foreground shadow-lg shadow-primary/25 hover:shadow-xl hover:shadow-primary/30 hover:-translate-y-0.5 active:translate-y-0 border border-primary/20",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
xl: "h-12 rounded-lg px-10 text-base",
|
||||
xl: "h-12 rounded-lg px-10 text-base font-semibold",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto h-4 w-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="h-2 w-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-semibold",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||
|
||||
const DropdownMenuShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Moon, Sun, Monitor } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { useTheme } from "@/contexts/ThemeContext"
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||
<Sun className="mr-2 h-4 w-4" />
|
||||
<span>Light</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||
<Moon className="mr-2 h-4 w-4" />
|
||||
<span>Dark</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||
<Monitor className="mr-2 h-4 w-4" />
|
||||
<span>System</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
type Theme = 'dark' | 'light' | 'system'
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode
|
||||
defaultTheme?: Theme
|
||||
storageKey?: string
|
||||
}
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: Theme
|
||||
setTheme: (theme: Theme) => void
|
||||
}
|
||||
|
||||
const initialState: ThemeProviderState = {
|
||||
theme: 'system',
|
||||
setTheme: () => null,
|
||||
}
|
||||
|
||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
defaultTheme = 'system',
|
||||
storageKey = 'devour-ui-theme',
|
||||
...props
|
||||
}: ThemeProviderProps) {
|
||||
const [theme, setTheme] = useState<Theme>(
|
||||
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement
|
||||
|
||||
root.classList.remove('light', 'dark')
|
||||
|
||||
if (theme === 'system') {
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
.matches
|
||||
? 'dark'
|
||||
: 'light'
|
||||
|
||||
root.classList.add(systemTheme)
|
||||
return
|
||||
}
|
||||
|
||||
root.classList.add(theme)
|
||||
}, [theme])
|
||||
|
||||
const value = {
|
||||
theme,
|
||||
setTheme: (theme: Theme) => {
|
||||
localStorage.setItem(storageKey, theme)
|
||||
setTheme(theme)
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeProviderContext.Provider {...props} value={value}>
|
||||
{children}
|
||||
</ThemeProviderContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeProviderContext)
|
||||
|
||||
if (context === undefined)
|
||||
throw new Error('useTheme must be used within a ThemeProvider')
|
||||
|
||||
return context
|
||||
}
|
||||
+159
-22
@@ -1,6 +1,7 @@
|
||||
@import "tailwindcss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
|
||||
|
||||
/* Custom CSS Variables */
|
||||
/* CSS Variables for both light and dark themes */
|
||||
:root {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
@@ -24,6 +25,50 @@
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.light {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 187 92% 43%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 187 92% 43%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 187 92% 43%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 187 92% 43%;
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
* {
|
||||
border-color: hsl(var(--border));
|
||||
@@ -33,6 +78,8 @@ body {
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
@@ -42,16 +89,29 @@ body {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: hsl(222.2 84% 4.9%);
|
||||
background: hsl(var(--background));
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: hsl(217.2 32.6% 17.5%);
|
||||
background: hsl(var(--muted-foreground) / 0.3);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: hsl(215 20.2% 35.1%);
|
||||
background: hsl(var(--muted-foreground) / 0.5);
|
||||
}
|
||||
|
||||
/* Light theme scrollbar */
|
||||
.light ::-webkit-scrollbar-track {
|
||||
background: hsl(var(--muted));
|
||||
}
|
||||
|
||||
.light ::-webkit-scrollbar-thumb {
|
||||
background: hsl(var(--border));
|
||||
}
|
||||
|
||||
.light ::-webkit-scrollbar-thumb:hover {
|
||||
background: hsl(var(--muted-foreground) / 0.7);
|
||||
}
|
||||
|
||||
/* Gradient text */
|
||||
@@ -59,53 +119,64 @@ body {
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
background-image: linear-gradient(to right, #22d3ee, #67e8f9, #2dd4bf);
|
||||
background-image: linear-gradient(135deg, #22d3ee, #67e8f9, #2dd4bf);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Glow effects */
|
||||
.glow {
|
||||
box-shadow: 0 0 20px rgba(6, 182, 212, 0.3),
|
||||
0 0 40px rgba(6, 182, 212, 0.2),
|
||||
0 0 60px rgba(6, 182, 212, 0.1);
|
||||
box-shadow: 0 0 20px hsl(var(--primary) / 0.3),
|
||||
0 0 40px hsl(var(--primary) / 0.2),
|
||||
0 0 60px hsl(var(--primary) / 0.1);
|
||||
}
|
||||
|
||||
.glow-text {
|
||||
text-shadow: 0 0 20px rgba(6, 182, 212, 0.5),
|
||||
0 0 40px rgba(6, 182, 212, 0.3);
|
||||
text-shadow: 0 0 20px hsl(var(--primary) / 0.5),
|
||||
0 0 40px hsl(var(--primary) / 0.3);
|
||||
}
|
||||
|
||||
/* Glass effect */
|
||||
.glass {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
background-color: hsl(var(--background) / 0.05);
|
||||
backdrop-filter: blur(24px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.light .glass {
|
||||
background-color: hsl(var(--background) / 0.8);
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
/* Grid background */
|
||||
.grid-background {
|
||||
background-image:
|
||||
linear-gradient(rgba(6, 182, 212, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(6, 182, 212, 0.03) 1px, transparent 1px);
|
||||
linear-gradient(hsl(var(--border) / 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, hsl(var(--border) / 0.03) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
|
||||
/* Animated gradient border */
|
||||
.gradient-border {
|
||||
position: relative;
|
||||
background: linear-gradient(hsl(222.2 84% 4.9%), hsl(222.2 84% 4.9%)) padding-box,
|
||||
linear-gradient(135deg, hsl(187 92% 43%), hsl(160 84% 39%), hsl(187 92% 43%)) border-box;
|
||||
background: linear-gradient(hsl(var(--background)), hsl(var(--background))) padding-box,
|
||||
linear-gradient(135deg, hsl(var(--primary)), hsl(var(--accent)), hsl(var(--primary))) border-box;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
.code-block {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
background-color: hsl(var(--muted) / 0.5);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 0.5rem;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
}
|
||||
|
||||
.light .code-block {
|
||||
background-color: hsl(var(--muted));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
/* Smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
@@ -113,8 +184,8 @@ html {
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: rgba(6, 182, 212, 0.3);
|
||||
color: white;
|
||||
background: hsl(var(--primary) / 0.3);
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
|
||||
/* Custom animations */
|
||||
@@ -129,8 +200,8 @@ html {
|
||||
}
|
||||
|
||||
@keyframes glow-anim {
|
||||
0%, 100% { box-shadow: 0 0 20px rgba(6, 182, 212, 0.3); }
|
||||
50% { box-shadow: 0 0 40px rgba(6, 182, 212, 0.6); }
|
||||
0%, 100% { box-shadow: 0 0 20px hsl(var(--primary) / 0.3); }
|
||||
50% { box-shadow: 0 0 40px hsl(var(--primary) / 0.6); }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
@@ -138,6 +209,55 @@ html {
|
||||
to { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-left {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gradient-x {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
@@ -152,4 +272,21 @@ html {
|
||||
|
||||
.animate-shimmer {
|
||||
animation: shimmer 2s linear infinite;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-in-left {
|
||||
animation: slide-in-left 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animate-bounce-in {
|
||||
animation: bounce-in 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animate-gradient-x {
|
||||
animation: gradient-x 3s ease infinite;
|
||||
background-size: 200% 200%;
|
||||
}
|
||||
Reference in New Issue
Block a user