This commit is contained in:
z.ai committed 2026-10-08 11:09:37 +08:00
1 parent c1541b80df
commit b4c7908029
97 files changed
+12403 -154

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
import { Link } from "@/i18n/navigation";
import { ArrowRight, Sparkles } from "lucide-react";
import { Container } from "@/components/ui/layout";
import { buttonClassName } from "@/components/ui/button-variants";
/**
* 首页 Hero 区。
*
* 对应 DESIGN.md 的 hero-band-sky:大气渐变(天空蓝 → 柔奶油)作为背景,
* 居中标题、副标题与按钮行。品牌薄荷绿仅用于单个强调 CTA。
*
* 这里用 CSS 渐变 + 径向光晕替代插画资源,避免引入额外图片依赖,
* 同时保持规范要求的「氛围感渐变」基调。
*/
export interface HomeHeroProps {
eyebrow: string;
title: string;
subtitle: string;
primaryLabel: string;
secondaryLabel: string;
stats: {
posts: number;
tags: number;
since: string;
postsLabel: string;
tagsLabel: string;
sinceLabel: string;
};
}
export function HomeHero({
eyebrow,
title,
subtitle,
primaryLabel,
secondaryLabel,
stats,
}: HomeHeroProps) {
return (
<section
aria-labelledby="hero-heading"
className="relative overflow-hidden border-b border-hairline-soft"
>
{/* 氛围渐变背景(装饰性,对辅助技术隐藏) */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10 bg-gradient-to-b from-hero-sky-from via-[#d9dbe0] to-hero-sky-to"
/>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10 opacity-70 [background:radial-gradient(60%_50%_at_50%_0%,rgba(255,255,255,0.85),transparent_70%)]"
/>
<Container className="flex flex-col items-center py-section text-center sm:py-section-lg lg:py-hero">
{/* 大写微标签 */}
<p className="inline-flex items-center gap-xs rounded-full border border-white/60 bg-white/70 px-md py-[6px] text-micro-uppercase font-semibold uppercase tracking-[0.5px] text-steel backdrop-blur-sm">
<Sparkles className="size-3.5 text-brand-green-deep" aria-hidden="true" />
{eyebrow}
</p>
<h1
id="hero-heading"
className="mt-lg w-full max-w-[880px] text-heading-2 font-semibold text-ink text-balance-pretty sm:text-heading-1 lg:text-hero-display"
>
{title}
</h1>
<p className="mt-lg w-full max-w-[640px] text-body-md text-slate text-balance-pretty sm:text-subtitle">
{subtitle}
</p>
{/* 按钮行:品牌绿强调 CTA + 描边次级按钮 */}
<div className="mt-xxl flex flex-col items-center gap-sm sm:flex-row">
<Link
href="/#posts"
className={buttonClassName({ variant: "accent", size: "lg" })}
>
{primaryLabel}
<ArrowRight className="size-4" aria-hidden="true" />
</Link>
<Link
href="/about"
className={buttonClassName({ variant: "secondary", size: "lg" })}
>
{secondaryLabel}
</Link>
</div>
{/* 站点统计:窄屏下压缩间距,避免三个卡片把布局撑出横向滚动 */}
<dl className="mt-section grid w-full min-w-0 max-w-[560px] grid-cols-3 gap-xs sm:mt-hero sm:gap-md">
<StatItem value={String(stats.posts)} label={stats.postsLabel} />
<StatItem value={String(stats.tags)} label={stats.tagsLabel} />
<StatItem value={stats.since} label={stats.sinceLabel} />
</dl>
</Container>
</section>
);
}
function StatItem({ value, label }: { value: string; label: string }) {
return (
<div className="flex min-w-0 flex-col items-center gap-[2px] rounded-lg border border-white/60 bg-white/55 px-xs py-sm backdrop-blur-sm sm:px-md">
<dt className="sr-only">{label}</dt>
<dd className="font-mono text-heading-5 font-semibold text-ink sm:text-heading-4">
{value}
</dd>
<dd className="text-caption text-steel">{label}</dd>
</div>
);
}