制作一个响应式头部导航栏(Tailwind CSS + Headless UI)

头部导航栏几乎是每个网站必备的元素,可以提供快速的页面跳转和主要信息展示。在移动设备为主的当前环境下,制作一个网站首先需要考虑移动端用户的体验,那么使用响应式方案来实现就是首选方案。

借助前端社区快速发展的各种工具,比如 Tailwind CSS 原子化样式方案可以实现任意样式(以及暗黑模式、响应式等), Headless UI 基于无样式组件提供的可扩展可自定样式的各种交互组件,我们可以快速高效的实现一个优秀好看可维护的响应式头部导航栏。

本文介绍以 Next.js 框架,结合 Tailwind CSS, Headless UI 如何制作一个响应式头部导航栏。

实现效果:

一. 准备

1. 初始化 Next.js 项目,选择 Tailwind CSS 模版

bash 复制代码
npx create-next-app@latest
bash 复制代码
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias? No / Yes

2. 安装 Headless UI 和图标库

这里我们使用 Headless UI 的 Dialog 组件来实现移动端的收起展开菜单交互效果,也可以使用其他无样式组件(如 Radix UI)或者基于样式自行实现。图标也是同理,使用了 heroicons 中的菜单图标。

bash 复制代码
npm i @headlessui/react @heroicons/react

3. 修改默认全局样式

这里我们先不考虑暗黑模式和其他全局样式。先采用统一默认全局背景黑色,字体颜色白色。

src/styles/globals.css

css 复制代码
:root {
  --color-foreground: #fff;
  --color-background: #000;
}

body {
  color: var(--color-foreground);
  background: var(--color-background);
}

4. 自定义一些 Tailwind CSS 颜色配置

即使 Tailwind CSS 提供了很多默认配置和默认颜色,但是每个项目不同,肯定都会有自己的颜色和配置需求,这些在 Tailwind CSS 中也方便扩展和配置,更多配置可以参考文档

tailwind.config.jsextend.colors 中扩展一些黑色的颜色,后续使用可以直接使用 bg-black-50, bg-black-100

diff 复制代码
+  colors: {
+    black: {
+      50: "#1A1A1A",
+      100: "#222222",
+      200: "#333333",
+    },
+  }

制作导航栏组件

我们可以参考 Tailwind Components 提供的一些组件来实现导航栏组件

src/components/Navbar.tsx

tsx 复制代码
export default function Navbar() {
  return <header></header>
}

2. 布局

这里我们使用语义化标签 header, nav 来作为主要的父元素,并且加上背景、flex、左右靠边、上下居中等样式,划分好需要哪些子元素,以及子元素所在的位置。

tsx 复制代码
<header className="bg-black-100">
  <nav
    className="mx-auto flex items-center justify-between p-2 lg:px-8"
    aria-label="Global"
  >
    {/* 左侧 Logo */}
    {/* 左侧 Title */}
    {/* 菜单列表 */}
    {/* 右侧菜单 */}
    {/* 移动端可见的右侧展开收起图标按钮 */}
  </nav>
  {/* 移动端可见的展开后的菜单列表 Dialog */}
</header>

LogoTitle 就是简单的图片和文字,这里样式比较简单,实际实现是可以用 next/image 组件代替 img 标签。

tsx 复制代码
{/* 左侧 Logo */}
 <div className="flex">
  <Link href="/" className="ml-4">
    <span className="sr-only">Haunted Dorm</span>
    <img
      className="h-10 w-auto rounded-lg lg:h-16"
      src="https://res.minigame.vip/gc-assets/haunted-dorm/haunted-dorm_icon.png"
      alt="logo"
    />
  </Link>
</div>
{/* 左侧 Title */}
<div className="mx-2 text-white lg:mx-8">Haunted Dorm</div>

4. 实现菜单列表

需要注意的是,在 Tailwind CSS 中,实现样式时是以移动端样式为主,然后根据不同响应式断点来添加更多的不同尺寸下的样式,所以我们的菜单列表在移动端是隐藏的,需要一开始就加上 hidden 隐藏样式,在 lg 下加上 flex 让他可见。

并且我们把菜单数据提取成变量,基于此循环出菜单列表,更利于代码复用。

tsx 复制代码
const menus = [
  {
    title: "Home Page",
    href: "/",
  },
  {
    title: "Game Details",
    href: "/detail",
  },
  {
    title: "Game Strategy",
    href: "/guide",
  },
  {
    title: "Interactive Community",
    href: "/community",
  },
];

{/* 菜单列表 */}
<div className="hidden lg:flex lg:gap-x-12">
  {menus.map((menu) => (
    <Link
      key={menu.title}
      href={menu.href}
      className="text-sm font-bold leading-6 text-white"
    >
      {menu.title}
    </Link>
  ))}
</div>

5. 实现右侧菜单和展开收起图标按钮

右侧菜单展开收起图标按钮的样式也比较简单,只需要加上 flex-end 让其靠右。展开收起图标按钮只有在移动端可见,并且我们需要加上点击事件来控制移动端菜单列表是否展开状态,这里使用简单的 useState 来控制即可。

tsx 复制代码
import { Bars3Icon } from "@heroicons/react/24/outline"; // 引入图标
import { useState } from "react";

export default function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);

return (
  // ...省略以上部分代码 
    {/* 右侧菜单 */}
    <div className="flex flex-1 justify-end">
      <a
        href="https://play.google.com/store/apps/details?id=com.iogame.chopio&hl=en"
        className="text-sm font-semibold leading-6 text-white"
      >
        <span className="sr-only">Download</span>
        Download
      </a>
    </div>
          
    {/* 移动端可见的右侧展开收起图标按钮 */}
    <div className="flex lg:hidden">
      <button
        type="button"
        className="inline-flex items-center justify-center rounded-md p-2.5 text-white"
        onClick={() => setMobileMenuOpen(true)}
      >
        <span className="sr-only">Open main menu</span>
        <Bars3Icon className="h-8 w-8" aria-hidden="true" />
      </button>
    </div>
  )
}

6. 实现移动端的菜单列表

这里我们使用Headless UI Dialog 组件来展示移动端的菜单列表,因为其需要基于展示收起状态切换显示,也可以自行实现这一组件。样式方面需要给他在 lg 下隐藏

tsx 复制代码
import { Dialog } from "@headlessui/react";

// ...省略部分代码
<Dialog
  as="div"
  className="lg:hidden"
  open={mobileMenuOpen}
  onClose={setMobileMenuOpen}
>
  <Dialog.Panel className="fixed right-0 top-16 z-10 w-full overflow-y-auto bg-black-200 p-4">
    <div className="space-y-4">
      {menus.map((menu) => (
        <Link
          key={menu.title}
          href={menu.href}
          className="block p-3 text-center text-base font-bold leading-7 text-white"
        >
          {menu.title}
        </Link>
      ))}
    </div>
  </Dialog.Panel>
</Dialog>

参考链接

相关推荐
Larcher5 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐17 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭29 分钟前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu1 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花1 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程