导航
NavItem - 导航项
侧边栏里的一行。
用法
import { NavItem } from '@misoto22/folio'说明
侧栏里的一行。
用 aria-current="page",而不只是靠颜色:当前行必须让看不见“它更深一点”的读者也能认出来。在这套系统里它还由字重和一块填充底色承担,所以在黑白打印和低对比度下都还在。
对框架中立。asChild 是 Next 或 React Router 应用拿到客户端跳转的方式,而这个包不用引入它们中的任何一个。
结构
| 部件 | 说明 |
|---|---|
| 行必填 | 一个 --control-h-sm 高、带 --radius 圆角的 <a href>——或者,在 asChild 下,是你交给它的那个路由链接:它接过类名和 aria-current,自己成为这一行。 |
| 图标 | 一个可选的 Remix Icon 组件,18px,aria-hidden,排在标签前面。它只由原生那条分支渲染:走 slot 的行要把图标放进子元素里面,因为 Slot 只收一个子节点。 |
| 标签必填 | children,也是这一行无障碍名称的全部——图标对它没有任何贡献。 |
| 选中底色 | active 一步打开的东西:一块 --stone 填充、中等字重,以及 aria-current="page"。三重信号,所以当前行在单色和低对比度下都活得下来。 |
实践建议
推荐
- href 要在 slot 进来的那个子元素上再写一遍:asChild 转发的只有类名和 aria-current,别的什么都不转发——所以一个自己不带 href 的 <Link>,就是一行哪儿都去不了的行。
- asChild 模式下要把图标放进子元素里面——icon 属性在那里是被无声丢掉的,整条侧边栏渲染成一列看起来没有标记的行,就是这么来的。
- active 要从路由当前的路径推出来,而不是从最后一次点击:写出 aria-current="page" 的正是它——所以一条只记着自己被点了哪儿的侧边栏,告诉读者的是他按过的那一行,而不是他正在的那一页。
避免
- 别为了标出这一页属于哪个分区,就把父级那一行也设成 active:active 的含义就是 aria-current="page",出现两个,就是告诉读者他同时在两个地方。
- 别再把这一行压得更紧:它是 --control-h-sm,默认密度下 36px、data-density="compact" 下 30px,再往下加一个 py 类,留下的就是一列要拇指瞄准才点得中的目标。
示例
default
一列侧栏,其中一行是当前项。active 一步给出三个信号——一层 --stone 填充、中等字重,以及 aria-current="page"——所以这一行在单色打印和低对比度下都还活着,而单靠颜色做不到。图标是 aria-hidden 的,对名称没有任何贡献;children 就是这一行完整的无障碍名称。active 要从路由器的当前路径推导出来,而不是从最后一次点击:一个追踪自己点击的侧栏,是在告诉读者他们在自己按下的那一行上,而不是在他们实际所处的那个页面上。
one current row
两个分区,它们之间只有一个 aria-current。别顺手把某一页所属的那个分区也标上:active 意味着 aria-current="page",而两个 aria-current 等于告诉读者他们同时在两个地方。分组标题是一个标签、不是一行——那些引题不可聚焦,也不会导航。也别把这些行压得更紧;它们坐在 --control-h-sm 上,宽松下 36px、紧凑密度下 30px,而一个比这更小的内边距类,留下的是一列拇指必须瞄准才按得中的目标。这个 nav 是按这个例子命名的,不是按它所描绘的东西。这一页本身就是从一个叫「Documentation」的 nav 里送出来的,而两个同 role、又共用一个名字的地标,任何靠地标导航的人都分不出彼此——包括 axe,它会因此判这一页不通过。
router link
asChild 把这一行交给路由器自己的 Link——这里用一个普通锚点来顶替。它只转发类名和 aria-current,别的什么都不转发,所以要在子元素上把 href 再写一遍:一条不带自己 href 的链接,是一行哪儿也不去的行。图标也要放进子元素里面,因为在这个模式下 icon 属性根本不渲染,于是整条侧栏可能就这么交付出去,看起来像一列图标被悄悄丢掉的行。Slot 只接受恰好一个子元素,这也是为什么图标是放进链接里面、而不是摆在它旁边。
无障碍
- aria-current="page",而且不只靠颜色:当前行同时由字重和填充底色承担。