导航栏是几乎所有网站都必备的组件,一个好的响应式导航栏需要在不同设备上都能提供出色的用户体验。本节将介绍如何使用 Tailwind CSS 实现功能完善的响应式导航栏。
基础导航栏结构
桌面端导航
<nav class="bg-white shadow">
<div class="max-w-7xl mx-auto px-4">
<div class="flex justify-between h-16">
<!-- Logo -->
<div class="flex-shrink-0 flex items-center">
<img class="h-8 w-auto" src="/logo.svg" alt="Logo" />
</div>
<!-- 主导航菜单 -->
<div class="hidden md:flex items-center space-x-8">
<a href="/" class="text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium">
首页
</a>
<a href="/products" class="text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium">
产品
</a>
<a href="/about" class="text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium">
关于
</a>
<a href="/contact" class="text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium">
联系我们
</a>
</div>
<!-- 用户菜单 -->
<div class="hidden md:flex items-center space-x-4">
<button class="bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg text-sm font-medium">
登录
</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium">
注册
</button>
</div>
</div>
</div>
</nav>
移动端菜单按钮
<!-- 汉堡菜单按钮 -->
<div class="flex md:hidden">
<button type="button"
class="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-100"
aria-controls="mobile-menu"
aria-expanded="false">
<span class="sr-only">打开主菜单</span>
<!-- 汉堡菜单图标 -->
<svg class="block h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16" />
</svg>
<!-- 关闭图标 -->
<svg class="hidden h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
移动端菜单面板
<!-- 移动端菜单 -->
<div class="md:hidden" id="mobile-menu">
<div class="px-2 pt-2 pb-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-md text-base font-medium text-gray-900 hover:bg-gray-100">
首页
</a>
<a href="/products" class="block px-3 py-2 rounded-md text-base font-medium text-gray-900 hover:bg-gray-100">
产品
</a>
<a href="/about" class="block px-3 py-2 rounded-md text-base font-medium text-gray-900 hover:bg-gray-100">
关于
</a>
<a href="/contact" class="block px-3 py-2 rounded-md text-base font-medium text-gray-900 hover:bg-gray-100">
联系我们
</a>
</div>
<div class="pt-4 pb-3 border-t border-gray-200">
<div class="px-2 space-y-1">
<button class="block w-full text-left px-3 py-2 rounded-md text-base font-medium text-gray-900 hover:bg-gray-100">
登录
</button>
<button class="block w-full text-left px-3 py-2 rounded-md text-base font-medium bg-blue-500 text-white hover:bg-blue-600">
注册
</button>
</div>
</div>
</div>
交互功能实现
菜单切换功能
function setupMobileMenu() {
const button = document.querySelector('[aria-controls="mobile-menu"]')
const menu = document.getElementById('mobile-menu')
const icons = button.querySelectorAll('svg')
button.addEventListener('click', () => {
const expanded = button.getAttribute('aria-expanded') === 'true'
button.setAttribute('aria-expanded', !expanded)
menu.classList.toggle('hidden')
icons[0].classList.toggle('hidden')
icons[1].classList.toggle('hidden')
})
}
滚动处理
function handleNavbarScroll() {
const navbar = document.querySelector('nav')
let lastScroll = 0
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset
if (currentScroll <= 0) {
navbar.classList.remove('-translate-y-full')
return
}
if (currentScroll > lastScroll && !navbar.contains(document.activeElement)) {
// 向下滚动时隐藏
navbar.classList.add('-translate-y-full')
} else {
// 向上滚动时显示
navbar.classList.remove('-translate-y-full')
}
lastScroll = currentScroll
})
}
高级功能实现
下拉菜单
<div class="relative group">
<button class="flex items-center space-x-1 text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium">
<span>产品</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<div class="absolute left-0 w-48 mt-2 bg-white rounded-lg shadow-lg py-2 hidden group-hover:block">
<a href="/products/category1" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
分类一
</a>
<a href="/products/category2" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
分类二
</a>
<a href="/products/category3" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
分类三
</a>
</div>
</div>
搜索框集成
<div class="flex-1 flex justify-center px-2 lg:ml-6 lg:justify-end">
<div class="max-w-lg w-full lg:max-w-xs">
<label for="search" class="sr-only">搜索</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<input id="search"
type="search"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="搜索" />
</div>
</div>
</div>
样式优化
活跃状态
<a href="/products"
class="text-gray-900 hover:text-gray-600 px-3 py-2 text-sm font-medium
border-b-2 border-transparent hover:border-gray-300
transition-colors duration-200
aria-[current=page]:text-blue-600 aria-[current=page]:border-blue-600"
aria-current="page">
产品
</a>
滚动动效
<nav class="fixed top-0 inset-x-0 bg-white shadow
transform transition-transform duration-300
motion-reduce:transition-none">
<!-- 导航内容 -->
</nav>
深色模式支持
<nav class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto px-4">
<div class="flex justify-between h-16">
<!-- Logo -->
<div class="flex-shrink-0 flex items-center">
<img class="h-8 w-auto block dark:hidden" src="/logo-light.svg" alt="Logo" />
<img class="h-8 w-auto hidden dark:block" src="/logo-dark.svg" alt="Logo" />
</div>
<!-- 导航链接 -->
<div class="hidden md:flex items-center space-x-8">
<a href="/" class="text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-300">
首页
</a>
<!-- 更多链接 -->
</div>
</div>
</div>
</nav>
性能优化
延迟加载
// 延迟加载非关键资源
function lazyLoadNavResources() {
const images = document.querySelectorAll('nav img[data-src]')
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
})
})
images.forEach(img => observer.observe(img))
}
动画性能
/* 使用 transform 而不是 margin/top 来实现动画 */
.nav-animation {
transform: translateY(0);
transition: transform 0.3s ease;
will-change: transform;
}
.nav-hidden {
transform: translateY(-100%);
}
可访问性增强
键盘导航
function setupKeyboardNav() {
const menuItems = document.querySelectorAll('nav a, nav button')
menuItems.forEach((item, index) => {
item.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowRight':
e.preventDefault()
menuItems[(index + 1) % menuItems.length].focus()
break
case 'ArrowLeft':
e.preventDefault()
menuItems[(index - 1 + menuItems.length) % menuItems.length].focus()
break
}
})
})
}
ARIA 属性支持
<nav role="navigation" aria-label="主导航">
<button type="button"
aria-controls="mobile-menu"
aria-expanded="false"
aria-label="打开主菜单">
<!-- 按钮内容 -->
</button>
<div id="mobile-menu"
role="menu"
aria-orientation="vertical"
aria-labelledby="menu-button">
<!-- 菜单内容 -->
</div>
</nav>
最佳实践
响应式设计原则
- 移动优先设计
- 断点设置合理
- 内容优先级排序
交互设计要点
- 清晰的视觉反馈
- 平滑的动画过渡
- 直观的操作方式
性能优化策略
- 合理使用 CSS transforms
- 避免布局抖动
- 资源按需加载
可访问性建议
- 完善的键盘支持
- 适当的 ARIA 属性
- 合理的颜色对比度