feat(marketing): 重构营销页面与四层叙事组件体系
- 重构 About、Contact、Team 等静态营销页面 - 重构 News 新闻列表与详情页 - 重构 Products 产品目录、详情与独立产品页面 - 重构 Services 与 Solutions 服务/解决方案页面 - 重构 Detail 四层叙事组件 (Hero → Value → Trust → CTA) - 重构 Sections 页面区块组件 (Hero, CTA, SocialProof, WhyUs 等) - 新增 SectionHeader、ServiceCard、CaseCard 等可复用组件
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user