Skip to content

Commit ca57ffd

Browse files
feat(home): 社区版块改编辑体前哨波段 + 滚动揭示/反色 wipe 动效
反馈:三个并排一样的格子没设计感,想要下滑时的交互变换。改成三条全宽波段 (CommunityNetwork client 组件,motion/react): - 站名超大衬线体做编辑体信号,功能呼号(Live Ops / Game Node / AI Desk) 取代无意义的 01/02/03 序号;monitor 带呼吸红点(它本就是实时状态台) - 滚动进入视口错峰升起(whileInView + stagger),hover 墨色从左漫满整条 并反色(复用站点反色 idiom 做成 wipe) - mounted 门控 + useReducedMotion:SSR/无 JS/reduced-motion 直接可见, 不把内容藏死;呼吸点与 wipe 都 motion-reduce 降级 本地:typecheck / lint(两文件干净) / build 全过;首页仍 ● /[locale] SSG (client 组件只 hydrate 不破坏静态);Playwright 实测静止 + hover 反色两态。
1 parent 7d808a1 commit ca57ffd

2 files changed

Lines changed: 119 additions & 56 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
import Image from "next/image";
5+
import { ArrowUpRight } from "lucide-react";
6+
import { motion, useReducedMotion } from "motion/react";
7+
import { cn } from "@/lib/utils";
8+
9+
type Outpost = {
10+
key: string;
11+
callsign: string;
12+
title: string;
13+
desc: string;
14+
href: string;
15+
live?: boolean;
16+
};
17+
18+
/**
19+
* 首页"社区网络"版块:三个生态前哨(monitor / mc / openInvest)做成全宽波段,
20+
* 滚动进入视口时错峰升起,hover 时墨色从左漫过整条(反色 wipe)。
21+
* SSR/无 JS/reduced-motion 时直接渲染可见状态(mounted 门控),不把内容藏死。
22+
*/
23+
export function CommunityNetwork({
24+
label,
25+
outposts,
26+
}: {
27+
label: string;
28+
outposts: Outpost[];
29+
}) {
30+
const reduce = useReducedMotion();
31+
const [mounted, setMounted] = useState(false);
32+
useEffect(() => setMounted(true), []);
33+
const animate = mounted && !reduce;
34+
35+
return (
36+
<div className="mt-16 border-t-4 border-[var(--foreground)] transition-colors duration-300">
37+
<div className="flex items-center gap-2 py-4 font-mono text-xs uppercase tracking-widest border-b border-[var(--foreground)] text-[var(--foreground)]">
38+
<Image
39+
src="/friends/ailumao.png"
40+
alt=""
41+
width={22}
42+
height={22}
43+
className="[image-rendering:pixelated]"
44+
/>
45+
{label}
46+
</div>
47+
48+
<ul>
49+
{outposts.map((o, i) => (
50+
<motion.li
51+
key={o.key}
52+
initial={animate ? { opacity: 0, y: 26 } : false}
53+
whileInView={animate ? { opacity: 1, y: 0 } : undefined}
54+
viewport={{ once: true, amount: 0.4 }}
55+
transition={{
56+
duration: 0.55,
57+
delay: i * 0.12,
58+
ease: [0.22, 1, 0.36, 1],
59+
}}
60+
className={cn(
61+
"group relative overflow-hidden border-[var(--foreground)]",
62+
i < outposts.length - 1 && "border-b",
63+
)}
64+
>
65+
{/* 墨色 wipe 层:hover 时从左漫满整条 */}
66+
<span
67+
aria-hidden
68+
className="pointer-events-none absolute inset-0 origin-left scale-x-0 bg-[var(--foreground)] transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:scale-x-100 motion-reduce:transition-none"
69+
/>
70+
71+
<a
72+
href={o.href}
73+
target="_blank"
74+
rel="noopener noreferrer"
75+
className="relative flex flex-col gap-3 py-9 px-6 transition-colors duration-500 group-hover:text-[var(--background)] md:flex-row md:items-center md:gap-8"
76+
data-umami-event="community_click"
77+
data-umami-event-target={o.key}
78+
data-umami-event-location="hero"
79+
>
80+
{/* 呼号 */}
81+
<span className="flex w-28 shrink-0 items-center gap-2 font-mono text-[11px] uppercase tracking-[0.2em] text-neutral-400 group-hover:text-[var(--background)]">
82+
{o.live && (
83+
<span className="relative flex h-1.5 w-1.5">
84+
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#CC0000] opacity-75 motion-reduce:hidden" />
85+
<span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-[#CC0000]" />
86+
</span>
87+
)}
88+
{o.callsign}
89+
</span>
90+
91+
{/* 站名(超大衬线,编辑体信号) */}
92+
<span className="font-serif text-3xl font-bold uppercase leading-none tracking-tight text-[var(--foreground)] group-hover:text-[var(--background)] md:text-4xl">
93+
{o.title}
94+
</span>
95+
96+
{/* 描述 */}
97+
<span className="font-body text-sm leading-relaxed text-neutral-600 group-hover:text-[var(--background)] dark:text-neutral-300 md:ml-auto md:max-w-xs md:text-right">
98+
{o.desc}
99+
</span>
100+
101+
<ArrowUpRight className="hidden h-6 w-6 shrink-0 text-neutral-400 transition-transform duration-300 group-hover:translate-x-1 group-hover:-translate-y-1 group-hover:text-[var(--background)] md:block" />
102+
</a>
103+
</motion.li>
104+
))}
105+
</ul>
106+
</div>
107+
);
108+
}

app/components/Hero.tsx

Lines changed: 11 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Link from "next/link";
22
import { Suspense } from "react";
3-
import { ArrowRight } from "lucide-react";
43
import { getTranslations } from "next-intl/server";
54
import ZoteroFeedLazy from "@/app/components/ZoteroFeedLazy";
5+
import { CommunityNetwork } from "@/app/components/CommunityNetwork";
66
import { Contribute } from "@/app/components/Contribute";
77
import { ShareLink } from "@/app/components/ShareLink";
88
import Image from "next/image";
@@ -42,26 +42,25 @@ export async function Hero() {
4242
},
4343
];
4444

45-
const communitySites: {
46-
key: string;
47-
title: string;
48-
desc: string;
49-
href: string;
50-
}[] = [
45+
const communitySites = [
5146
{
5247
key: "monitor",
48+
callsign: "Live Ops",
5349
title: t("community.monitor.title"),
5450
desc: t("community.monitor.desc"),
5551
href: "https://monitor.involutionhell.com/",
52+
live: true,
5653
},
5754
{
5855
key: "mc",
56+
callsign: "Game Node",
5957
title: t("community.mc.title"),
6058
desc: t("community.mc.desc"),
6159
href: "https://mc.involutionhell.com/",
6260
},
6361
{
6462
key: "invest",
63+
callsign: "AI Desk",
6564
title: t("community.invest.title"),
6665
desc: t("community.invest.desc"),
6766
href: "https://openinvest.involutionhell.com/",
@@ -147,55 +146,11 @@ export async function Hero() {
147146
</div>
148147
</div>
149148

150-
{/* Community Network - IH 生态站点 */}
151-
<div className="mt-16 border-t-4 border-[var(--foreground)] transition-colors duration-300">
152-
<div className="flex items-center gap-2 py-4 font-mono text-xs uppercase tracking-widest border-b border-[var(--foreground)] text-[var(--foreground)]">
153-
<Image
154-
src="/friends/ailumao.png"
155-
alt=""
156-
width={22}
157-
height={22}
158-
className="[image-rendering:pixelated]"
159-
/>
160-
{t("community.label")}
161-
</div>
162-
<ul className="grid grid-cols-1 md:grid-cols-3">
163-
{communitySites.map((s, idx) => (
164-
<li
165-
key={s.title}
166-
className={cn(
167-
"group border-b border-[var(--foreground)] md:border-r last:border-r-0",
168-
idx === 2 && "md:border-r-0",
169-
)}
170-
>
171-
<a
172-
href={s.href}
173-
target="_blank"
174-
rel="noopener noreferrer"
175-
className="flex items-baseline gap-4 p-6 hover:bg-neutral-100 dark:hover:bg-neutral-900 transition-colors hard-shadow-hover"
176-
data-umami-event="community_click"
177-
data-umami-event-target={s.key}
178-
data-umami-event-location="hero"
179-
>
180-
<span className="font-mono text-[10px] text-neutral-400 pt-1 shrink-0">
181-
NET-0{idx + 1}
182-
</span>
183-
<span className="min-w-0">
184-
<span className="flex items-center gap-2 mb-1.5">
185-
<span className="text-xl font-serif font-bold uppercase text-[var(--foreground)]">
186-
{s.title}
187-
</span>
188-
<ArrowRight className="h-4 w-4 shrink-0 text-neutral-400 group-hover:translate-x-1 group-hover:text-[#CC0000] transition-all" />
189-
</span>
190-
<span className="block text-sm font-body text-neutral-600 dark:text-neutral-300 leading-relaxed">
191-
{s.desc}
192-
</span>
193-
</span>
194-
</a>
195-
</li>
196-
))}
197-
</ul>
198-
</div>
149+
{/* Community Network - IH 生态站点(滚动揭示 + 反色 wipe,见 CommunityNetwork) */}
150+
<CommunityNetwork
151+
label={t("community.label")}
152+
outposts={communitySites}
153+
/>
199154

200155
{/* Top-level directories - Grid with shared borders */}
201156
<div className="mt-16 border-t-4 border-[var(--foreground)] transition-colors duration-300">

0 commit comments

Comments
 (0)