feat(ui): optimize CTA buttons with contextual copy and fix static export build issues

CTA Optimization:

- Implement scenario-based CTA text across 6 key locations

- Add responsive Header CTA with icon+compact design

- Enhance CTA Section with vision-driven copy

Bug Fixes:

- Fix useSearchParams() build failure with dynamic import wrapper

- Remove useSearchParams() from PageTransition component

- Fix React 19 useEffect lint errors via useSyncExternalStore

UI Enhancements:

- Add ripple effects and gradient animations to Button

- Enhance loading skeleton with branded pulse animation
This commit is contained in:
张翔
2026-05-11 19:03:37 +08:00
parent c474394237
commit f08874f5c4
20 changed files with 794 additions and 112 deletions
+27 -15
View File
@@ -4,7 +4,7 @@ import { Suspense, useState, useEffect, useCallback } from 'react';
import { StaticLink } from '@/components/ui/static-link';
import Image from 'next/image';
import { usePathname } from 'next/navigation';
import { Menu, X } from 'lucide-react';
import { Menu, X, MessageCircle } from 'lucide-react';
import { AnimatePresence, motion } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { ThemeToggle } from '@/components/ui/theme-toggle';
@@ -75,25 +75,25 @@ function HeaderContent() {
return (
<>
<header
<header
className={`
fixed top-0 left-0 right-0 z-50
transition-all duration-300 ease-out
${isScrolled
? 'bg-[var(--color-bg-primary)]/95 backdrop-blur-xl border-b border-[var(--color-border-primary)] shadow-sm'
${isScrolled
? 'bg-[var(--color-bg-primary)]/95 backdrop-blur-xl border-b border-[var(--color-border-primary)] shadow-sm'
: 'bg-transparent'
}
`}
>
<div className="container-wide">
<div className="flex items-center justify-between h-16">
<StaticLink
<StaticLink
href="/"
className="flex items-center group"
aria-label="返回首页"
>
<Image
src={resolvedTheme === 'dark' ? '/logo-white.svg' : '/logo.svg'}
<Image
src={resolvedTheme === 'dark' ? '/logo-light.svg' : '/logo.svg'}
alt={COMPANY_INFO.name}
width={120}
height={30}
@@ -133,12 +133,12 @@ function HeaderContent() {
aria-current={isActive(item) ? 'page' : undefined}
>
{item.label}
<span
<span
className={`
absolute bottom-0 left-1/2 -translate-x-1/2 w-6 h-0.5 bg-[var(--color-brand-primary)] rounded-full
transition-all duration-200 ease-out
${isActive(item)
? 'opacity-100 scale-x-100'
${isActive(item)
? 'opacity-100 scale-x-100'
: 'opacity-0 scale-x-0'
}
`}
@@ -150,9 +150,20 @@ function HeaderContent() {
<div className="hidden md:flex items-center gap-3">
<ThemeToggle />
<Button
<Button
size="sm"
asChild
className="hidden lg:flex"
>
<StaticLink href="/contact" data-testid="consult-button">
<MessageCircle className="w-4 h-4 mr-1.5" />
</StaticLink>
</Button>
<Button
size="sm"
asChild
className="lg:hidden"
>
<StaticLink href="/contact" data-testid="consult-button"></StaticLink>
</Button>
@@ -176,7 +187,7 @@ function HeaderContent() {
<AnimatePresence mode="wait">
{isOpen && (
<motion.div
<motion.div
ref={focusTrapRef}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
@@ -184,12 +195,12 @@ function HeaderContent() {
transition={{ duration: 0.2 }}
className="fixed inset-0 z-40 md:hidden"
>
<div
<div
className="absolute inset-0 bg-black/30 backdrop-blur-sm"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
<motion.div
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
@@ -236,7 +247,8 @@ function HeaderContent() {
size="lg"
>
<StaticLink href="/contact" onClick={() => setIsOpen(false)}>
<MessageCircle className="w-4 h-4 mr-2" />
</StaticLink>
</Button>
</div>