放大产品中心菜单并适配 iPad 触控

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frudrax Cheng
2026-05-19 10:15:20 +08:00
parent 7abfc36ae3
commit 28093fabae
+50 -28
View File
@@ -46,19 +46,19 @@ function isActive(item: NavItem): boolean {
} }
function getNavLinkClass(item: NavItem) { function getNavLinkClass(item: NavItem) {
const baseClass = 'flex items-center gap-2 px-4 py-2.5 rounded-full font-medium transition-all duration-300'; const baseClass = 'nav-link flex items-center gap-2 px-5 py-3 rounded-full text-base font-semibold transition-all duration-300 touch-manipulation select-none';
if (isActive(item)) { if (isActive(item)) {
return `${baseClass} bg-primary text-white shadow-md`; return `${baseClass} bg-primary text-white shadow-md`;
} }
return `${baseClass} text-gray-600 hover:text-primary hover:bg-primary/10`; return `${baseClass} text-gray-600`;
} }
function getMobileNavLinkClass(item: NavItem) { function getMobileNavLinkClass(item: NavItem) {
const baseClass = 'flex flex-col items-center justify-center py-2 px-2 min-w-[4.5rem] rounded-xl transition-all duration-300'; const baseClass = 'flex flex-col items-center justify-center py-2.5 px-3 min-w-[5.5rem] rounded-xl transition-all duration-300 touch-manipulation select-none';
if (isActive(item)) { if (isActive(item)) {
return `${baseClass} bg-primary text-white`; return `${baseClass} bg-primary text-white`;
} }
return `${baseClass} text-gray-600 hover:bg-primary/10`; return `${baseClass} text-gray-600`;
} }
--- ---
@@ -85,25 +85,25 @@ function getMobileNavLinkClass(item: NavItem) {
<span>{item.label}</span> <span>{item.label}</span>
<i class="fa fa-angle-down text-xs ml-0.5 transition-transform duration-200" data-dropdown-chevron></i> <i class="fa fa-angle-down text-xs ml-0.5 transition-transform duration-200" data-dropdown-chevron></i>
</button> </button>
<div class="absolute top-full left-1/2 -translate-x-1/2 pt-2 opacity-0 invisible transition-all duration-200 z-50" <div class="absolute top-full left-1/2 -translate-x-1/2 pt-3 opacity-0 invisible transition-all duration-200 z-50"
data-dropdown-panel role="menu"> data-dropdown-panel role="menu">
<div class="bg-white rounded-2xl shadow-xl border border-gray-100 p-3 w-[600px]"> <div class="bg-white rounded-2xl shadow-xl border border-gray-100 p-4 w-[760px]">
<div class="grid grid-cols-3 gap-2"> <div class="grid grid-cols-3 gap-3">
{item.children.map(child => ( {item.children.map(child => (
<a href={child.href} role="menuitem" <a href={child.href} role="menuitem"
class={`group flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all ${ class={`menu-item group flex items-center gap-3.5 px-4 py-3.5 rounded-xl transition-all touch-manipulation select-none ${
child.id === activeNav child.id === activeNav
? 'bg-primary text-white' ? 'bg-primary text-white'
: 'text-gray-700 hover:bg-primary/10 hover:text-primary' : 'text-gray-700'
}`}> }`}>
<span class={`w-9 h-9 rounded-lg flex items-center justify-center shrink-0 ${ <span class={`menu-item-icon w-12 h-12 rounded-xl flex items-center justify-center shrink-0 transition-colors ${
child.id === activeNav child.id === activeNav
? 'bg-white/20 text-white' ? 'bg-white/20 text-white'
: 'bg-primary/10 text-primary group-hover:bg-primary group-hover:text-white' : 'bg-primary/10 text-primary'
}`}> }`}>
<i class={`fa ${child.icon} text-base`}></i> <i class={`fa ${child.icon} text-xl`}></i>
</span> </span>
<span class="text-sm font-semibold whitespace-nowrap">{child.label}</span> <span class="text-base font-semibold whitespace-nowrap">{child.label}</span>
</a> </a>
))} ))}
</div> </div>
@@ -142,19 +142,19 @@ function getMobileNavLinkClass(item: NavItem) {
class={getMobileNavLinkClass(item)} class={getMobileNavLinkClass(item)}
aria-haspopup="true" aria-expanded="false" aria-haspopup="true" aria-expanded="false"
data-mobile-dropdown-trigger> data-mobile-dropdown-trigger>
<i class={`fa ${item.icon} text-lg mb-1`}></i> <i class={`fa ${item.icon} text-xl mb-1`}></i>
<span class="text-xs font-medium whitespace-nowrap">{item.label}</span> <span class="text-sm font-semibold whitespace-nowrap">{item.label}</span>
</button> </button>
) : ( ) : (
<a href={item.href} class={getMobileNavLinkClass(item)}> <a href={item.href} class={getMobileNavLinkClass(item)}>
<i class={`fa ${item.icon} text-lg mb-1`}></i> <i class={`fa ${item.icon} text-xl mb-1`}></i>
<span class="text-xs font-medium whitespace-nowrap">{item.label}</span> <span class="text-sm font-semibold whitespace-nowrap">{item.label}</span>
</a> </a>
)) ))
} }
</div> </div>
<a href="/#contact-info" class="flex items-center justify-center py-2 px-3 bg-primary text-white rounded-xl min-w-[3rem] ml-1"> <a href="/#contact-info" class="flex items-center justify-center py-2.5 px-3.5 bg-primary text-white rounded-xl min-w-[3.25rem] ml-1.5">
<i class="fa fa-phone text-lg"></i> <i class="fa fa-phone text-xl"></i>
</a> </a>
</div> </div>
</div> </div>
@@ -163,16 +163,16 @@ function getMobileNavLinkClass(item: NavItem) {
<div id="mobile-product-panel" <div id="mobile-product-panel"
class="lg:hidden fixed left-0 right-0 z-30 bg-white shadow-xl border-b border-gray-200 transform -translate-y-full opacity-0 pointer-events-none transition-all duration-300" class="lg:hidden fixed left-0 right-0 z-30 bg-white shadow-xl border-b border-gray-200 transform -translate-y-full opacity-0 pointer-events-none transition-all duration-300"
style="top: 108px;" role="menu"> style="top: 108px;" role="menu">
<div class="max-w-md mx-auto px-3 py-3 grid grid-cols-3 gap-2"> <div class="max-w-lg mx-auto px-4 py-4 grid grid-cols-3 gap-3">
{navItems.find(item => item.children)?.children?.map(child => ( {navItems.find(item => item.children)?.children?.map(child => (
<a href={child.href} role="menuitem" <a href={child.href} role="menuitem"
class={`flex flex-col items-center gap-1.5 py-2.5 px-1 rounded-xl transition-colors ${ class={`flex flex-col items-center gap-2 py-4 px-2 rounded-xl transition-colors touch-manipulation select-none ${
child.id === activeNav child.id === activeNav
? 'bg-primary text-white' ? 'bg-primary text-white'
: 'bg-primary/5 text-gray-700 hover:bg-primary/10' : 'bg-primary/5 text-gray-700'
}`}> }`}>
<i class={`fa ${child.icon} text-lg ${child.id === activeNav ? 'text-white' : 'text-primary'}`}></i> <i class={`fa ${child.icon} text-2xl ${child.id === activeNav ? 'text-white' : 'text-primary'}`}></i>
<span class={`text-[11px] font-semibold leading-tight text-center whitespace-nowrap ${child.id === activeNav ? 'text-white' : 'text-gray-700'}`}>{child.label}</span> <span class={`text-sm font-semibold leading-tight text-center whitespace-nowrap ${child.id === activeNav ? 'text-white' : 'text-gray-700'}`}>{child.label}</span>
</a> </a>
))} ))}
</div> </div>
@@ -182,8 +182,8 @@ function getMobileNavLinkClass(item: NavItem) {
<div id="mobile-product-backdrop" <div id="mobile-product-backdrop"
class="lg:hidden fixed inset-0 z-20 bg-black/20 opacity-0 pointer-events-none transition-opacity duration-300"></div> class="lg:hidden fixed inset-0 z-20 bg-black/20 opacity-0 pointer-events-none transition-opacity duration-300"></div>
<!-- Spacer for mobile to prevent content overlap (header 3.5rem + nav 2.5rem) --> <!-- Spacer for mobile to prevent content overlap (header ~3.5rem + nav ~4rem) -->
<div class="lg:hidden h-[6rem] transition-all duration-300"></div> <div class="lg:hidden h-[7.5rem] transition-all duration-300"></div>
<slot /> <slot />
<Footer /> <Footer />
@@ -198,7 +198,7 @@ function getMobileNavLinkClass(item: NavItem) {
(function() { (function() {
var navContainer = document.getElementById('mobile-nav-container'); var navContainer = document.getElementById('mobile-nav-container');
var headerRow = document.getElementById('mobile-header-row'); var headerRow = document.getElementById('mobile-header-row');
var spacer = document.querySelector('.lg\\:hidden.h-\\[6rem\\]'); var spacer = document.querySelector('.lg\\:hidden.h-\\[7\\.5rem\\]');
var lastScrollTop = 0; var lastScrollTop = 0;
var scrollThreshold = 50; var scrollThreshold = 50;
@@ -239,7 +239,7 @@ function getMobileNavLinkClass(item: NavItem) {
} }
// Restore spacer height // Restore spacer height
if (spacer) { if (spacer) {
(spacer as HTMLElement).style.height = '6rem'; (spacer as HTMLElement).style.height = '7.5rem';
} }
} }
@@ -379,6 +379,28 @@ function getMobileNavLinkClass(item: NavItem) {
.no-scrollbar::-webkit-scrollbar { .no-scrollbar::-webkit-scrollbar {
display: none; display: none;
} }
/* Hover effects only on real pointer devices (avoid sticky hover on iPad/touch) */
@media (hover: hover) and (pointer: fine) {
.nav-link.text-gray-600:hover {
color: var(--color-primary, #165DFF);
background-color: rgb(22 93 255 / 0.1);
}
.menu-item.text-gray-700:hover {
background-color: rgb(22 93 255 / 0.1);
color: var(--color-primary, #165DFF);
}
.menu-item.text-gray-700:hover .menu-item-icon {
background-color: var(--color-primary, #165DFF);
color: #fff;
}
}
/* Active tap feedback for touch / iPad */
.nav-link:active,
.menu-item:active {
transform: scale(0.97);
}
</style> </style>
</body> </body>
</html> </html>