feat: оживление сайта — обложки, hero-фон, статистика, анимации

- ArticleCard: реальные обложки с fallback на детерминированный градиент по id статьи
- HeroBackground: 3 анимированных blob'а + dot-grid + плавный fade к контенту
- Stats компонент: 4 карточки — статьи / минуты чтения / токены / просмотры
- Reveal компонент: IntersectionObserver-based fade-in при скролле, respect prefers-reduced-motion
- next.config: rewrites /uploads/* → engine, чтобы картинки работали с относительными путями
- На странице статьи — обложка над контентом
This commit is contained in:
Alexey Pavlov
2026-05-31 09:17:08 +03:00
parent a16bf812e4
commit b1c09aa53f
8 changed files with 352 additions and 84 deletions
+70
View File
@@ -0,0 +1,70 @@
'use client';
export default function HeroBackground() {
return (
<div className="absolute inset-0 -z-10 overflow-hidden pointer-events-none">
{/* Mesh gradient — несколько размытых blob'ов с медленной анимацией */}
<div className="hero-blob hero-blob-1" />
<div className="hero-blob hero-blob-2" />
<div className="hero-blob hero-blob-3" />
{/* Сетка точек поверх — тонкий фоновый паттерн */}
<div className="hero-grid" />
{/* Виньетка к низу — плавный переход к контенту */}
<div className="hero-fade" />
<style jsx>{`
.hero-blob {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.5;
}
.hero-blob-1 {
width: 520px; height: 520px;
background: radial-gradient(circle, rgba(16,185,129,0.6) 0%, transparent 70%);
top: -120px; right: -80px;
animation: blob1 22s ease-in-out infinite alternate;
}
.hero-blob-2 {
width: 420px; height: 420px;
background: radial-gradient(circle, rgba(20,184,166,0.5) 0%, transparent 70%);
top: 50px; left: -60px;
animation: blob2 28s ease-in-out infinite alternate;
}
.hero-blob-3 {
width: 360px; height: 360px;
background: radial-gradient(circle, rgba(132,204,22,0.35) 0%, transparent 70%);
top: 200px; left: 40%;
animation: blob3 25s ease-in-out infinite alternate;
}
@keyframes blob1 {
0% { transform: translate(0, 0) scale(1); }
100% { transform: translate(-60px, 80px) scale(1.1); }
}
@keyframes blob2 {
0% { transform: translate(0, 0) scale(1); }
100% { transform: translate(80px, -40px) scale(0.95); }
}
@keyframes blob3 {
0% { transform: translate(0, 0) scale(1); }
100% { transform: translate(-40px, -60px) scale(1.08); }
}
.hero-grid {
position: absolute;
inset: 0;
background-image: radial-gradient(circle, rgb(var(--ink) / 0.06) 1px, transparent 1px);
background-size: 24px 24px;
mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%);
}
.hero-fade {
position: absolute;
left: 0; right: 0; bottom: 0;
height: 180px;
background: linear-gradient(to bottom, transparent, rgb(var(--bg)));
}
`}</style>
</div>
);
}