Files
novalon-website/src/components/sections/section-header.tsx
T
张翔 829d83522c feat(marketing): 重构营销页面与四层叙事组件体系
- 重构 About、Contact、Team 等静态营销页面
- 重构 News 新闻列表与详情页
- 重构 Products 产品目录、详情与独立产品页面
- 重构 Services 与 Solutions 服务/解决方案页面
- 重构 Detail 四层叙事组件 (Hero → Value → Trust → CTA)
- 重构 Sections 页面区块组件 (Hero, CTA, SocialProof, WhyUs 等)
- 新增 SectionHeader、ServiceCard、CaseCard 等可复用组件
2026-07-07 06:53:40 +08:00

101 lines
2.9 KiB
TypeScript

'use client';
import { motion } from 'framer-motion';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
interface SectionHeaderProps {
/** English label shown above title (e.g. "What We Do") */
label: string;
/** Main title text */
title: string;
/** Optional highlighted part of the title (rendered in brand red) */
highlight?: string;
/** Optional description below the title */
desc?: string;
/** Whether this header is on a dark background */
light?: boolean;
className?: string;
}
const EASE = [0.22, 1, 0.36, 1] as const;
export function SectionHeader({
label,
title,
highlight,
desc,
light = false,
className = '',
}: SectionHeaderProps) {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
className={`mb-12 md:mb-16 lg:mb-20 ${className}`}
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.6, ease: EASE }}
>
{/* Eyebrow label with brand accent bar */}
<div className="flex items-center gap-3 mb-4 md:mb-5">
{/* Brand accent bar — grows from bottom on entry */}
<motion.div
className="w-1 rounded-full origin-bottom"
style={{
height: '1rem',
backgroundColor: light
? 'rgba(255,255,255,0.5)'
: 'var(--color-brand)',
}}
initial={shouldReduceMotion ? {} : { scaleY: 0 }}
whileInView={{ scaleY: 1 }}
viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.4, delay: 0.1, ease: EASE }}
/>
<motion.div
className="text-[10px] tracking-[4px] uppercase font-medium"
style={{
color: light
? 'rgba(255,255,255,0.5)'
: 'var(--color-brand)',
}}
initial={shouldReduceMotion ? {} : { opacity: 0, x: -8 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.4, delay: 0.2, ease: EASE }}
>
{label}
</motion.div>
</div>
{/* Title */}
<h2
className="font-sans text-[clamp(28px,6vw,52px)] font-bold leading-[1.1] tracking-[-1px] mb-4 md:mb-5"
style={{ color: light ? '#FFFFFF' : 'var(--color-text-primary)' }}
>
{title}
{highlight && (
<span style={{ color: 'var(--color-brand)' }}>
{highlight}
</span>
)}
</h2>
{/* Description */}
{desc && (
<p
className="text-[15px] leading-relaxed max-w-[480px]"
style={{
color: light
? 'rgba(255,255,255,0.5)'
: 'var(--color-text-muted)',
}}
>
{desc}
</p>
)}
</motion.div>
);
}