六月最后一个周末,我把自己的站缩到 360px 宽,导航挤成了三行半。
一共七项:首页、技术随笔、生活记录、归档、标签、关于、合规中心。每一项都写了 white-space: nowrap,谁也不肯让谁,于是换行、再换行。
难看是小事。难看之后,正文被顶到屏幕下方,第一眼看不到文章标题 —— 这就不是小事了。
我原以为把字号从 14px 降到 13px 就完了。错。13px 之后还是三行,只是每行更挤。而 13px 的中文在手机上已经接近看不清的边界。一个看不清的导航,等于没有导航。
让步的方向一开始就错了。
第一版:关掉脚本就没用了
很自然地,我写了个按钮加一段脚本:点击切 class,样式表里 .nav--open 控制显隐。
然后我想起这个站有一条自我约束:禁用脚本时,页面必须仍然完整。我关掉脚本刷新 —— 按钮还在,点了没反应,七项全摊在页头,跟改之前一模一样。等于白改。
我不死心,又加了一行:默认展开,脚本跑起来之后再收进去。无脚本时至少是「丑,但一个字不少」。
但这么一写,按钮的正确性就全压在脚本上了。按下之后,读屏软件知道它是展开还是收起吗?我试了,不知道。因为 class 是给样式表看的,不是给辅助技术看的。
第二版:复选框那套,我放弃了
接着试的是那个流传很广的办法:用隐藏的 input type="checkbox" 配一个 label,靠 :checked 加兄弟选择器控制显隐。
它在视觉上真能跑起来。问题出在键盘上。Tab 走到那个 label 时,我不能确定每个浏览器都会给它焦点,而焦点落不上去,菜单对键盘用户就是打不开的。这一条已经足够让我放弃。
还有一条更别扭:那个隐藏的复选框会留在无障碍树里,读屏软件念出来的是「复选框,未选中」。一个导航按钮被念成复选框,用户没法知道按下去会发生什么。
第三版:details 和 summary
最后落在 details 和 summary 上。它们是 HTML 本来就有的元素,浏览器负责其余一切:点一下开,再点一下关,Enter 和空格都能切换,展开状态由浏览器自己暴露给辅助技术,我一行 aria- 都不用写。
自带的三角标记我不喜欢,用 list-style: none 加 ::-webkit-details-marker 两行去掉,自己画了一个。真正要写的样式不多:
.nav-disclosure > summary {
cursor: pointer;
list-style: none;
}
.nav-disclosure > summary::-webkit-details-marker { display: none; }
@media (min-width: 48rem) {
.nav-disclosure > summary { display: none; }
}
宽屏时把 summary 藏起来,菜单自然常开。窄屏时它是折叠块,收起这个动作由脚本完成。也就是说:脚本没跑起来,导航就是全部展开的样子。
这里有一条我事后才想明白的界线:导航的 HTML 必须始终在源码里,脚本只改可见性。details 正好符合 —— 收起的是显示,不是内容。
代价也不小
公平地说,details 不是处处都强。它的开合没有过渡动画,除非自己写,我懒得写。点菜单里的链接之后它也不会自动合上,桌面宽度下无所谓,窄屏时就得再点一次按钮。
另外,万一某个浏览器不认 details,它会忽略这个元素、把里面的内容全摊开。我没遇到过,但这个降级方向仍然是「显示」而不是「隐藏」,我认为这个方向是对的。
还有一个我没弄明白的
屏幕大约在 410px 宽的时候,页面会冒出一条很细的横向滚动条,宽一点或窄一点都没有。我逐项关掉页头、免责声明条、页脚,都没能定位到它。可能只是模拟窄屏的错觉 —— 我手上没有真的窄屏设备。
先记在这里。丑一点我能忍,横向滚动在手机上是真的烦。
现在
最后交出来的东西:一段 HTML、四十来行样式、七行脚本。没有框架,没有构建。
选 details 不是因为它是什么正确做法。复选框那套对很多站点是够用的,它牺牲的恰好是我最不想牺牲的那部分 —— 键盘和读屏。反过来,details 牺牲的是动画和一点控制力。我接受这个交换。