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:
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -88,13 +88,13 @@ jest.mock('lucide-react', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('HomeContentV15 - 深色 Hero(对标 Accenture 纯黑 Hero)', () => {
|
describe('HomeContentV15 - 浅色 Hero(暗黑模式 token 友好)', () => {
|
||||||
it('renders the dark canvas hero with a white logo variant', () => {
|
it('renders the light canvas hero with a brand logo variant', () => {
|
||||||
render(<HomeContentV15 />);
|
render(<HomeContentV15 />);
|
||||||
|
|
||||||
expect(screen.getByTestId('hero-section')).toBeInTheDocument();
|
expect(screen.getByTestId('hero-section')).toBeInTheDocument();
|
||||||
const heroLogo = screen.getByTestId('mock-next-image');
|
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', () => {
|
it('keeps a single primary CTA and demotes the secondary action to a text link', () => {
|
||||||
|
|||||||
@@ -158,13 +158,13 @@ function HeroSection({ heroData, stats }: {
|
|||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
ref={containerRef}
|
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"
|
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={{
|
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',
|
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="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="grid items-center gap-16 lg:grid-cols-[1.05fr_0.95fr]">
|
||||||
<div className="max-w-4xl">
|
<div className="max-w-4xl">
|
||||||
{/* 品牌标识:深色画布使用白色版 Logo(浅色版见 header/footer) */}
|
{/* 品牌标识:浅色画布使用深色版 Logo(深色版见 footer 深色区块) */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
@@ -180,7 +180,7 @@ function HeroSection({ heroData, stats }: {
|
|||||||
className="mb-6"
|
className="mb-6"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/logo-white.svg"
|
src="/logo.svg"
|
||||||
alt={`${COMPANY_INFO.name} Logo`}
|
alt={`${COMPANY_INFO.name} Logo`}
|
||||||
width={224}
|
width={224}
|
||||||
height={56}
|
height={56}
|
||||||
@@ -189,7 +189,7 @@ function HeroSection({ heroData, stats }: {
|
|||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 品牌口号 Badge:深色画布上的品牌红电压 */}
|
{/* 品牌口号 Badge:浅色画布上的品牌红信号 */}
|
||||||
{slogan && (
|
{slogan && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -197,18 +197,18 @@ function HeroSection({ heroData, stats }: {
|
|||||||
transition={{ duration: 0.35, delay: 0.03, ease: EASE_OUT }}
|
transition={{ duration: 0.35, delay: 0.03, ease: EASE_OUT }}
|
||||||
className="mb-6"
|
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}
|
{slogan}
|
||||||
</span>
|
</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 主标题:大胆、直接(深色画布上的白色宣言) */}
|
{/* 主标题:大胆、直接(浅色画布上的墨色宣言) */}
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ opacity: 0, y: 40 }}
|
initial={{ opacity: 0, y: 40 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.45, delay: 0.06, ease: EASE_OUT }}
|
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}
|
{heading}
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
@@ -224,10 +224,10 @@ function HeroSection({ heroData, stats }: {
|
|||||||
>
|
>
|
||||||
{displayStats.map((stat, i) => (
|
{displayStats.map((stat, i) => (
|
||||||
<div key={i} className="flex items-baseline gap-2">
|
<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}
|
{stat.value}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm text-white/60 whitespace-nowrap">
|
<span className="text-sm text-text-secondary whitespace-nowrap">
|
||||||
{stat.label}
|
{stat.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -241,7 +241,7 @@ function HeroSection({ heroData, stats }: {
|
|||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.35, delay: 0.24, ease: EASE_OUT }}
|
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}
|
{subheading}
|
||||||
</motion.p>
|
</motion.p>
|
||||||
@@ -259,7 +259,7 @@ function HeroSection({ heroData, stats }: {
|
|||||||
<a
|
<a
|
||||||
href={secondaryCtaHref}
|
href={secondaryCtaHref}
|
||||||
data-testid="hero-secondary-link"
|
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}
|
{secondaryCtaLabel}
|
||||||
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
|
<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>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 右侧:产品视觉(SVG 数据看板示意,浅色卡片浮于深色画布) */}
|
{/* 右侧:产品视觉(SVG 数据看板示意,浅色卡片浮于浅色画布) */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 40 }}
|
initial={{ opacity: 0, y: 40 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
|||||||
Reference in New Issue
Block a user