Skip to content

Commit f383cb2

Browse files
committed
feat: add HeroSection and PixelBlast components
1 parent f2cba0a commit f383cb2

File tree

7 files changed

+843
-4
lines changed

7 files changed

+843
-4
lines changed

app/[lang]/(home)/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Features,
66
Footer,
77
Hero,
8+
HeroSection,
89
Navbar,
910
} from "@/components/home";
1011
import { getAppData } from "@/lib/data";
@@ -44,8 +45,12 @@ export default async function HomePage() {
4445
<div className="absolute bottom-0 left-0 w-[600px] h-[400px] bg-primary-900/20 rounded-full blur-3xl opacity-20 animate-pulse-slow" />
4546
</div>
4647

47-
<Navbar version={appData.version} stars={appData.stars} />
48-
<Hero version={appData.version} releaseAssets={appData.releaseAssets} />
48+
{/* PixelBlast covers Navbar + Hero together */}
49+
<HeroSection>
50+
<Navbar version={appData.version} stars={appData.stars} />
51+
<Hero version={appData.version} releaseAssets={appData.releaseAssets} />
52+
</HeroSection>
53+
4954
<Features />
5055
<Extensions />
5156
<Downloads

components/home/Features.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function Features() {
4848
];
4949

5050
return (
51-
<section id="features" className="relative py-20 overflow-hidden">
51+
<section id="features" className="relative pt-0 pb-20 overflow-hidden">
5252
{/* Background Decoration */}
5353
<div className="absolute inset-0 bg-gradient-grid bg-[length:50px_50px] opacity-5 dark:opacity-10" />
5454

components/home/HeroSection.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
import { useTheme } from "next-themes";
4+
import { useEffect, useState } from "react";
5+
import { PixelBlast } from "./PixelBlast";
6+
7+
export function HeroSection({ children }: { children: React.ReactNode }) {
8+
const { resolvedTheme } = useTheme();
9+
const [mounted, setMounted] = useState(false);
10+
const [isMobile, setIsMobile] = useState(false);
11+
12+
useEffect(() => {
13+
setMounted(true);
14+
const check = () => setIsMobile(window.innerWidth < 768);
15+
check();
16+
window.addEventListener("resize", check, { passive: true });
17+
return () => window.removeEventListener("resize", check);
18+
}, []);
19+
20+
const pixelColor = resolvedTheme === "dark" ? "#79C476" : "#5fa05c";
21+
const pixelSize = isMobile ? 5 : 3;
22+
const patternDensity = isMobile ? 0.72 : 0.88;
23+
const edgeFade = isMobile ? 0.42 : 0.28;
24+
25+
return (
26+
// pb-24 absorbs the visual gap between Hero and Features so the
27+
// PixelBlast canvas covers it — no bleed needed, no z-index fights.
28+
<div className="relative pb-44">
29+
{/* PixelBlast: strictly inside this container. Only rendered after
30+
hydration to prevent the SSR white-flash. */}
31+
{mounted && (
32+
<PixelBlast
33+
color={pixelColor}
34+
variant="circle"
35+
pixelSize={pixelSize}
36+
patternScale={2.5}
37+
patternDensity={patternDensity}
38+
speed={0.4}
39+
edgeFade={edgeFade}
40+
enableRipples={true}
41+
rippleIntensityScale={1.1}
42+
rippleSpeed={0.3}
43+
rippleThickness={0.12}
44+
transparent={true}
45+
/>
46+
)}
47+
48+
{/* Hero content */}
49+
<div className="relative z-10">{children}</div>
50+
51+
{/* Bottom fade — purely inside the section, never touches Features.
52+
Tailwind dark: avoids any JS-driven hydration colour flash. */}
53+
<div className="absolute bottom-0 left-0 right-0 h-44 pointer-events-none z-20 bg-gradient-to-b from-transparent to-white dark:to-gray-950" />
54+
</div>
55+
);
56+
}

0 commit comments

Comments
 (0)