Refactor/optimize ui #24
@@ -12,6 +12,7 @@ import { EASE_OUT } from '@/components/ui/page-decoration';
|
|||||||
import { CTAButton } from '@/components/ui/cta-button';
|
import { CTAButton } from '@/components/ui/cta-button';
|
||||||
import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants';
|
import { COMPANY_INFO, TRUST_SIGNALS, EARLY_ACCESS } from '@/lib/constants';
|
||||||
import { useSiteConfig } from '@/lib/site-config';
|
import { useSiteConfig } from '@/lib/site-config';
|
||||||
|
import { useReducedMotion } from '@/hooks/use-reduced-motion';
|
||||||
import { type NewsItem } from '@/lib/constants/news';
|
import { type NewsItem } from '@/lib/constants/news';
|
||||||
import { NEWS } from '@/lib/constants/news';
|
import { NEWS } from '@/lib/constants/news';
|
||||||
|
|
||||||
@@ -145,7 +146,9 @@ function HeroSection({ heroData }: {
|
|||||||
heroData?: Record<string, any> | null;
|
heroData?: Record<string, any> | null;
|
||||||
}) {
|
}) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const spotRef = useRef<HTMLDivElement>(null);
|
||||||
const siteConfig = useSiteConfig();
|
const siteConfig = useSiteConfig();
|
||||||
|
const reduceMotion = useReducedMotion();
|
||||||
|
|
||||||
const heading = heroData?.heading || heroData?.headingBottom || '让每一家企业都拥有数据驱动的决策能力';
|
const heading = heroData?.heading || heroData?.headingBottom || '让每一家企业都拥有数据驱动的决策能力';
|
||||||
const subheading = heroData?.subheading || heroData?.description || '';
|
const subheading = heroData?.subheading || heroData?.description || '';
|
||||||
@@ -160,14 +163,20 @@ function HeroSection({ heroData }: {
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className="relative min-h-[90vh] flex items-center overflow-hidden bg-bg-primary"
|
className="relative min-h-[90vh] flex items-center overflow-hidden bg-bg-primary"
|
||||||
data-testid="hero-section"
|
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`);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 背景纹理:极淡的品牌色点阵(浅色画布上可见) */}
|
{/* L1 静态点阵:token 化(颜色/alpha 随主题反色),深底自动切中性浅灰 */}
|
||||||
<div className="absolute inset-0 pointer-events-none opacity-[0.03]"
|
<div className="absolute inset-0 pointer-events-none hero-dot-grid" aria-hidden="true" />
|
||||||
style={{
|
{/* L2 光标聚光(Skim 层):mask 跟随鼠标,仅写 2 个 CSS 变量,无 canvas、无逐帧 JS;
|
||||||
backgroundImage: `radial-gradient(circle at 25% 25%, rgba(196,30,58,0.4) 1px, transparent 1px)`,
|
reduced-motion 下不渲染,避免动态 */}
|
||||||
backgroundSize: '48px 48px',
|
{!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="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]">
|
||||||
|
|||||||
@@ -768,6 +768,31 @@ html[data-theme='dark'] body {
|
|||||||
background-size: 24px 24px;
|
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 {
|
.bg-grain {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user