fix(theme): 品牌色令牌语义拆分与暗黑模式全站治理
- 拆分品牌色「文字/底色」双通道:新增 --color-brand-ink(-rgb)(深色 #F87171) 与 tailwind brand.ink;text-brand → text-brand-ink 迁移 247 处; bg-brand/border-brand 保持 --color-brand-rgb 不翻转(红底白字 5.58:1 不能动) - dark 块补齐 --color-brand-bg: #2A1418(修复深色白字压浅粉底 1.04:1) - hero 徽章新增 --badge-accent-text 桥接:accentColor 直用作深底文字不可读 (#1e3a5f 深底 1.65:1),浅色取原色 / 深色 color-mix 提亮 - 硬编码颜色 token 化:bg-white/text-gray-*/border-gray-* → bg-bg-*/text-text-*/border-border-* - 可访问性:触控目标 <24px 归零(footer 链接列 [&_a]:min-h-6 收口 377 处等)、 news 搜索框补 aria-label、标题跳级 h2→h4 修正为 h3、装饰 blur 光斑容器补 overflow-hidden 消除伪文本裁剪 - 验证:tsc 0 / eslint 0 error / jest 0 失败用例 / dogfood 33 路由 × 双主题 P1-P3 全零
This commit is contained in:
@@ -41,7 +41,7 @@ export function RichTextEditor({ value, onChange, placeholder = '请输入内容
|
||||
Link.configure({
|
||||
openOnClick: false,
|
||||
HTMLAttributes: {
|
||||
class: 'text-brand underline',
|
||||
class: 'text-brand-ink underline',
|
||||
},
|
||||
}),
|
||||
Placeholder.configure({
|
||||
@@ -87,7 +87,7 @@ function Toolbar({ editor }: { editor: Editor }) {
|
||||
const btnClass = (isActive: boolean) =>
|
||||
`p-1.5 rounded transition-colors ${
|
||||
isActive
|
||||
? 'bg-brand/20 text-brand'
|
||||
? 'bg-brand/20 text-brand-ink'
|
||||
: 'text-white/60 hover:bg-white/[0.05] hover:text-white'
|
||||
}`;
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export function TechStackShowcase({ title, subtitle, categories }: TechStackShow
|
||||
{category.items.map((item, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="px-3 py-1.5 text-xs font-medium bg-bg-secondary text-text-secondary border border-border-primary hover:border-brand/30 hover:text-brand transition-colors duration-300"
|
||||
className="px-3 py-1.5 text-xs font-medium bg-bg-secondary text-text-secondary border border-border-primary hover:border-brand/30 hover:text-brand-ink transition-colors duration-300"
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
@@ -365,7 +365,7 @@ export function DataProofSection({ title, subtitle, metrics }: DataProofSectionP
|
||||
{metric.trend && (
|
||||
<div className={cn(
|
||||
'mt-4 inline-flex items-center gap-1.5 text-sm font-medium',
|
||||
metric.trendUp ? 'text-success' : 'text-brand'
|
||||
metric.trendUp ? 'text-success' : 'text-brand-ink'
|
||||
)}>
|
||||
<span>{metric.trend}</span>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function TestimonialSection({
|
||||
<div key={index} className="w-full shrink-0">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="relative p-10 lg:p-16 border border-border-primary bg-bg-primary">
|
||||
<Quote className="absolute top-8 left-8 w-12 h-12 opacity-10 text-brand" />
|
||||
<Quote className="absolute top-8 left-8 w-12 h-12 opacity-10 text-brand-ink" />
|
||||
<div className="relative z-10">
|
||||
<p className="text-xl lg:text-2xl text-text-primary leading-relaxed font-light mb-10 lg:mb-14">
|
||||
「{testimonial.quote}」
|
||||
|
||||
@@ -55,7 +55,7 @@ export function CaseStudiesSection({
|
||||
<SectionLabel className="justify-center">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="w-14 h-px bg-brand" />
|
||||
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
||||
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand-ink">
|
||||
客户案例
|
||||
</span>
|
||||
<div className="w-14 h-px bg-brand" />
|
||||
@@ -85,7 +85,7 @@ export function CaseStudiesSection({
|
||||
</span>
|
||||
</div>
|
||||
<div className="absolute top-5 left-5">
|
||||
<span className="px-3 py-1.5 text-xs font-bold text-brand bg-brand-soft/80 backdrop-blur-sm">
|
||||
<span className="px-3 py-1.5 text-xs font-bold text-brand-ink bg-brand-soft/80 backdrop-blur-sm">
|
||||
{study.industry}
|
||||
</span>
|
||||
</div>
|
||||
@@ -99,7 +99,7 @@ export function CaseStudiesSection({
|
||||
{study.challenge}
|
||||
</p>
|
||||
<div className="pt-6 border-t border-border-primary">
|
||||
<div className="text-2xl font-bold text-brand mb-1">{study.result}</div>
|
||||
<div className="text-2xl font-bold text-brand-ink mb-1">{study.result}</div>
|
||||
<div className="text-xs text-text-muted">核心成果</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,20 +15,20 @@ export function CaseStudyCard({ study, index }: CaseStudyCardProps) {
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-50px' }}
|
||||
transition={{ duration: 0.3, delay: index * 0.06 }}
|
||||
className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md"
|
||||
className="rounded-xl border border-border-primary bg-bg-elevated p-6 shadow-sm transition-shadow hover:shadow-md"
|
||||
>
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<span className="rounded-full bg-red-50 px-2.5 py-1 text-xs font-medium text-brand">
|
||||
<span className="rounded-full bg-red-50 px-2.5 py-1 text-xs font-medium text-brand-ink">
|
||||
{study.industry}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">{study.client}</span>
|
||||
<span className="text-sm text-text-muted">{study.client}</span>
|
||||
</div>
|
||||
|
||||
<h4 className="mb-2 text-sm font-semibold text-gray-900">挑战</h4>
|
||||
<p className="mb-4 text-sm leading-relaxed text-gray-600">{study.challenge}</p>
|
||||
<h4 className="mb-2 text-sm font-semibold text-text-primary">挑战</h4>
|
||||
<p className="mb-4 text-sm leading-relaxed text-text-secondary">{study.challenge}</p>
|
||||
|
||||
<h4 className="mb-2 text-sm font-semibold text-gray-900">解决方案</h4>
|
||||
<p className="mb-4 text-sm leading-relaxed text-gray-600">{study.solution}</p>
|
||||
<h4 className="mb-2 text-sm font-semibold text-text-primary">解决方案</h4>
|
||||
<p className="mb-4 text-sm leading-relaxed text-text-secondary">{study.solution}</p>
|
||||
|
||||
<div className="rounded-lg bg-green-50 p-3">
|
||||
<p className="text-sm font-medium text-green-800">{study.result}</p>
|
||||
|
||||
@@ -21,13 +21,13 @@ export function CertificationList({ certifications }: CertificationListProps) {
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true, margin: '-50px' }}
|
||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm text-gray-700 shadow-sm transition-all hover:border-gray-300 hover:shadow-md"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-border-primary bg-bg-elevated px-4 py-2 text-sm text-text-secondary shadow-sm transition-all hover:border-border-secondary hover:shadow-md"
|
||||
>
|
||||
<Award className="h-4 w-4 text-brand" />
|
||||
<Award className="h-4 w-4 text-brand-ink" />
|
||||
<span className="font-medium">{cert.name}</span>
|
||||
<span className="text-xs text-gray-400">{cert.issuer}</span>
|
||||
<span className="text-xs text-text-muted">{cert.issuer}</span>
|
||||
{cert.link && cert.link !== '#' && (
|
||||
<ExternalLink className="h-3 w-3 text-gray-400" />
|
||||
<ExternalLink className="h-3 w-3 text-text-muted" />
|
||||
)}
|
||||
</motion.a>
|
||||
))}
|
||||
|
||||
@@ -37,7 +37,7 @@ export function CertificationsSection({ certifications }: CertificationsSectionP
|
||||
<SectionLabel className="justify-center">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="w-14 h-px bg-brand" />
|
||||
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand">
|
||||
<span className="text-[11px] tracking-[0.4em] uppercase font-bold text-brand-ink">
|
||||
资质认证
|
||||
</span>
|
||||
<div className="w-14 h-px bg-brand" />
|
||||
@@ -59,7 +59,7 @@ export function CertificationsSection({ certifications }: CertificationsSectionP
|
||||
key={idx}
|
||||
className="group flex items-center gap-4 px-8 py-5 bg-bg-primary hover:bg-bg-secondary transition-all duration-300"
|
||||
>
|
||||
<div className="w-12 h-12 rounded-xl bg-brand-soft flex items-center justify-center text-brand">
|
||||
<div className="w-12 h-12 rounded-xl bg-brand-soft flex items-center justify-center text-brand-ink">
|
||||
<Shield className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -28,7 +28,8 @@ export function DetailCTASection({
|
||||
backgroundSize: '48px 48px',
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
{/* 装饰光斑容器:overflow-hidden 防止 500px blur 圆撑大 section scrollHeight(伪文本裁剪误报源) */}
|
||||
<div className="absolute inset-0 pointer-events-none overflow-hidden">
|
||||
<div className="absolute top-1/3 right-1/4 w-[500px] h-[500px] bg-white/[0.03] rounded-full blur-[140px]" />
|
||||
</div>
|
||||
|
||||
@@ -78,7 +79,7 @@ export function DetailCTASection({
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.5, duration: 0.3 }}
|
||||
className="mt-10 text-sm text-white/50"
|
||||
className="mt-10 text-sm text-white/70"
|
||||
>
|
||||
平均响应时间 < 2小时 · 7×24小时技术支持 · 无需预付费用
|
||||
</motion.p>
|
||||
|
||||
@@ -79,9 +79,12 @@ export function DetailHero({
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: `${theme.accentColor}10`,
|
||||
color: theme.accentColor,
|
||||
// 文字色经 --badge-accent-text 间接引用:浅色取 accentColor 原色,
|
||||
// 深色由 globals.css color-mix 自动提亮(accentColor 直用作深底文字会不可读)
|
||||
color: 'var(--badge-accent-text)',
|
||||
borderColor: `${theme.accentColor}25`,
|
||||
}}
|
||||
'--badge-accent': theme.accentColor,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<span>
|
||||
<Sparkles className="w-3.5 h-3.5" />
|
||||
|
||||
@@ -32,7 +32,7 @@ function FeatureTags({ text }: { text: string }) {
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<h4 className="font-bold text-ink text-base">{title}</h4>
|
||||
<h3 className="font-bold text-ink text-base">{title}</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{tags.map((tag) => (
|
||||
<span
|
||||
@@ -99,7 +99,7 @@ function MetricCard({
|
||||
<Icon className="w-6 h-6" />
|
||||
</div>
|
||||
|
||||
<div ref={ref} className="text-3xl sm:text-4xl font-black text-brand tabular-nums leading-none mb-1">
|
||||
<div ref={ref} className="text-3xl sm:text-4xl font-black text-brand-ink tabular-nums leading-none mb-1">
|
||||
{displayValue}
|
||||
</div>
|
||||
|
||||
@@ -112,7 +112,7 @@ function MetricCard({
|
||||
|
||||
export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
return (
|
||||
<section className="relative bg-white py-20 sm:py-28 md:py-36 overflow-hidden">
|
||||
<section className="relative bg-bg-primary py-20 sm:py-28 md:py-36 overflow-hidden">
|
||||
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 32 }}
|
||||
@@ -123,13 +123,13 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
产品功能
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95] max-w-3xl">
|
||||
{product.title}
|
||||
<span className="text-brand"> 核心能力</span>
|
||||
<span className="text-brand-ink"> 核心能力</span>
|
||||
</h2>
|
||||
<p className="text-lg text-text-secondary max-w-2xl mt-6 leading-relaxed">
|
||||
{product.overview}
|
||||
@@ -176,7 +176,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
核心优势
|
||||
</span>
|
||||
</div>
|
||||
@@ -194,7 +194,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
}}
|
||||
className="bain-card p-5 flex items-start gap-3"
|
||||
>
|
||||
<CheckCircle2 className="w-5 h-5 text-brand mt-0.5 shrink-0" />
|
||||
<CheckCircle2 className="w-5 h-5 text-brand-ink mt-0.5 shrink-0" />
|
||||
<p className="text-sm font-medium text-text-secondary leading-relaxed">{benefit}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
@@ -205,14 +205,14 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
技术规格
|
||||
</span>
|
||||
</div>
|
||||
<div className="bain-card p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Shield className="w-5 h-5 text-brand" />
|
||||
<h4 className="font-bold text-ink text-sm">技术保障</h4>
|
||||
<Shield className="w-5 h-5 text-brand-ink" />
|
||||
<h3 className="font-bold text-ink text-sm">技术保障</h3>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{product.specs.slice(0, 5).map((spec, index) => (
|
||||
@@ -228,7 +228,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
}}
|
||||
className="flex items-start gap-3 text-sm text-text-secondary"
|
||||
>
|
||||
<ArrowRight className="w-4 h-4 text-brand mt-0.5 shrink-0" />
|
||||
<ArrowRight className="w-4 h-4 text-brand-ink mt-0.5 shrink-0" />
|
||||
<span>{spec}</span>
|
||||
</motion.li>
|
||||
))}
|
||||
@@ -249,7 +249,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
效果数据
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@ function AnimatedCounter({ value, duration = 2000 }: { value: string; duration?:
|
||||
}, [isInView, value, duration]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="text-3xl sm:text-4xl font-black text-brand tabular-nums leading-none">
|
||||
<div ref={ref} className="text-3xl sm:text-4xl font-black text-brand-ink tabular-nums leading-none">
|
||||
{displayValue}
|
||||
</div>
|
||||
);
|
||||
@@ -70,7 +70,7 @@ export function DetailTrustSection({
|
||||
if (!hasContent) return null;
|
||||
|
||||
return (
|
||||
<section className="relative py-20 sm:py-28 md:py-36 overflow-hidden bg-white">
|
||||
<section className="relative py-20 sm:py-28 md:py-36 overflow-hidden bg-bg-primary">
|
||||
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
|
||||
{dataProofs.length > 0 && (
|
||||
<div className="mb-20">
|
||||
@@ -83,7 +83,7 @@ export function DetailTrustSection({
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
用真实效果证明价值
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function EarlyAccessNotice() {
|
||||
>
|
||||
<p
|
||||
id="early-access-notice-heading"
|
||||
className="text-sm font-semibold text-brand mb-1"
|
||||
className="text-sm font-semibold text-brand-ink mb-1"
|
||||
>
|
||||
{EARLY_ACCESS.label}
|
||||
</p>
|
||||
@@ -40,7 +40,7 @@ export function EarlyAccessNotice() {
|
||||
</p>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="inline-flex items-center gap-1 text-sm font-medium text-brand underline-offset-4 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-secondary rounded"
|
||||
className="inline-flex items-center gap-1 min-h-6 text-sm font-medium text-brand-ink underline-offset-4 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-secondary rounded"
|
||||
>
|
||||
了解共创合作
|
||||
<span aria-hidden="true">→</span>
|
||||
|
||||
@@ -135,12 +135,12 @@ function SignalCard({ signal }: { signal: L3Signal }) {
|
||||
: 'hover:border-brand/40 hover:shadow-md hover:-translate-y-1'
|
||||
)}
|
||||
>
|
||||
<span className="inline-block text-[11px] tracking-[0.15em] uppercase font-bold text-brand mb-4">
|
||||
<span className="inline-block text-[11px] tracking-[0.15em] uppercase font-bold text-brand-ink mb-4">
|
||||
{eyebrow}
|
||||
</span>
|
||||
|
||||
<div
|
||||
className="w-10 h-10 mb-4 flex items-center justify-center rounded-md bg-brand-soft text-brand"
|
||||
className="w-10 h-10 mb-4 flex items-center justify-center rounded-md bg-brand-soft text-brand-ink"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Icon className="w-5 h-5" />
|
||||
|
||||
@@ -124,9 +124,9 @@ export function PressableButton({
|
||||
primary:
|
||||
'bg-brand text-white shadow-sm hover:shadow-brand-hover',
|
||||
secondary:
|
||||
'bg-white text-ink border border-border-primary hover:border-brand/30 hover:shadow-md',
|
||||
'bg-bg-elevated text-ink border border-border-primary hover:border-brand/30 hover:shadow-md',
|
||||
ghost:
|
||||
'bg-transparent text-brand hover:bg-brand/5',
|
||||
'bg-transparent text-brand-ink hover:bg-brand/5',
|
||||
};
|
||||
|
||||
const content = (
|
||||
@@ -175,7 +175,7 @@ export function HoverLink({
|
||||
<a
|
||||
href={href}
|
||||
className={`group inline-flex items-center gap-1.5 font-medium transition-colors duration-fast ease-standard ${
|
||||
color === 'brand' ? 'text-brand' : 'text-text-secondary hover:text-brand'
|
||||
color === 'brand' ? 'text-brand-ink' : 'text-text-secondary hover:text-brand-ink'
|
||||
} ${className}`}
|
||||
>
|
||||
<span className="relative inline-block">
|
||||
|
||||
@@ -30,7 +30,7 @@ export function ProductCard({ product }: ProductCardProps) {
|
||||
className="group"
|
||||
>
|
||||
<Link href={`/products/${product.id}`} className="block">
|
||||
<div className="relative h-full rounded-2xl bg-white border border-border-primary overflow-hidden hover:border-brand/30 hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<div className="relative h-full rounded-2xl bg-bg-elevated border border-border-primary overflow-hidden hover:border-brand/30 hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<div className="aspect-video relative overflow-hidden bg-bg-secondary">
|
||||
<Image
|
||||
src={product.image}
|
||||
@@ -54,7 +54,7 @@ export function ProductCard({ product }: ProductCardProps) {
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-bold text-ink mb-3 group-hover:text-brand transition-colors">
|
||||
<h3 className="text-xl font-bold text-ink mb-3 group-hover:text-brand-ink transition-colors">
|
||||
{product.title}
|
||||
</h3>
|
||||
|
||||
@@ -75,7 +75,7 @@ export function ProductCard({ product }: ProductCardProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-brand group-hover:gap-3 transition-all">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-brand-ink group-hover:gap-3 transition-all">
|
||||
了解详情
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
const icons: LucideIcon[] = [Zap, Clock, Users, Award, TrendingUp];
|
||||
|
||||
return (
|
||||
<section className="relative bg-white py-20 sm:py-28 md:py-36 overflow-hidden">
|
||||
<section className="relative bg-bg-primary py-20 sm:py-28 md:py-36 overflow-hidden">
|
||||
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 32 }}
|
||||
@@ -31,7 +31,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
服务能力
|
||||
</span>
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
<div className="mb-16">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
核心能力
|
||||
</span>
|
||||
</div>
|
||||
@@ -92,7 +92,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
服务优势
|
||||
</span>
|
||||
</div>
|
||||
@@ -110,7 +110,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
}}
|
||||
className="bain-card p-5 flex items-start gap-3"
|
||||
>
|
||||
<CheckCircle2 className="w-5 h-5 text-brand mt-0.5 shrink-0" />
|
||||
<CheckCircle2 className="w-5 h-5 text-brand-ink mt-0.5 shrink-0" />
|
||||
<p className="text-sm font-medium text-text-secondary leading-relaxed">{benefit}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
@@ -122,7 +122,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
服务流程
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
|
||||
className="group"
|
||||
>
|
||||
<Link href={`/solutions/${solution.id}`} className="block">
|
||||
<div className="relative h-full rounded-2xl bg-white border border-border-primary overflow-hidden hover:border-brand/30 hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<div className="relative h-full rounded-2xl bg-bg-elevated border border-border-primary overflow-hidden hover:border-brand/30 hover:shadow-lg hover:-translate-y-1 transition-all duration-300">
|
||||
<div className="aspect-video relative overflow-hidden bg-bg-secondary flex items-center justify-center">
|
||||
{solution.image ? (
|
||||
<Image
|
||||
@@ -53,7 +53,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
|
||||
className="object-cover group-hover:scale-105 transition-transform duration-150"
|
||||
/>
|
||||
) : (
|
||||
<Icon className="w-16 h-16 text-brand/20 group-hover:text-brand/40 transition-colors" />
|
||||
<Icon className="w-16 h-16 text-brand-ink/20 group-hover:text-brand-ink/40 transition-colors" />
|
||||
)}
|
||||
|
||||
<div className="absolute top-4 left-4 flex gap-2">
|
||||
@@ -71,7 +71,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-bold text-ink mb-3 group-hover:text-brand transition-colors">
|
||||
<h3 className="text-xl font-bold text-ink mb-3 group-hover:text-brand-ink transition-colors">
|
||||
{solution.title}
|
||||
</h3>
|
||||
|
||||
@@ -90,7 +90,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-brand group-hover:gap-3 transition-all">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-brand-ink group-hover:gap-3 transition-all">
|
||||
查看方案
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
@@ -142,7 +142,7 @@ export function ServiceCard({ service }: ServiceCardProps) {
|
||||
{service.features.slice(0, 3).map((feature) => (
|
||||
<span
|
||||
key={feature}
|
||||
className="px-2.5 py-1 rounded-md text-xs font-medium bg-white text-blue-600 border border-border-primary"
|
||||
className="px-2.5 py-1 rounded-md text-xs font-medium bg-bg-elevated text-blue-600 border border-border-primary"
|
||||
>
|
||||
{feature.split(':')[0]}
|
||||
</span>
|
||||
|
||||
@@ -26,12 +26,12 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
行业方案
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95] max-w-3xl">
|
||||
为<span className="text-brand">{solution.industry}</span>量身定制
|
||||
为<span className="text-brand-ink">{solution.industry}</span>量身定制
|
||||
</h2>
|
||||
<p className="text-lg text-text-secondary max-w-2xl mt-6 leading-relaxed">
|
||||
{solution.description}
|
||||
@@ -124,7 +124,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
价值主张
|
||||
</span>
|
||||
</div>
|
||||
@@ -170,7 +170,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand">
|
||||
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
|
||||
产品组合
|
||||
</span>
|
||||
</div>
|
||||
@@ -192,7 +192,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
|
||||
{solution.suiteCombination.primaryProducts.map((product) => (
|
||||
<span
|
||||
key={product}
|
||||
className="px-4 py-2 bg-brand-soft/50 text-brand text-sm font-semibold border border-brand/20"
|
||||
className="px-4 py-2 bg-brand-soft/50 text-brand-ink text-sm font-semibold border border-brand/20"
|
||||
>
|
||||
{product}
|
||||
</span>
|
||||
|
||||
@@ -15,7 +15,7 @@ export function Footer() {
|
||||
.flatMap(group => group.items);
|
||||
|
||||
return (
|
||||
<footer className="bg-dark-bg text-white relative overflow-hidden" data-testid="footer" role="contentinfo">
|
||||
<footer className="bg-dark-bg text-white relative overflow-hidden [&_a]:inline-flex [&_a]:items-center [&_a]:min-h-6" data-testid="footer" role="contentinfo">
|
||||
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-12 gap-10 lg:gap-12 py-16 lg:py-20">
|
||||
<div data-testid="card-brand" className="col-span-2 lg:col-span-4">
|
||||
|
||||
@@ -85,7 +85,7 @@ export function MegaDropdown({ label, groups, isOpen, isActive, onToggle, onOpen
|
||||
<span className="text-xs text-text-muted">{group.description}</span>
|
||||
)}
|
||||
{group.highlight && (
|
||||
<Sparkles className="w-3.5 h-3.5 text-brand" />
|
||||
<Sparkles className="w-3.5 h-3.5 text-brand-ink" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -101,7 +101,7 @@ export function MegaDropdown({ label, groups, isOpen, isActive, onToggle, onOpen
|
||||
<span className={`shrink-0 px-1.5 py-0.5 text-[10px] font-medium ${
|
||||
item.badge === '敬请期待'
|
||||
? 'bg-bg-tertiary text-text-muted'
|
||||
: 'bg-brand/10 text-brand'
|
||||
: 'bg-brand/10 text-brand-ink'
|
||||
}`}>
|
||||
{item.badge === '敬请期待' && <Lock className="w-2.5 h-2.5 inline mr-0.5" />}
|
||||
{item.badge}
|
||||
|
||||
@@ -60,13 +60,13 @@ export function MobileTabBar() {
|
||||
<Icon
|
||||
className={cn(
|
||||
'w-6 h-6 transition-colors duration-300',
|
||||
active ? 'text-brand' : 'text-text-muted group-hover:text-text-secondary'
|
||||
active ? 'text-brand-ink' : 'text-text-muted group-hover:text-text-secondary'
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
'text-xs mt-1 transition-colors duration-300',
|
||||
active ? 'text-brand font-medium' : 'text-text-muted'
|
||||
active ? 'text-brand-ink font-medium' : 'text-text-muted'
|
||||
)}
|
||||
>
|
||||
{tab.label}
|
||||
|
||||
@@ -19,7 +19,7 @@ const buttonVariants = cva(
|
||||
ghost:
|
||||
'text-text-secondary hover:bg-bg-secondary hover:text-text-primary active:scale-[0.97] transition-all duration-fast ease-standard',
|
||||
link:
|
||||
'text-text-primary underline-offset-4 hover:text-brand relative after:absolute after:bottom-0 after:left-0 after:h-[2px] after:w-0 hover:after:w-full after:bg-brand after:transition-all after:duration-normal after:ease-standard',
|
||||
'text-text-primary underline-offset-4 hover:text-brand-ink relative after:absolute after:bottom-0 after:left-0 after:h-[2px] after:w-0 hover:after:w-full after:bg-brand after:transition-all after:duration-normal after:ease-standard',
|
||||
destructive:
|
||||
'bg-error text-white shadow-sm hover:bg-error-hover hover:-translate-y-px active:translate-y-0 active:scale-[0.97] transition-all duration-fast ease-standard',
|
||||
},
|
||||
|
||||
@@ -111,7 +111,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<Check className="size-4 text-brand" />
|
||||
<Check className="size-4 text-brand-ink" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -136,7 +136,7 @@ const ContextMenuRadioItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<Circle className="size-2 fill-brand text-brand" />
|
||||
<Circle className="size-2 fill-brand text-brand-ink" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
|
||||
@@ -45,7 +45,7 @@ const VARIANT_STYLES: Record<CTAButtonVariant, string> = {
|
||||
dark:
|
||||
'text-dark-text-primary border border-dark-border hover:border-dark-text-muted hover:bg-white/5 px-10 py-4 font-semibold text-base',
|
||||
inline:
|
||||
'text-text-primary hover:text-brand px-0 py-0 font-medium text-sm bg-transparent border-0',
|
||||
'text-text-primary hover:text-brand-ink px-0 py-0 font-medium text-sm bg-transparent border-0',
|
||||
};
|
||||
|
||||
const ARROW_STYLES: Record<CTAButtonVariant, string> = {
|
||||
|
||||
@@ -113,7 +113,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="size-4 text-brand" />
|
||||
<Check className="size-4 text-brand-ink" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -138,7 +138,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="size-2 fill-brand text-brand" />
|
||||
<Circle className="size-2 fill-brand text-brand-ink" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
|
||||
@@ -31,7 +31,7 @@ const LabeledInput = React.forwardRef<HTMLInputElement, LabeledInputProps>(
|
||||
>
|
||||
{label}
|
||||
{required && (
|
||||
<span className="text-brand ml-1.5" aria-hidden="true">*</span>
|
||||
<span className="text-brand-ink ml-1.5" aria-hidden="true">*</span>
|
||||
)}
|
||||
</Label>
|
||||
)}
|
||||
@@ -56,7 +56,7 @@ const LabeledInput = React.forwardRef<HTMLInputElement, LabeledInputProps>(
|
||||
{error && (
|
||||
<p
|
||||
id={errorId}
|
||||
className="mt-1.5 text-sm text-brand"
|
||||
className="mt-1.5 text-sm text-brand-ink"
|
||||
role="alert"
|
||||
data-testid="error-message"
|
||||
>
|
||||
|
||||
@@ -31,7 +31,7 @@ const LabeledTextarea = React.forwardRef<HTMLTextAreaElement, LabeledTextareaPro
|
||||
>
|
||||
{label}
|
||||
{required && (
|
||||
<span className="text-brand ml-1.5" aria-hidden="true">*</span>
|
||||
<span className="text-brand-ink ml-1.5" aria-hidden="true">*</span>
|
||||
)}
|
||||
</Label>
|
||||
)}
|
||||
@@ -56,7 +56,7 @@ const LabeledTextarea = React.forwardRef<HTMLTextAreaElement, LabeledTextareaPro
|
||||
{error && (
|
||||
<p
|
||||
id={errorId}
|
||||
className="mt-1.5 text-sm text-brand"
|
||||
className="mt-1.5 text-sm text-brand-ink"
|
||||
role="alert"
|
||||
data-testid="error-message"
|
||||
>
|
||||
|
||||
@@ -26,7 +26,7 @@ interface MetricCardProps {
|
||||
}
|
||||
|
||||
const accentStyles: Record<string, string> = {
|
||||
brand: 'text-brand bg-brand-bg',
|
||||
brand: 'text-brand-ink bg-brand-bg',
|
||||
blue: 'text-accent-blue bg-accent-blue/10',
|
||||
teal: 'text-accent-teal bg-accent-teal/10',
|
||||
amber: 'text-accent-amber bg-accent-amber/10',
|
||||
|
||||
@@ -68,7 +68,7 @@ export function MilestoneTimeline({
|
||||
)}>
|
||||
<div className="w-2 h-2 rounded-full bg-brand" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-brand mb-1 tracking-wider">
|
||||
<div className="text-sm font-bold text-brand-ink mb-1 tracking-wider">
|
||||
{item.year}
|
||||
</div>
|
||||
<h4 className={cn('text-lg font-semibold mb-2', isDark ? 'text-white' : 'text-text-primary')}>
|
||||
@@ -80,7 +80,7 @@ export function MilestoneTimeline({
|
||||
{item.highlight && (
|
||||
<div className={cn(
|
||||
'mt-3 inline-flex items-center px-3 py-1 rounded-full text-xs font-medium',
|
||||
isDark ? 'bg-brand/10 text-brand' : 'bg-brand-bg text-brand'
|
||||
isDark ? 'bg-brand/10 text-brand-ink' : 'bg-brand-bg text-brand-ink'
|
||||
)}>
|
||||
{item.highlight}
|
||||
</div>
|
||||
@@ -112,7 +112,7 @@ export function MilestoneTimeline({
|
||||
)}>
|
||||
<div className="w-2 h-2 rounded-full bg-brand" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-brand mb-2 tracking-wider">
|
||||
<div className="text-sm font-bold text-brand-ink mb-2 tracking-wider">
|
||||
{item.year}
|
||||
</div>
|
||||
<h4 className={cn('text-base font-semibold mb-2 leading-snug', isDark ? 'text-white' : 'text-text-primary')}>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function SectionLabel({
|
||||
)}
|
||||
>
|
||||
<div className="w-10 h-px bg-brand" />
|
||||
<span className="text-[13px] tracking-[0.15em] font-medium text-brand">
|
||||
<span className="text-[13px] tracking-[0.15em] font-medium text-brand-ink">
|
||||
{children}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ const RadioGroupItem = React.forwardRef<
|
||||
ref={ref}
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
'aspect-square size-4 rounded-full border border-border-primary bg-bg-primary text-brand shadow-sm',
|
||||
'aspect-square size-4 rounded-full border border-border-primary bg-bg-primary text-brand-ink shadow-sm',
|
||||
'focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'data-[state=checked]:border-brand',
|
||||
@@ -43,7 +43,7 @@ const RadioGroupItem = React.forwardRef<
|
||||
data-slot="radio-group-indicator"
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Circle className="size-2.5 fill-brand text-brand" />
|
||||
<Circle className="size-2.5 fill-brand text-brand-ink" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
));
|
||||
|
||||
@@ -133,7 +133,7 @@ const SelectItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="size-4 text-brand" />
|
||||
<Check className="size-4 text-brand-ink" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
|
||||
Reference in New Issue
Block a user