CSS 应该是约束系统吗?

发布: (2025年12月8日 GMT+8 02:42)
10 min read

Source: Hacker News

CSS 很难。布局规则相当复杂,仅凭示例很难掌握。比如“让一个 “” 居中,这可是著名的难题。或者还记得 2000 年代,你会在 A List Apart 上看到各种实现“圣杯布局”的疯狂方法——具体来说,就是一个具有相同高度的页眉、主体和侧栏,以及页脚?鉴于它如此混乱,我们是不是应该把它扔掉,重新用一种完全不同的系统来开始?

我确实觉得自己在这方面有一定的权威。研究生期间,我编写了 CSS 2.2 规范的第一个正式规范;该正式规范通过了(相关片段的)合规性测试。所以我对现有算法相当了解,当然,当我谈论设计师的实际做法或其他系统时,我已经超出了那方面的专业范围。

约束

一种常被提议的 CSS 替代方案是约束系统。在约束系统中,你可以直接写:

(obj.top + obj.bottom) / 2 == (obj.parent.top + obj.parent.bottom) / 2

这行约束obj 的垂直中点必须等于其父元素的垂直中点;换句话说,它约束了 obj 垂直居中。

事实上,这个想法已经被大量探索。在 CSS 中,有著名的约束层叠样式表论文。该论文的思路是,网页作者编写约束(类似上面的),然后浏览器运行一个约束求解器,计算出满足这些约束的每个对象的位置和尺寸。这与普通 CSS 十分类似,只是作者写的是规则,浏览器运行的是布局算法来计算尺寸和位置。自然,两者实际上都要计算除尺寸和位置之外的很多东西:字体、颜色等等。但布局被视为问题的“难点”,我并不反对这一点。

事实上,作者们(尤其是Alan Borning)在约束求解器方面有悠久的历史,特别是与Cassowary 增量约束求解器关联,其中“增量”意味着它不仅可以快速求解约束,还能在页面稍有变化(如响应 JavaScript 或用户操作)时极快地重新求解。真实的浏览器也会这么做。他们的工作相当成功。最显著的例子是 iOS 使用基于 Cassowary 重新实现的约束布局,我听说它相当受欢迎。

约束有什么问题

尽管如此,我并不认为约束系统真的会让 Web 更好。使用像当前 CSS 这样的基于规则的系统时,挑战在于规则本身非常复杂,且很难预测布局会怎样,因为在脑海中执行这些规则几乎是不可能的。但在基于约束的系统中,布局可能会出现欠定超定的情况——也就是说,可能有多个布局满足你的约束,或者根本没有布局满足。

事实上,为 UI 编写既不欠定也不超定的布局约束基本上是不可能的,我也不确定有人真正做到过。即便是上面那个简单的“垂直居中”约束,也没有确定任意一个盒子的大小或位置。它并没有说明外部盒子(obj.parent)应该是容纳子盒子(obj)的最小可能尺寸,或者它们必须都在屏幕内,等等。也许你会再写其他约束来实现这些目标,但约束写得越多,冲突的可能性就越大,导致超定。

处理方式有几种:

  • 欠定布局 – 添加隐式规则(例如盒子必须保持在屏幕内)或优化准则(选取占用空间最少的布局)。
  • 超定布局 – 为每个约束分配权重,并优化以最少破坏约束为目标。

这并不是一种疯狂的系统构建方式——它基本上就是 LaTeX 的工作原理——但在实际操作中,权重、隐式规则的具体形式以及优化准则会成为决定布局外观的关键因素。简单的隐式规则/准则/权重会导致奇怪、糟糕的边缘情况,所以如果你想让布局看起来好看,就需要非常复杂的规则,而这又回到了从样式表难以预测实际布局的困境。

LaTeX 并因其可预测的布局而广受喜爱。虽然约束布局在 iOS 上很流行,但值得注意的是,iOS 只支持少数几种屏幕/窗口形状。人们仍然抱怨约束布局“挑剔、脆弱且不可预测”,调试(尤其是欠定和超定布局)被认为是繁琐且令人恼火的。它也被认为是冗长的;鉴于 UI 设计中有许多约定和模式,能够模块化地表达这些模式会很理想,但约束系统——尤其是当你开始加入隐式规则或优化准则时——显式地具备模块化特性。

我为 Apple 将其推出感到鼓掌,也很高兴人们在使用它并解决了自己的问题。LaTeX 解决了我的问题!新一代学生对Typst显得非常兴奋,我还没尝试过。但我确实认为,在约束系统中工作的设计师会遭遇理论上预期的那类问题。

根本问题是什么?

那么,为什么这如此困难?为什么每当我们做布局时都会出现各种奇怪的边缘情况?我认为问题在于布局本身真的很难。

这里,我给你一个例子。下面是我在CSS 2.2 正式语义中对 text-align: center 的规范:

[(is-text-align/center textalign)
 (= (- (left-outer f) (left-content b))
    (- (right-content b) (right-outer l)))]

这段代码的含义是:如果容器 b 指定了 text-align: center,则它的左间距(第一个子元素 f 的左外边缘位置减去容器 b 的左内容边缘)等于它的右间距(使用右边缘、最后一个子元素 l,并且减法顺序相反)。这本身就是一个约束!

但如果你往上看几行(见此处),会发现,在真正应用这个约束之前,我们会先检查容器是否足够大以容纳内容;如果不够大,我们会无论如何都左对齐。

什么?真的?是的。这是 CSS 语义中的一个古怪细节(参见CSS 2.1 第 9.4.2 节)。对此确切怪癖的控制标准现在是CSS Text Level 3,它清晰地说明:如果文本在一个太小的盒子里居中,我们不希望它从左边缘溢出(因为可能会超出屏幕,用户无法滚动);左对齐可以确保它只会从右边缘溢出。

这是一种古怪的细节,但你可能从未注意到它;如果注意到了,这个边缘情况可能比布局在没有它时的表现要好。换句话说,把这个边缘情况写进 text-align 的定义,是 CSS 设计者的明智选择——将经过艰苦实践得来的设计智慧嵌入直观规则中,而人们大多数情况下都能毫无问题地使用这些规则。

Back to Blog

相关文章

阅读更多 »

滚开联系页面

请提供您希望翻译的具体摘录或摘要文本,我才能为您进行翻译。