Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions frontend/app/[locale]/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import { getPlatformStats } from "@/lib/about/stats"
import { getSponsors } from "@/lib/about/github-sponsors"

import { HeroSection } from "@/components/about/HeroSection"
import { TopicsSection } from "@/components/about/TopicsSection"
import { FeaturesSection } from "@/components/about/FeaturesSection"
import { PricingSection } from "@/components/about/PricingSection"
import { CommunitySection } from "@/components/about/CommunitySection"

export default function AboutPage() {
export default async function AboutPage() {
const [stats, sponsors] = await Promise.all([
getPlatformStats(),
getSponsors()
])

return (
<main className="min-h-screen bg-neutral-950 text-white">
<HeroSection />
<main className="min-h-screen bg-gray-50 dark:bg-black overflow-hidden text-gray-900 dark:text-white
w-[100vw] relative left-[50%] right-[50%] -ml-[50vw] -mr-[50vw]"
>

<HeroSection stats={stats} />
<TopicsSection />
<FeaturesSection />
<PricingSection />
<PricingSection sponsors={sponsors} />
<CommunitySection />

</main>
)
}
}
2 changes: 1 addition & 1 deletion frontend/app/[locale]/contacts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ export default function ContactsPage() {
</ul>
</main>
);
}
}
29 changes: 29 additions & 0 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,32 @@
--border: color-mix(in oklab, var(--foreground) 18%, var(--background));
--input: color-mix(in oklab, var(--foreground) 18%, var(--background));
}

/* about-community */
@keyframes scroll {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}

.animate-scroll {
animation: scroll 40s linear infinite;
}

.pause-on-hover:hover .animate-scroll {
animation-play-state: paused;
}

/* Vertical Marquee Animation */
@keyframes marquee-vertical {
0% { transform: translateY(0); }
100% { transform: translateY(-50%); } /* Рухаємось на 50%, бо контент дубльовано */
}

.animate-marquee-vertical {
animation: marquee-vertical var(--duration, 40s) linear infinite;
}

/* Зупинка при наведенні мишкою, щоб роздивитися */
.hover\:pause:hover .animate-marquee-vertical {
animation-play-state: paused;
}
185 changes: 90 additions & 95 deletions frontend/components/about/CommunitySection.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,85 @@
"use client"

import { motion } from "framer-motion"
import { MessageCircle } from "lucide-react"
import { useTranslations } from "next-intl"
import { MessageCircle, Github, ArrowRight, ExternalLink } from "lucide-react"
import { TESTIMONIALS, type Testimonial } from "@/data/about"

const testimonialData = [
{
name: "Alex Chen",
role: "Frontend Engineer @ Meta",
avatar: "AC",
platform: "LinkedIn",
},
{
name: "Sarah Johnson",
role: "Senior SWE @ Google",
avatar: "SJ",
platform: "Twitter",
},
{
name: "Marcus Williams",
role: "Backend Developer @ Stripe",
avatar: "MW",
platform: "Twitter",
},
{
name: "Emily Park",
role: "Full Stack @ Vercel",
avatar: "EP",
platform: "LinkedIn",
},
{
name: "David Kim",
role: "Staff Engineer @ Netflix",
avatar: "DK",
platform: "Twitter",
},
{
name: "Lisa Thompson",
role: "Engineering Manager @ Amazon",
avatar: "LT",
platform: "LinkedIn",
},
]
import Link from "next/link"

export function CommunitySection() {
const t = useTranslations("about.community")
return (
<section className="w-full py-16 md:py-24 relative overflow-hidden bg-gray-50 dark:bg-transparent">

<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[300px] h-[300px] bg-[#1e5eff]/5 dark:bg-[#ff2d55]/5 blur-[80px] rounded-full pointer-events-none" />

const testimonials = testimonialData.map((data, index) => ({
...data,
content: t(`testimonials.${index}`),
}))
<div className="w-full relative z-10">

<div className="max-w-7xl mx-auto px-4 mb-12 text-center">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full border border-gray-200 dark:border-white/10 bg-white dark:bg-white/5 text-gray-500 dark:text-gray-400 text-[10px] font-bold uppercase tracking-widest mb-4">
<MessageCircle size={10} /> Community Love
</div>

<h2 className="text-3xl md:text-4xl font-black tracking-tight text-gray-900 dark:text-white mb-4">
Approved by <span className="text-transparent bg-clip-text bg-gradient-to-r from-[#1e5eff] to-[#174ad6] dark:from-[#ff2d55] dark:to-[#e0264b]">Survivors</span>
</h2>
<p className="text-gray-600 dark:text-gray-400 max-w-xl mx-auto text-base font-light leading-relaxed">
Join thousands of developers who stopped guessing and started shipping. Real feedback from real engineers.
</p>
</div>

return (
<section className="overflow-hidden px-6 py-24 bg-background transition-colors duration-300">
<div className="mx-auto max-w-6xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="mb-16 text-center"
>
<h2 className="text-3xl font-bold text-foreground md:text-4xl transition-colors duration-300">
{t("title")}
</h2>
<p className="mt-4 text-muted-foreground transition-colors duration-300">
{t("subtitle")}
</p>
</motion.div>
<div className="relative w-full pause-on-hover mb-16">

<div className="pointer-events-none absolute left-0 top-0 z-10 h-full w-12 md:w-32 bg-gradient-to-r from-gray-50 dark:from-background to-transparent" />
<div className="pointer-events-none absolute right-0 top-0 z-10 h-full w-12 md:w-32 bg-gradient-to-l from-gray-50 dark:from-background to-transparent" />

<div className="relative">
<div className="pointer-events-none absolute left-0 top-0 z-10 h-full w-32 bg-gradient-to-r from-background to-transparent transition-colors duration-300" />
<div className="pointer-events-none absolute right-0 top-0 z-10 h-full w-32 bg-gradient-to-l from-background to-transparent transition-colors duration-300" />
<div className="flex w-max min-w-full">
<div className="flex shrink-0 gap-4 px-2 animate-scroll">
{TESTIMONIALS.map((testimonial, index) => (
<TestimonialCard key={`loop1-${index}`} {...testimonial} />
))}
</div>
<div className="flex shrink-0 gap-4 px-2 animate-scroll" aria-hidden="true">
{TESTIMONIALS.map((testimonial, index) => (
<TestimonialCard key={`loop2-${index}`} {...testimonial} />
))}
</div>
</div>
</div>

<div className="max-w-7xl mx-auto px-4 text-center">
<Link
href="https://github.com/DevLoversTeam/devlovers.net/discussions"
target="_blank"
className="group relative inline-flex flex-col md:flex-row items-center justify-center gap-4 p-1.5 pl-6 pr-1.5 rounded-full
bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10
transition-all duration-300 ease-out
hover:scale-[1.02]
hover:border-[#1e5eff]/50 dark:hover:border-[#ff2d55]/50
hover:shadow-[0_0_30px_-5px_rgba(30,94,255,0.15)] dark:hover:shadow-[0_0_30px_-5px_rgba(255,45,85,0.15)]"
>
<div className="flex flex-col items-start md:items-center">
<span className="text-sm text-gray-700 dark:text-gray-200 font-medium">
Have a success story or feature request?
</span>
</div>

<span className="flex items-center gap-2 px-5 py-2.5 rounded-full
bg-gray-900 dark:bg-white text-white dark:text-black
text-xs font-bold uppercase tracking-wider
transition-all duration-300
group-hover:bg-[#1e5eff] dark:group-hover:bg-[#ff2d55]
group-hover:text-white dark:group-hover:text-white"
>
<Github size={14} />
Join Discussion
<ArrowRight size={12} className="group-hover:translate-x-1 transition-transform" />
</span>
</Link>

<p className="mt-6 text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-widest font-bold opacity-60">
We read every single thread
</p>
</div>

<div className="flex gap-6 overflow-hidden">
<motion.div
animate={{ x: [0, -1920] }}
transition={{ duration: 60, repeat: Number.POSITIVE_INFINITY, ease: "linear" }}
className="flex shrink-0 gap-6"
>
{[...testimonials, ...testimonials].map((testimonial, index) => (
<TestimonialCard key={index} {...testimonial} />
))}
</motion.div>
</div>
</div>
</div>
</section>
)
Expand All @@ -96,34 +91,34 @@ function TestimonialCard({
avatar,
content,
platform,
}: {
name: string
role: string
avatar: string
content: string
platform: string
}) {
icon: Icon,
color
}: Testimonial) {
return (
<div className="w-80 shrink-0 rounded-2xl border border-border bg-card p-6 backdrop-blur-sm transition-colors duration-300">
<div className="mb-4 flex items-start justify-between">
<div className="w-[280px] md:w-[320px] shrink-0 rounded-xl border border-gray-200 dark:border-white/10 bg-white dark:bg-[#0f0f0f] p-5 shadow-sm hover:shadow-md transition-all duration-300 hover:border-gray-300 dark:hover:border-white/20 hover:-translate-y-1">
<div className="mb-3 flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-[#2C7FFF] to-sky-400 text-sm font-bold text-white shadow-lg shadow-[#2C7FFF]/20">
<div className="flex h-9 w-9 items-center justify-center rounded-full bg-gray-100 dark:bg-white/10 border border-gray-200 dark:border-white/5 text-xs font-bold text-gray-700 dark:text-gray-200">
{avatar}
</div>
<div>
<div className="font-semibold text-foreground transition-colors duration-300">{name}</div>
<div className="text-xs text-muted-foreground transition-colors duration-300">{role}</div>
<div className="font-bold text-gray-900 dark:text-white text-sm leading-none mb-1">{name}</div>
<div className="text-[10px] uppercase tracking-wide text-gray-500 font-medium">{role}</div>
</div>
</div>

<div className="flex h-6 w-6 items-center justify-center rounded-full bg-muted transition-colors duration-300">
<MessageCircle className="h-3 w-3 text-muted-foreground" />
<div className={`flex h-7 w-7 items-center justify-center rounded-full ${color}`}>
<Icon size={12} />
</div>
</div>

<p className="text-sm leading-relaxed text-muted-foreground transition-colors duration-300">{content}</p>
<p className="text-sm leading-relaxed text-gray-600 dark:text-gray-300 font-normal">
&quot;{content}&quot;
</p>

<div className="mt-4 text-xs text-muted-foreground/60 transition-colors duration-300">via {platform}</div>
<div className="mt-3 text-[10px] text-gray-400 dark:text-gray-600 font-mono flex items-center gap-1">
via {platform} <ExternalLink size={8} />
</div>
</div>
)
}
}
Loading