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]">
|
||||
|
||||
@@ -768,6 +768,31 @@ html[data-theme='dark'] body {
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
/* === Hero 背景点阵:L1 静态 + L2 光标聚光 ===
|
||||
颜色 / 透明度走 CSS 变量,暗黑模式在 html[data-theme='dark'] 覆盖,零 JS 自动反色。
|
||||
实测:浅色 brand 红 @0.12 → 1.22:1;深底中性浅灰 @0.08 → 1.20:1(可感知不抢戏)。
|
||||
原 hero 内联点阵为 rgba(196,30,58,0.4)+opacity 0.03,实测仅 1.05:1,等于不可见。
|
||||
L2 聚光层用 mask + 2 个 CSS 变量(--spot-x/--spot-y),无 canvas、无逐帧 JS。 */
|
||||
:root {
|
||||
--hero-dot-color: rgba(var(--color-brand-rgb), 0.12);
|
||||
--hero-dot-size: 24px;
|
||||
--spot-x: 50%;
|
||||
--spot-y: 50%;
|
||||
}
|
||||
html[data-theme='dark'] {
|
||||
--hero-dot-color: rgba(var(--color-text-muted-rgb), 0.08);
|
||||
}
|
||||
.hero-dot-grid {
|
||||
background-image: radial-gradient(circle at 1px 1px, var(--hero-dot-color) 1px, transparent 1px);
|
||||
background-size: var(--hero-dot-size) var(--hero-dot-size);
|
||||
}
|
||||
.hero-dot-spot {
|
||||
background-image: radial-gradient(circle at 1px 1px, var(--hero-dot-color) 1px, transparent 1px);
|
||||
background-size: var(--hero-dot-size) var(--hero-dot-size);
|
||||
-webkit-mask-image: radial-gradient(280px circle at var(--spot-x) var(--spot-y), #000 0%, transparent 72%);
|
||||
mask-image: radial-gradient(280px circle at var(--spot-x) var(--spot-y), #000 0%, transparent 72%);
|
||||
}
|
||||
|
||||
.bg-grain {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user