导航栏很重要,企业或产品把最希望用户到达的地址放入了导航栏,一切从导航栏开始。
导航栏风格多变,下面我们会从复杂程度出发,将它们分为基础 air 版和进阶 pro 版,由浅入深介绍我是如何开发一款导航栏的,同时介绍 hanav 这个导航栏组件库,带领我们顺流直下,帮助完成进阶导航栏的开发。
基础导航栏 Air
有的网站导航栏简单直观。例如文档类网站,React 官网导航栏非常精简,由几个链接、一个搜索框和一个主题按钮组成;Vue 官网的导航栏则有一些需要 hover 才出现的文字列表菜单,而这个列表菜单已经是基础导航栏里最复杂的模样了。
Vue | React |
---|---|
实现这样的导航栏不太困难,如果想为面板绣朵花、加一些动画,在 CSS 中设置 opacity
、visiblity
、transform
、transition
即可,但是要特别注意其中的 visibility
不能遗漏,否则会导致键盘无法正常地 Tab 访问:
上图中导航栏的菜单面板消失后没有设置 visibility
,从焦点的外边框可以看到,按下键盘的 Tab 访问到了本不该访问的未展开的菜单面板内容。
其次,上图暴露了另一个容易忽略的常见错误,菜单的触发器是一个普通元素 <div>
,而非可以被聚焦的控件 <button>
,注意“产品功能”这个触发器无法被聚焦。
元素使用 CSS 过渡动画消失后,需要设置
visibility: hidden
,来避免键盘和屏幕阅读器的错误访问。
也许 visiblity
和 <button>
就是基础导航栏最难的技术细节了。
进阶导航栏 Pro
下面有四个网站的导航栏,它们是 hanav 的灵感来源。第一眼看见它们的时候,一定会被细腻的动画和图文并茂的内容深深吸引:
Apple | Vercel | Radix UI | |
---|---|---|---|
一眼很难深刻体会复杂的导航栏,更重要的是感受内在品质,我们要亲手验证屏幕阅读器能否正常导航、键盘能否完全控制。下面我们会从高可访问性、键盘导航和动画,三个角度来更进阶地了解导航栏。
下面的内容讨论热度低,所有会有较多的概念解释,理解了这些概念将有助于开发导航栏和各类高品质组件或应用。
高可访问
当前的前端技术环境关于可访问性的讨论热度不高,不过随着时间推移,情况会越来越好。
从企业和产品的角度,客户群体的不断扩大,只有具备多元和包容的品质才能照顾到每个人;从开发者的角度,有一部分会受到企业发展的影响,另一部分,当上层功能不断完善,或者意识到功能永远无法满足时,也许会转而关注那些能让程序变得坚固耐用的基础技术。
高可访问性让“能用”变得“好用”。
什么是可访问性 A11y(Accessibility),最常见的是设置图片的 alt
属性,以供网络差或图片资源丢失,看不见图片的情况下仍然有机会了解能概括图片的文字信息,可见可访问性是一个普及所有人的概念,而非针对特定群体:
- 正确使用语义标签和原生控件(如
<button>
); - 至少 4.5:1 的文字背景颜色对比度;
- 允许用户减弱动画(动态)效果、允许切换亮/暗主题;
- 响应式设计;
- 视频包含字幕;
- 提供网站的弱网(少流量)版本;
- 支持键盘导航;
- 符合 WAI-ARIA 规范,元素有正确的角色、属性、状态,支持辅助技术 AT(Assistive Technology)导航(如屏幕阅读器);
- 更多可以查看 Web 内容无障碍指南(WCAG)2.2 或中文版的 WCAG 2.1。
下面有几张图片,每张对应了一个可访问性优化项的实践,可以帮助更形象地理解可访问性的应用:
高对比度 | 弱网访问 | 快速导航 | 减弱动画效果 |
---|---|---|---|
音乐应用 Spotify 有优秀的可访问性,Spotify 团队把可访问性纳入了软件开发生命周期,想了解更多关于 A11y 的实践可以查看他们的技术文档“可访问性指南”。
有了上面的可访问性基础,接下来我们为导航栏对号入座一些优化项。可以打开 hanav 演示网页,方便和后面的例子同步测试,火狐浏览器的开发者工具包含一个可访问性面板,有助于调试诸如对比度、焦点顺序之类的可访问性相关特性。
语义标签和原生控件
导航栏使用 <nav>
存放网站最常用的链接,使用 <button>
触发菜单面板的展开。使用 <button>
作为点击事件的元素是常识,<button>
会作为控件得到符合操作系统习惯的特性,才能被聚焦和被屏幕阅读器识别;而 <nav>
作为导航栏至少会提供 3 个好处:
- 利于 SEO;
- 意义明确便于代码维护;
- 能被屏幕阅读器识别从而提供便利。
下面的动图演示了我用 macOS 的屏幕阅读器“旁白”,在网站提供了 <nav>
后如何快速访问导航栏:
打开旁白后,按下 Ctrl-Opt-U 激活旁白工具“转子”,继续按下左右方向键可以找到“地标”过滤项,如果网站使用了 <nav>
,导航内容将会出现在这个面板中,回车即可跳跃至导航栏。
不同的辅助技术实现的功能不同,有的辅助技术可能不包含对
<nav>
的识别。
减弱动态效果
CSS 提供了减弱动态效果的媒体查询,通过 JavaScript 同样可以检测操作系统是否开启“减弱动态效果”的选项。下面的动图展示了应用检测到操作系统开启“减弱动态效果”后,切换到导航栏的无动画版本组件的效果:
响应式设计
导航栏响应式移动设备时,有一种常见的固定模式,原先桌面导航栏上的内容会被收纳在一个面板中,这个面板由汉堡按钮触发:
Apple | Next.js | Vue | 少数派 |
---|---|---|---|
这导致原来桌面端通过 hover 或点击触发的二级菜单面板变成了移动端的三级菜单,下面的动图演示了导航栏适配移动端后的效果:
辅助技术的 ARIA 属性和状态
ARIA 属性和状态能增强辅助技术(如屏幕阅读器)的访问,除此之外没有其它作用,另外语义标签已经内置了 ARIA 角色,所以正确地使用语义标签已经做到了辅助技术的增强。
导航栏常见的 ARIA 属性有下面这些:
aria-expanded
,表示菜单是否展开,可以在用于展开菜单面板的按钮上使用;aria-controls
,通常用于触发展开菜单面板的按钮上,它的值是按钮对应菜单面板的 id,表示元素控制着对应 id 的菜单面板;aria-labelledby
,通常用于菜单面板上,它的值是面板对应的按钮的 id,表示当前面板可以由 id 元素的内容概括;aria-hidden
,跳过屏幕阅读器访问某个元素及其所有子元素;aria-label
,类似别名,设置后屏幕阅读器会阅读。
下面的动图展示了设置了 ARIA 属性和状态后,屏幕阅读器访问导航栏的模样:
可以看到,当阅读器访问触发菜单的按钮时,会根据 aria-expanded
的值为 false
,读出“已收起”,会根据 aria-label
,读出“Main”,这是因为 <nav>
设置了 aria-label="Main"
。由于不同屏幕阅读器具备的功能不同,所以部分 ARIA 属性在旁白中暂时无效。
键盘导航
键盘导航是可访问性的一部分,但是这里分解成单独一节,以突出它在用户体验的占比。如果对键盘导航是否实用有疑问,可以把自行车比作鼠标、双脚行走比作键盘,或许自行车可以带我们更快地抵达目的地,但是不能因此就失去双脚行走的能力,有时候双脚更灵活。
一,在优化键盘导航之前,有一些可访问性的常见错误会导致键盘导航失效:
- 例如在跳转链接的时候使用点击事件和
location.href
,而不使用<a>
; - 在需要点击事件的地方使用
<div>
,而不使用<button>
。
二,杜绝这样的基础概念问题,确保可聚焦的元素不遗漏之后,再进行错误聚焦逻辑的修复:只聚焦屏幕内显示的元素,不聚焦隐藏元素,例如在“基础导航栏 Air”一节列举的不设置 visibility: hidden
导致透明元素仍然可聚焦的问题。
如果元素的结构合理,那么 Tab 的序列就会是基本正确的,至此,已经可以通过键盘或屏幕阅读器访问整个网页了。基础版的导航栏至少需要做到这一步。
三,进行键盘导航的聚焦优化,让键盘导航符合用户使用操作系统的习惯:
- 例如焦点跳跃,通过按钮触发对话框时,焦点从按钮跳跃至对话框的第一个可聚焦元素;
- 焦点循环,焦点会在一组包含头尾的元素中循环流动,通过 Escape 退出循环;
- Home 跳跃至一组可聚焦元素的头,End 跳跃至尾;
- 使用方向键进行上下左右元素的聚焦。
四,最后是快捷键的实现,这一步也是键盘战胜鼠标的关键,输入指令永远比 GUI 上的光标移动更快速。要注意,快捷键是一般键盘导航(即 Tab 和 Shift-Tab)的增强,不是替代关系。下面是三个网站快捷键(按下 Shift-?)的例子:
GitHub | Spotify | |
---|---|---|
下面的动图展示了在适配键盘导航之后的导航栏,使用键盘访问的模样:
可以看到,当焦点聚焦触发器按钮时,按下回车或空格,焦点跳跃至菜单面板的第一个可聚焦元素,当焦点位于菜单面板最后一个可聚焦元素时,按下 Tab,焦点循环跳跃至第一个可聚焦元素,最后按下 Escape,焦点回到触发器按钮并收起了菜单面板。
这样的导航栏可以完全通过 Tab、Shift-Tab 和 Escape 访问,对键盘用户非常友好。不止一种焦点控制的实现,如果想了解更多可以查看 W3C 提供的导航栏模版范例以及键盘导航的实践建议。
丝滑动画
动画是平淡文字的旋律,是黑白世界的颜料。
下面的动图展示了带有过渡动画的导航栏的样子:
通常在我们平时浏览的网页里,可能“渐隐”配合“位移”的导航栏动画会占多数,并且切换菜单时,两个菜单在动画上不会产生关联。类似:
.default {
opacity: 0;
transform: translateY(-5px);
visibility: hidden;
transition: opacity .3s, transform .3s, visibility .3s;
}
.expanded {
opacity: 1;
transform: translateY(0px);
visibility: visible;
}
相比,上面动图的动画是一种比较复杂的导航栏切换动画的实现,它有一个好处,它可以很好地表达“内容展现的由来、切换的过程和消失的去向”,很容易被接受和理解。
hanav 默认提供了这种较为复杂的动画,如果回到“进阶导航栏 Pro”这一节的开头,会从 Apple、Radix UI、Vercel 和 Google 的有关页面的导航栏找到这个动画的影子。
hanav
以上就是在开发一个导航栏时,我会考虑的几个方向。上面的内容把导航栏分为了基础和进阶,但是这样的分类不代表基础版的导航栏就不好用,如果基础版的导航栏同样具备语义标签、原生控件、基础的键盘交互以及不加多余的 ARIA 属性,那么这个导航栏依然是合格的、并且是好用的,同时基础版的导航栏可以快速地完成开发,后续的进阶优化是可扩展的。
另一方面,如果在您的场景里,需要更周全的设计,刚好符合之前提到的,需要响应式设计、正确的语义标签和 ARIA 属性、合理的动画、允许减弱动态效果,刚好对上面每节最后例子的截图里的导航栏有好感,那么刚好现在是时候引出一直陪伴在我们身边的导航栏组件库 hanav 了。
之前每一节最后的例子截图里的导航栏,都由 hanav 支持,如果想亲自尝试,可以访问 hanav 的演示网页。
hanav 为我们做了很多工作,比如可访问性、键盘导航以及动画方面的优化。丝滑直观的动画是 hanav 的一个特点,可是如果希望自定义过渡动画,hanav 同样允许自定义切换菜单、展开和收起菜单的动画。无需担心自定义功能会增加 hanav 的产包、影响摇树,hanav 提供了独立的自定义动画组件,专用于防止无用代码的介入。
其它的,hanav 基于 React,使用形态直观、入参简洁,开发体验友好。尽管 hanav 是一个 React 库,但无论在开发导航栏还是其他组件或应用时,高可访问性、键盘导航和动画设计始终是通用的,它们让网页从“能用”变得“好用”。
相关链接:
- Accessible Rich Internet Applications (WAI-ARIA) 1.2,W3C 的可访问性规范
- 可访问性指南,Spotify 的可访问性指南
- focus-rings,Discord 技术团队开发的焦点控制库
- Discord Accessibility,Discord 的可访问性板块
- focus-trap,焦点控制库
- #NoMouse 挑战,一个不使用鼠标的全球性活动
- Web Content Accessibility Guidelines (WCAG) 2.1,W3C 的可访问性指南
- ARIA Authoring Practices Guide (APG),W3C 的可访问性场景模式和实践指南
- 中华人民共和国无障碍环境建设法
- 无障碍环境建设条例
- 上海互联网信息无障碍公共服务平台
- 中华人民共和国残疾人保障法
- GB/T 37668-2019,信息技术——互联网内容可访问性技术要求与测试方法