Linear Header 里藏着什么:一个「精准消失」的自适应系统

Linear Header 里藏着什么:一个「精准消失」的自适应系统

Linear 顶部导航栏隐藏着一套基于 ResizeObserver 的容器级自适应系统——它决定了哪些元素精准消失,以及为什么用 visibility:hidden 而非 display:none。

每日产品 UI 设计拆解
2026/5/15 · 1:10
0 订阅 · 1 内容
Linear 的设计语言有一句口头禅:「Information per pixel is the goal.」1 大多数 PM 第一次读到这句话,会觉得不过是一条设计格言;直到有人扒开了 Linear 生产环境里的压缩代码,才看清这句话是怎么落地进每一个组件的。
Tomas Pustelnik(前端工程师)用 AI 逆向工程还原了 Linear 的顶部 Header,写下了「过度工程化?也许。但绝对聪明,绝对漂亮。」2 这篇文章让 Linear 的 Header 从「看上去普通的顶部导航栏」变成了一堂关于设计决策的公开课。

你每天在用,但可能没注意到

打开 Linear,当一个项目的标签(Tabs)数量增多,或者你把浏览器窗口缩小,注意 Header 的变化——它不会出现水平滚动条,标签行也不会换行,多出来的 Tab 项会「悄悄消失」,被一个「More」下拉按钮收纳起来。整个过程没有布局跳动,也没有闪烁。
这种「精准消失」在大多数产品里是做不到的,或者代价很高。通常有两种更简单的处理方式:
  • 让 Tab 区域出现横向滚动(常见做法,但不优雅)
  • 用 CSS 截断超出部分,牺牲可访问性(更偷懒)
Linear 选了第三条路:实时感知容器实际可用空间,决定哪些 Tab 可见、哪些折叠。这不是一个样式上的技巧,而是一套完整的行为系统。
Linear Header 主要结构分区:Title(面包屑)、Tabs(自适应标签)、Side(快捷操作)、Subheader(筛选器)
Linear Header 主要结构分区:Title(面包屑)、Tabs(自适应标签)、Side(快捷操作)、Subheader(筛选器)

Header 的四个区域,各司其职

Linear Header 由四个结构区域构成,Tomas Pustelnik 在逆向工程分析中将其清晰标注出来2
区域位置职责
Title左侧上下文感知面包屑:当前位置的路径导航
Tabs中间当前视图可切换的标签页,可自适应隐藏
Side右侧快捷操作图标按钮组(筛选、设置等)
Subheader下方第二行过滤器栏,在当前视图支持时显示
四个区域中,Tabs 是复杂度的来源。其他三个区域的内容相对固定,而 Tabs 的项数取决于用户创建了多少个自定义视图。一个活跃团队可能有 10 个以上 Tab,但 Header 的水平空间永远是有限的。

ResponsiveSlot:不用 CSS 媒体查询的响应式

解决「有限空间 + 不定数量 Tab」这个矛盾的核心,是一个名为 ResponsiveSlot 的组件。2
它的运作方式和大多数「响应式」设计截然不同:
  • CSS 媒体查询的做法:基于浏览器视口宽度(viewport width)决定显示什么。在固定断点(如 768px、1024px)切换布局。
  • ResponsiveSlot 的做法:基于容器的实际可用空间,通过 ResizeObserver API 持续监听 Header 区域的尺寸变化,实时计算能容纳几个 Tab 项。
每个 Tab 项通过 priority 属性向一个 MobX store(前端状态管理库,负责追踪 UI 的实时状态)注册自己的「优先级」,当空间不足时,低优先级的 Tab 先消失,高优先级的始终可见。这让 UI 的折叠行为变成了一个可配置的优先级队列,而不是死板的「前 N 个可见、后面都折叠」。
这与 Linear 的整体设计哲学一致:间距体系是 4px 基础单位(4/8/12/16/24/32/48px 的精确序列),组件边界通过 1px 边框区分而非阴影。1 这种「精确克制」的思路,在 ResponsiveSlot 里体现为:空间计算精确到像素,不存在「大约能放下」的模糊区间。

两个让你注意不到「变化发生了」的决策

有了 ResponsiveSlot 来决定哪些 Tab 要折叠,接下来的问题是:折叠的瞬间,UI 不能抖动。Linear 用两个具体的技术决策解决了这个问题。2

visibility: hidden 而非 display: none

这两个 CSS 属性看上去都能「隐藏」元素,区别在于:
  • display: none:元素从布局中完全移除,其他元素会填充它原来的位置 → 引发布局重排
  • visibility: hidden:元素不可见,但仍占据原来的空间 → 布局不变
Linear 对即将折叠的 Tab 项使用 visibility: hidden。这意味着在 ResizeObserver 测量期间,所有 Tab 的尺寸数据仍然准确(元素还在,只是透明的)。测量完成、「More」按钮尺寸确定之后,才真正决定哪些 Tab 从 DOM 上消失。
这个顺序是关键:先量尺寸,再做切换,而不是「切换了再修正」。少了一次引发用户可感知布局跳动的机会。
而「More」按钮本身,用绝对定位精确放在最后一个可见 Tab 之后。它的宽度参与整体的空间计算,不是事后补丁进来的。
Tabs 自适应三步流程:全部可见 → 空间收窄时溢出项消失 → 「More」按钮收纳折叠项
Tabs 自适应三步流程:全部可见 → 空间收窄时溢出项消失 → 「More」按钮收纳折叠项
此外,Tab 项支持内联拖拽排序——用户可以在 Header 里直接拖动 Tab 改变顺序。visibility: hidden 保留布局位置,使得这个拖拽交互不需要特殊处理折叠态。display: none 做不到这一点。

局部 MobX Store,而非全局

状态管理是另一个容易被忽视的决策。多数前端状态管理方案会把 UI 状态放进全局 store,让各组件订阅。Linear 的 ResponsiveSlot 反其道而行:
MobX store 通过工厂函数创建,保存在 React Context(React 框架里组件间共享状态的机制)里——这个 store 只服务于当前这个 Header 实例,不与其他页面的 Header 共享。2 用 Tomas 的话来说,这是「本地化状态管理而非全局 store 的典范」。
结果是:Header 的 Tab 折叠状态完全自治,切换页面不会有残留状态的干扰,也不会给全局 store 增加噪音。这种「只对自己负责」的状态隔离,是 Linear 「surgical, understated」1 这一评语的具体来源之一。

放在横坐标上看:同场景,不同哲学

Linear 的 ResponsiveSlot 做的是「减法」:在有限空间里把不够重要的东西隐藏起来,保持界面不拥挤。有意思的是,这个「减法」的背后配套了一个「加法」:Cmd+K 命令面板
被折叠起来的操作不是消失了,而是可以通过命令面板零鼠标访问。3 可见区域的「干净」和命令面板的「完整」,是同一套策略的两面。
Notion 处理类似问题的方式截然不同。4 Notion 的侧边栏是无限嵌套的层级树(pages within pages),信息架构本身是递归的;而 Linear 的侧边栏是扁平的任务导向结构,只有 Teams/Projects/Views 三层。5 这不是谁更好的问题——两种结构各自匹配了各自的内容模型:Notion 里文档可以无限嵌套,侧边栏就必须支持无限嵌套;Linear 里一个 issue 最深到项目级,侧边栏就没有必要无限下钻。
ResponsiveSlot 的存在,正是因为 Linear 选择了「工具而非画布」的产品定位:用户是专业人士,界面应该密集且高效,但密集不等于拥挤,高效不等于把所有东西铺在屏幕上。6
Linear 多级子团队界面(5 层级联):信息层级的精确控制从 Header 延伸到整个导航体系
Linear 多级子团队界面(5 层级联):信息层级的精确控制从 Header 延伸到整个导航体系
这一套「减法可见 + 加法命令」的组合,被 SaaSUI.Design 的 Rakesh Mondal 总结为 2026 年 SaaS 设计里最受推崇的方向:「当新产品的设计师说『感觉像 Linear』——这是 2026 年最高的赞誉。」3 Calm Design 已经是品牌属性,而不只是风格偏好。

PM 可以带走的三个判断

判断一:容器响应 > 视口响应
ResizeObserver 监听容器宽度而非浏览器视口宽度——这一选择让同一个 Header 组件在嵌套布局、侧边栏展开/收起等场景下都能正确运作,不需要为每种布局写额外的断点规则。如果你的产品有「可变侧边栏 + 主内容区组件」的布局,这个方向值得参考。
判断二:「隐藏」不等于「移除」
visibility: hidden 的保留布局空间,让测量、拖拽、动画都能基于真实 DOM 状态运作,避免了「移除→重算→插回」的多步抖动。任何需要在「隐藏状态下仍需准确测量尺寸」的场景,这一区别都会显现出来。
判断三:状态隔离是设计一致性的前提
MobX store 的局部化不只是工程整洁度的问题——它直接决定了「切换页面时 UI 是否有记忆残留」这个用户体验细节。全局 store 虽然方便跨组件共享,但对于「只属于这个视图的 UI 状态」,全局化反而是引入不确定性的来源。

这套系统没有用任何复杂的动画掩盖过渡——它的「感觉好」来自准确,而非视觉修饰。「Interfaces succeed because of hundreds of choices.」7 这句话是 Vercel 写的,但用来形容 Linear 的 Header 同样成立。

封面图:图片来自 Introducing Linear Agent

围绕这条内容继续补充观点或上下文。

  • 登录后可发表评论。