feat: replace typing cursor with static subtitle, add gradient mesh hero background

This commit is contained in:
ashim-hq
2026-04-23 18:04:53 +08:00
parent c9f648708e
commit 8ff4155186
2 changed files with 13 additions and 54 deletions
+13 -4
View File
@@ -1,10 +1,18 @@
import { Github } from "lucide-react";
import { FadeIn } from "./fade-in";
import { TypingCursor } from "./typing-cursor";
export function Hero() {
return (
<section className="px-6 pt-32 pb-20 md:pt-44 md:pb-32">
<section className="relative overflow-hidden px-6 pt-32 pb-24 md:pt-44 md:pb-36">
{/* Stripe-style gradient mesh background */}
<div className="pointer-events-none absolute inset-0 -z-10">
<div className="absolute -top-[40%] -left-[20%] h-[80%] w-[60%] rounded-full bg-amber-400/20 blur-[120px]" />
<div className="absolute -top-[20%] -right-[10%] h-[70%] w-[50%] rounded-full bg-orange-300/15 blur-[100px]" />
<div className="absolute top-[20%] left-[30%] h-[60%] w-[40%] rounded-full bg-yellow-200/10 blur-[140px]" />
<div className="absolute -bottom-[30%] -right-[20%] h-[60%] w-[50%] rounded-full bg-orange-500/10 blur-[120px]" />
<div className="absolute bottom-0 left-0 h-px w-full bg-gradient-to-r from-transparent via-border to-transparent" />
</div>
<div className="mx-auto max-w-4xl text-center">
<FadeIn>
<h1 className="text-4xl font-extrabold tracking-tight sm:text-5xl md:text-6xl lg:text-7xl">
@@ -13,8 +21,9 @@ export function Hero() {
</FadeIn>
<FadeIn delay={0.1}>
<p className="mt-6 text-xl md:text-2xl">
<TypingCursor />
<p className="mx-auto mt-6 max-w-2xl text-lg text-muted md:text-xl">
50+ image processing tools with local AI. Self-hosted, air-gapped, and fully offline. No
data ever leaves your network.
</p>
</FadeIn>
@@ -1,50 +0,0 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { useCallback, useEffect, useState } from "react";
const phrases = [
"100% local processing.",
"Zero data leaves your network.",
"50+ image tools.",
"14 AI models. Your hardware.",
"Air-gapped ready.",
"Enterprise-grade. Free forever.",
"One Docker container.",
"Open source. Always.",
];
export function TypingCursor() {
const [index, setIndex] = useState(0);
const advance = useCallback(() => {
setIndex((i) => (i + 1) % phrases.length);
}, []);
useEffect(() => {
const timer = setInterval(advance, 3000);
return () => clearInterval(timer);
}, [advance]);
return (
<span className="inline-flex items-center">
<AnimatePresence mode="wait">
<motion.span
key={phrases[index]}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
className="text-accent"
>
{phrases[index]}
</motion.span>
</AnimatePresence>
<motion.span
animate={{ opacity: [1, 0] }}
transition={{ duration: 0.8, repeat: Infinity, repeatType: "reverse" }}
className="ml-0.5 inline-block w-[3px] h-[1em] bg-accent align-middle"
/>
</span>
);
}