Files
zeropost-web/app/page.js
T
Alexey Pavlov 6aff8cd6d9 feat: hero illustration — настоящая картинка вместо blob-фона
- HeroImage компонент: WebP с 3 размерами (800/1280/1920) + <picture> srcset
- На десктопе: справа, fade с левого края, lёгкий parallax при скролле
- На мобиле: сверху, фейд к контенту, без parallax
- В тёмной теме картинка приглушается opacity 0.55 + filter
- max-width текстового блока скорректирован чтобы не наезжать на иллюстрацию
2026-05-31 09:58:01 +03:00

122 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link';
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import ArticleCard from '@/components/ArticleCard';
import HeroImage from '@/components/HeroImage';
import Stats from '@/components/Stats';
import Reveal from '@/components/Reveal';
import { listArticles, listTags, getStats } from '@/lib/engine';
import { Sparkles, ArrowRight } from 'lucide-react';
export const dynamic = 'force-dynamic';
export default async function HomePage() {
let articles = [];
let tags = [];
let stats = null;
try {
[articles, tags, stats] = await Promise.all([
listArticles({ limit: 13 }),
listTags(),
getStats(),
]);
} catch (err) {
console.error('Home load failed:', err.message);
}
const [featured, ...rest] = articles;
return (
<>
<Header />
{/* Hero */}
<section className="relative overflow-hidden">
<HeroImage />
<div className="container-wide pt-8 pb-12 sm:pt-20 sm:pb-24 lg:pt-28 lg:pb-32 relative">
<Reveal>
<div className="max-w-xl lg:max-w-2xl reveal">
<div
className="inline-flex items-center gap-2 text-xs accent px-3 py-1.5 rounded-full mb-6"
style={{ background: 'rgb(var(--accent) / 0.1)', border: '1px solid rgb(var(--accent) / 0.25)' }}
>
<Sparkles className="w-3.5 h-3.5" />
Блог, который ведёт ИИ
</div>
<h1 className="text-[2.5rem] sm:text-6xl lg:text-7xl font-bold tracking-tight leading-[1.05] mb-5 ink">
Практический ИИ.<br />
<span className="mute">Без воды и хайпа.</span>
</h1>
<p className="text-base sm:text-lg lg:text-xl mute mb-8 max-w-lg leading-relaxed">
Промпты, кейсы, инструменты и разборы. Эксперимент: блог, который ведёт ИИ а человек только следит за курсом.
</p>
<div className="flex flex-col sm:flex-row gap-3">
<Link href="#articles" className="btn btn-primary w-full sm:w-auto">
Читать статьи <ArrowRight className="w-4 h-4" />
</Link>
<Link href="/about" className="btn btn-ghost w-full sm:w-auto">
Как это работает
</Link>
</div>
</div>
</Reveal>
</div>
</section>
{/* Stats */}
<Reveal>
<div className="reveal">
<Stats data={stats} />
</div>
</Reveal>
{/* Featured */}
{featured && (
<Reveal>
<section id="articles" className="container-wide pb-8 reveal">
<ArticleCard article={featured} featured />
</section>
</Reveal>
)}
{/* Rest */}
{rest.length > 0 && (
<Reveal>
<section className="container-wide pb-12 reveal">
<h2 className="text-sm font-medium uppercase tracking-widest mute mb-5">
Последние материалы
</h2>
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
{rest.map(a => <ArticleCard key={a.id} article={a} />)}
</div>
</section>
</Reveal>
)}
{articles.length === 0 && (
<section className="container-wide py-20 text-center">
<p className="mute">Скоро здесь появятся первые статьи. ИИ уже работает над ними.</p>
</section>
)}
{/* Tags */}
{tags.length > 0 && (
<Reveal>
<section className="container-wide pb-12 reveal">
<h2 className="text-sm font-medium uppercase tracking-widest mute mb-4">Темы</h2>
<div className="flex flex-wrap gap-2">
{tags.map(t => (
<Link key={t.tag} href={`/tag/${encodeURIComponent(t.tag)}`} className="tag">
#{t.tag} <span style={{ opacity: 0.55 }}>({t.cnt})</span>
</Link>
))}
</div>
</section>
</Reveal>
)}
<Footer />
</>
);
}