NavLink 导航项

导航列表中的一项,标示当前所在的页面。

'use client'

import { BookOpen, Gamepad2, House, Library } from 'lucide-react'
import { NavLink, Stack } from '@hina-ui/react'

const items = [
  { icon: House, label: '首页', active: false },
  { icon: Gamepad2, label: 'Galgame', active: true },
  { icon: BookOpen, label: '轻小说', active: false },
  { icon: Library, label: '漫画', active: false },
]

export default function Demo() {
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      {items.map(item => (
        <NavLink key={item.label} href="#" active={item.active} icon={<item.icon />}>
          {item.label}
        </NavLink>
      ))}
    </Stack>
  )
}
tsx

用法

import { NavLink } from '@hina-ui/react'
ts

NavLink 是纵向导航列表中的一项,默认渲染为链接,整行都是点击区域。它自己不提供列表容器,外层用 Stack 或 nav 排布。

import { NavLink, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      <NavLink href="#">概览</NavLink>
      <NavLink href="#">我的书架</NavLink>
      <NavLink href="#">收藏</NavLink>
    </Stack>
  )
}
tsx

它与 Link 的分工是明确的:Link 是正文行内的文字链接,随文字流动;NavLink 是导航结构中的一行,有固定行高与整行热区。正文里不要用 NavLink。

示例

当前项

active 标出读者当前所在的页面:字色回到正色并加重,背景同时浮现一层选中态底色。它还会给元素加上 aria-current="page",屏幕阅读器据此播报当前位置。

一组导航中同时只应有一项为 active。

import { NavLink, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      <NavLink href="#">概览</NavLink>
      <NavLink href="#" active>
        我的书架
      </NavLink>
      <NavLink href="#">收藏</NavLink>
    </Stack>
  )
}
tsx

悬停于选中项时,悬停底色叠加在选中态底色之上,而非替换它,对比度只升不降,因此选中状态在悬停过程中不会丢失。

图标

icon 属性放在文字之前,尺寸由组件统一为 4 个单位,不需要在图标上写尺寸类。图标是装饰,文字才是可读名称。

import { CircleUser, House, Settings } from 'lucide-react'
import { NavLink, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      <NavLink href="#" active icon={<House />}>
        概览
      </NavLink>
      <NavLink href="#" icon={<CircleUser />}>
        账号
      </NavLink>
      <NavLink href="#" icon={<Settings />}>
        设置
      </NavLink>
    </Stack>
  )
}
tsx

路由链接

as 指定渲染为何种元素,默认为 a。传入 next/link 等路由组件即可获得客户端跳转,href 等属性会透传过去。Server Component 无法把组件传给 as,这类组合需要放在 'use client' 模块中。当前项由调用方根据路由判断后传入 active,组件不自行匹配路由。

'use client'

import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { NavLink, Stack } from '@hina-ui/react'

const links = [
  { to: '/components/breadcrumb', label: 'Breadcrumb' },
  { to: '/components/nav-link', label: 'NavLink' },
  { to: '/components/splitter', label: 'Splitter' },
]

export default function Demo() {
  const pathname = usePathname()
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      {links.map(link => (
        <NavLink key={link.to} as={NextLink} href={link.to} active={pathname === link.to}>
          {link.label}
        </NavLink>
      ))}
    </Stack>
  )
}
tsx

不可用

disabled 用于暂时不可进入的入口:该项降到半透明、不接受鼠标点击、退出键盘 tab 序列,并带上 aria-disabled="true"。

导航项长期不可用时应当直接移除,而不是保留一个不可用的入口。

import { NavLink, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="none" as="nav" className="w-full max-w-56">
      <NavLink href="#" active>
        我的书架
      </NavLink>
      <NavLink href="#">收藏</NavLink>
      <NavLink href="#" disabled>
        创作中心
      </NavLink>
    </Stack>
  )
}
tsx

收起态

NavLink 放进 Sidebar 后会感知侧栏状态。侧栏收成 rail(只剩图标)时,文字淡出,label 的内容改由悬停提示给出,同时作为该项的无障碍名。

因此放进 Sidebar 的 NavLink 都应当写 label,否则收起后这一项既没有可见文字,也没有可读名称。脱离 Sidebar 时 label 不起作用,也不会渲染提示。用法见 Sidebar 的文档。

收起时导航文字较早淡出,展开时延后淡入。自定义页眉和页脚文字可用 SidebarLabel 保持相同过渡。

无障碍

  • 当前项带 aria-current="page",位置信息不依赖颜色。
  • 不可用项带 aria-disabled="true" 并退出 tab 序列。
  • 图标由组件标为装饰,不进入朗读序列;可读名称来自 children 的文字。
  • 收起成 rail 时,文字虽然不可见,但 label 会作为 aria-label 保留,朗读不受影响。
  • 组件本身不渲染 nav 地标,外层容器需要自行提供。

API

Props

属性
类型
默认值
说明
active
boolean
false
是否为当前页面
disabled
boolean
false
是否不可用
label
string
—
侧栏收起为 rail 时的提示文字与无障碍名
as
string | Component
'a'
渲染成的元素或组件
asChild
boolean
false
由子元素承担渲染
className
string
—
追加到根元素的类

其余属性透传到实际渲染的元素上,例如 href,或者路由组件自己的属性。

内容属性

属性
说明
children
项的文字,也是可读名称
icon
文字之前的图标