feat(home): hero 背景层 — 点阵 token 化 + 光标聚光(Skim 层)
参考 Accenture 分层背景(P1/P2 批次,用户决策先上点阵+聚光、粒子暂缓): L1 静态点阵 token 化: - 48px / hardcoded rgba(196,30,58,0.4) / opacity 0.03(实测 1.05:1,几乎不可见) - → 24px 网格 + 颜色/透明度走 CSS 变量(浅色 brand@0.12=1.22:1;深底中性浅灰 text-muted@0.08=1.20:1) - 暗黑模式零 JS 自动反色(原硬编码在深底不反色) L2 光标聚光(Skim 层): - mask + 2 个 CSS 变量(--spot-x/--spot-y),无 canvas、无逐帧 JS - 直接写 DOM 变量,零 re-render;prefers-reduced-motion 下不渲染 - 复用既有资产思路(hero-section-v2 光标跟随 / design-system inkGlow) 验证(evidence-first): - type-check 0 errors / lint 0 errors(127 warnings 均既有) - jest 13 passed(hero 声明先行断言未被破坏) - 视觉回归 22 passed(点阵 1px 改动在全页截图下低于像素阈值,快照零 diff; 已 curl 确认 DOM 实为 hero-dot-grid+hero-dot-spot,硬编码已清除) 粒子(L3)按决策暂缓,待 L2 聚光效果评估
This commit is contained in:
@@ -12,6 +12,7 @@ import { EASE_OUT } from '@/components/ui/page-decoration';
|
||||
import { CTAButton } from '@/components/ui/cta-button';
|
||||
import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants';
|
||||
import { useSiteConfig } from '@/lib/site-config';
|
||||
import { useReducedMotion } from '@/hooks/use-reduced-motion';
|
||||
import { type NewsItem } from '@/lib/constants/news';
|
||||
import { NEWS } from '@/lib/constants/news';
|
||||
|
||||
@@ -145,7 +146,9 @@ function HeroSection({ heroData }: {
|
||||
heroData?: Record<string, any> | null;
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const spotRef = useRef<HTMLDivElement>(null);
|
||||
const siteConfig = useSiteConfig();
|
||||
const reduceMotion = useReducedMotion();
|
||||
|
||||
const heading = heroData?.heading || heroData?.headingBottom || '让每一家企业都拥有数据驱动的决策能力';
|
||||
const subheading = heroData?.subheading || heroData?.description || '';
|
||||
@@ -160,14 +163,20 @@ function HeroSection({ heroData }: {
|
||||
ref={containerRef}
|
||||
className="relative min-h-[90vh] flex items-center overflow-hidden bg-bg-primary"
|
||||
data-testid="hero-section"
|
||||
onPointerMove={(e) => {
|
||||
if (!spotRef.current || !containerRef.current) return;
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
spotRef.current.style.setProperty('--spot-x', `${e.clientX - rect.left}px`);
|
||||
spotRef.current.style.setProperty('--spot-y', `${e.clientY - rect.top}px`);
|
||||
}}
|
||||
>
|
||||
{/* 背景纹理:极淡的品牌色点阵(浅色画布上可见) */}
|
||||
<div className="absolute inset-0 pointer-events-none opacity-[0.03]"
|
||||
style={{
|
||||
backgroundImage: `radial-gradient(circle at 25% 25%, rgba(196,30,58,0.4) 1px, transparent 1px)`,
|
||||
backgroundSize: '48px 48px',
|
||||
}}
|
||||
/>
|
||||
{/* L1 静态点阵:token 化(颜色/alpha 随主题反色),深底自动切中性浅灰 */}
|
||||
<div className="absolute inset-0 pointer-events-none hero-dot-grid" aria-hidden="true" />
|
||||
{/* L2 光标聚光(Skim 层):mask 跟随鼠标,仅写 2 个 CSS 变量,无 canvas、无逐帧 JS;
|
||||
reduced-motion 下不渲染,避免动态 */}
|
||||
{!reduceMotion && (
|
||||
<div ref={spotRef} className="absolute inset-0 pointer-events-none hero-dot-spot opacity-60" aria-hidden="true" />
|
||||
)}
|
||||
|
||||
<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]">
|
||||
|
||||
Reference in New Issue
Block a user