应用外壳 · app-shell
工具型页面的骨架:左边一列常驻导航、上面一条工具栏、右边是主区。 它存在的理由不是"少写几个 div",而是两个每个人都会踩的坑 —— 见下面两节。
1 · 标准三段式
应付账款 · 2025 年度
当前页靠三条线索标记:左侧 3px 竖条、底色、字重加一档 ——
不只靠底色,灰度打印也分得出来。而且它同时带了
aria-current="page",读屏用户听到的是"总览,当前页"。
2 · 坑之一:宽内容会撑破主区(两个保险)
❌ 常见写法:宽表格直接放主区
两件事都没做:min-width 保持默认的 auto
("不小于内容的最小宽度"),宽表格也没有自己的滚动容器。
于是 grid 轨道被内容顶开,主区被撑到栏外 ——
侧栏、顶栏一起被推出可视区。
✅ 本库:min-width: 0 + .shell__scroll
主区宽度锁在栏宽内(min-width: 0),宽内容放进
.shell__scroll 里自己横滚 —— 侧栏和顶栏不动,页面不横滚。
这就是 .shell__main { min-width: 0 } 存在的全部理由。
它是整个组件里最容易漏、后果最大的一行:漏了以后页面在 1280px 屏幕上看着正常,
一旦数据表多两列就横滚 —— 而且你会先怀疑表格,不会怀疑外壳。
3 · 坑之二:高度用 100dvh,不是 100vh
这一条在桌面浏览器上看不出来,只能在手机上看到,
所以没有截图可摆 —— 但它是本组件写 min-height: 100vh; min-height: 100dvh 两行的原因。
100vh取的是地址栏收起时的视口高(最大那个)- 地址栏一展开,可视区域变小,而元素仍然按大的算 ⇒ 底部被裁掉一截
- iOS Safari 上最明显:页面最后一个按钮点不到,而且不出现滚动条
4 · 侧栏更窄:只有图标
窄侧栏形态
图标态的名字必须来自可见文字 .shell__link-label ——
它被视觉隐藏(1px 盒 + 裁切)但仍在可访问树里,读屏念的是「总览」而不是「链接」。
改用 aria-label 也能让读屏念对,但那是第二份真值:改了可见文字忘了改它,
念的还是旧的;更关键的是抽屉里那份克隆补不回文字(克隆离开
.shell--rail 作用域,隐藏规则失效,但 aria-label 不会变成可见文字)。
title 只负责鼠标悬停提示,不负责名字。
5 · 主区自己滚(看板型)
只有主区滚动
.shell--scroll-main 让侧栏与顶栏完全不动,只有主区滚 ——
适合看板、监控墙。⚠️ 用这个变体时不要再让页面本身滚:两个滚动容器会让
"滚到底"的行为不可预期(用户滚的是哪一个?)。
6 · 窄屏:侧栏收走,顶栏出一个菜单按钮
把浏览器窗口缩到 768px 以下。侧栏整列消失,
主区铺满,顶栏起始边出现一个 ☰ 按钮 —— 点它打开抽屉。
Esc 关、点遮罩关、关闭后焦点回到按钮上,这三件事来自
03-patterns/drawer,本组件只做接线(shell-nav.js)。
应付账款 · 2025 年度
上面第 1–5 节没有引 shell-nav.js,所以它们缩窄后没有按钮,
侧栏直接消失 —— 缩窄窗口看看就知道了:一个入口都没有。
所以这层脚本在 START-HERE 里归「必需」,不是"可选增强":
"宽屏没有它一切照旧"只对桌面成立,而用户在手机上就走不出去了。
⚠️ 别在这里自己写一个滑出来的侧栏。抽屉要的不是"滑出来", 而是焦点陷阱 / 焦点归位 / 背景 inert / 锁滚动 / Esc 五件事。 自己写必然漏两件,而漏掉的那两件在鼠标用户眼里完全正常。
7 · 窄侧栏 + 抽屉:图标在桌面,文字在抽屉
把窗口缩到 768px 以下再点 ☰: 抽屉里是图标 + 文字。同一份标记 —— 不是第二份导航,也没有 JS 分支。
图标导航
文字包在 .shell__link-label 里:rail 状态下它被视觉隐藏,
但仍在可访问树里(读屏念「总览」,不是「链接」)。抽屉里的克隆被挂到
<body> 下,离开了 .shell--rail 的作用域,
隐藏规则自然不再命中 —— 文字自己就回来了。所以不能把文字删掉、
也不能用 display: none:前者抽屉里只剩一排图标,后者连名字都没了。
只做纯图标栏、不接抽屉的话,第 4 节那种写法(图标 + aria-label)
也成立;但只要你会用 data-shell-nav,就把文字留在 DOM 里 ——
一份标记同时满足两种形态。
8 · 平面外壳(shell--flat):融进纸面,不画第二层框
内嵌在报告里的外壳
什么时候用:外壳本身已经在卡片 / 弹窗 / 报告版式里。
这时它再自带一层底色和左边线,就会和外层边框形成"双重描边" ——
两条相距 1px 的线,看上去像渲染错位。
什么时候别用:外壳是整页容器。那时它需要自己的边界,
--flat 会让侧栏和主区糊成一片。