feat(home): Hero 浅色化(暗黑模式 token 友好)+ 视觉基线更新

- 撤销 P4 hero 深色全出血(bg-ink),回归浅色方案:bg-bg-primary + text-ink
- 全部使用 CSS 变量 token(bg-bg-primary/text-ink/text-text-secondary/text-brand),
  暗黑模式可直接覆盖 token 实现适配,无需逐元素反色
- logo-white.svg → logo.svg(浅色画布深色版)、点阵纹理淡化(0.06→0.03)
- 测试同步:hero logo 断言 /logo-white.svg → /logo.svg
- 视觉基线更新 3 张:home-fullpage / theme-light-main / theme-dark-main
- 附带修复:hero 背景原误用无效类 bg-primary(Tailwind 嵌套色名应为
  bg-bg-primary),全项目排查无同类误用
This commit is contained in:
2026-08-31 17:47:38 +08:00
parent 0ed1331d1f
commit fea5da7917
5 changed files with 18 additions and 18 deletions
@@ -88,13 +88,13 @@ jest.mock('lucide-react', () => {
};
});
describe('HomeContentV15 - 色 Hero对标 Accenture 纯黑 Hero', () => {
it('renders the dark canvas hero with a white logo variant', () => {
describe('HomeContentV15 - 色 Hero暗黑模式 token 友好', () => {
it('renders the light canvas hero with a brand logo variant', () => {
render(<HomeContentV15 />);
expect(screen.getByTestId('hero-section')).toBeInTheDocument();
const heroLogo = screen.getByTestId('mock-next-image');
expect(heroLogo).toHaveAttribute('src', '/logo-white.svg');
expect(heroLogo).toHaveAttribute('src', '/logo.svg');
});
it('keeps a single primary CTA and demotes the secondary action to a text link', () => {
+15 -15
View File
@@ -158,13 +158,13 @@ function HeroSection({ heroData, stats }: {
return (
<section
ref={containerRef}
className="relative min-h-[90vh] flex items-center overflow-hidden bg-ink"
className="relative min-h-[90vh] flex items-center overflow-hidden bg-bg-primary"
data-testid="hero-section"
>
{/* 背景纹理:极淡的品牌色点阵(色画布上可见) */}
<div className="absolute inset-0 pointer-events-none opacity-[0.06]"
{/* 背景纹理:极淡的品牌色点阵(色画布上可见) */}
<div className="absolute inset-0 pointer-events-none opacity-[0.03]"
style={{
backgroundImage: `radial-gradient(circle at 25% 25%, rgba(196,30,58,0.6) 1px, transparent 1px)`,
backgroundImage: `radial-gradient(circle at 25% 25%, rgba(196,30,58,0.4) 1px, transparent 1px)`,
backgroundSize: '48px 48px',
}}
/>
@@ -172,7 +172,7 @@ function HeroSection({ heroData, stats }: {
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-16 sm:py-20 md:py-24 lg:py-24">
<div className="grid items-center gap-16 lg:grid-cols-[1.05fr_0.95fr]">
<div className="max-w-4xl">
{/* 品牌标识:色画布使用色版 Logo色版见 header/footer */}
{/* 品牌标识:色画布使用色版 Logo色版见 footer 深色区块 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@@ -180,7 +180,7 @@ function HeroSection({ heroData, stats }: {
className="mb-6"
>
<Image
src="/logo-white.svg"
src="/logo.svg"
alt={`${COMPANY_INFO.name} Logo`}
width={224}
height={56}
@@ -189,7 +189,7 @@ function HeroSection({ heroData, stats }: {
/>
</motion.div>
{/* 品牌口号 Badge色画布上的品牌红电压 */}
{/* 品牌口号 Badge色画布上的品牌红信号 */}
{slogan && (
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -197,18 +197,18 @@ function HeroSection({ heroData, stats }: {
transition={{ duration: 0.35, delay: 0.03, ease: EASE_OUT }}
className="mb-6"
>
<span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-brand/15 text-brand-light text-sm font-medium border border-brand/30">
<span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-brand/5 text-brand text-sm font-medium border border-brand/15">
{slogan}
</span>
</motion.div>
)}
{/* 主标题:大胆、直接(色画布上的色宣言) */}
{/* 主标题:大胆、直接(色画布上的色宣言) */}
<motion.h1
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45, delay: 0.06, ease: EASE_OUT }}
className="text-4xl sm:text-5xl md:text-6xl lg:text-6xl 2xl:text-7xl font-black text-white leading-[1.05] tracking-tight mb-8 sm:mb-10"
className="text-4xl sm:text-5xl md:text-6xl lg:text-6xl 2xl:text-7xl font-black text-ink leading-[1.05] tracking-tight mb-8 sm:mb-10"
>
{heading}
</motion.h1>
@@ -224,10 +224,10 @@ function HeroSection({ heroData, stats }: {
>
{displayStats.map((stat, i) => (
<div key={i} className="flex items-baseline gap-2">
<span className={cn('text-3xl sm:text-4xl md:text-5xl font-black tabular-nums leading-none', i === 0 ? 'text-brand-light' : 'text-white')}>
<span className={cn('text-3xl sm:text-4xl md:text-5xl font-black tabular-nums leading-none', i === 0 ? 'text-brand' : 'text-ink')}>
{stat.value}
</span>
<span className="text-sm text-white/60 whitespace-nowrap">
<span className="text-sm text-text-secondary whitespace-nowrap">
{stat.label}
</span>
</div>
@@ -241,7 +241,7 @@ function HeroSection({ heroData, stats }: {
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35, delay: 0.24, ease: EASE_OUT }}
className="text-lg sm:text-xl text-white/70 max-w-2xl leading-relaxed mb-10"
className="text-lg sm:text-xl text-text-secondary max-w-2xl leading-relaxed mb-10"
>
{subheading}
</motion.p>
@@ -259,7 +259,7 @@ function HeroSection({ heroData, stats }: {
<a
href={secondaryCtaHref}
data-testid="hero-secondary-link"
className="group inline-flex items-center gap-2 text-sm font-medium text-white/70 hover:text-white transition-colors duration-200 py-4"
className="group inline-flex items-center gap-2 text-sm font-medium text-text-secondary hover:text-ink transition-colors duration-200 py-4"
>
{secondaryCtaLabel}
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
@@ -268,7 +268,7 @@ function HeroSection({ heroData, stats }: {
</motion.div>
</div>
{/* 右侧:产品视觉(SVG 数据看板示意,浅色卡片浮于色画布) */}
{/* 右侧:产品视觉(SVG 数据看板示意,浅色卡片浮于色画布) */}
<motion.div
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}