CSS:难以避免的缺陷
2026 年 6 月 4 日发布的这篇文章,是面向需要对网页进行样式设计、但并非专业网页开发者的 CSS 简易教程。作者表示自己既没时间,也缺乏相关经验,更愿意读一本相关的书,只能通过在 MDN 上搜索来学习这些知识,所以把目前学到的内容记录下来,认为或许有一定价值。
CSS、HTML 和 Web API 的知识体系庞大,成为专业人士需投入大量时间和精力。不过,现代网页有一个规模适中、容易学习的子集,足以应付编程博客或简单 GUI 这类简单任务。作者还没找到专门讲解这个子集的资源,但认为自己摸索也不算太难。然而,CSS 存在一些棘手的陷阱,会破坏页面效果,且可能不易察觉,调试起来可能需花费好几天时间。不过作者对本网站的样式挺满意,所用的 CSS 代码约 200 行,可读性不错。
优点:HTML5 语义化标签名
值得浏览 MDN 的元素参考。HTML 元素数量不多,像 `main`、`article`、`nav`、`kbd` 等标签能让页面结构搭建更轻松。还有一些不太明显的用法,如使用 `ul` 来创建各种列表,像 `header > nav` 里的网站板块列表;用 `details` 来实现目录;用 `dl`/`dt` 来创建成对的列表。
缺点:包装元素
查看“真正”网站的源代码,会发现内容都被一层又一层的包装元素包裹着,这可能让人误以为使用包装元素是解决布局问题的方法。作者因未编写过“生产级”的 CSS,无法明确表示赞同或反对,但根据经验,反其道而行之,即只使用具有语义的标记标签,然后为现有标记编写合适的 CSS,会更容易理解。
缺点:布局
布局问题在每个 GUI 框架中都是难题。想象一张固定大小的光栅图像和一段描述它的文字,在屏幕矩形区域内有多种排列方式。一般来说,只要总面积足够,对于给定的宽度和高度都能处理得不错。典型的 GUI 由盒子层层嵌套组成,每个盒子有很大“布局自由度”,但每个盒子的布局会影响其他盒子,因为通常希望所有盒子完美贴合,既无间隙也不重叠。重要的是,不存在通用的布局算法,不同系统使用不同的启发式方法,从简单的 RectCut 到功能全面的约束求解器,以及介于两者之间的各种方法。要全面理解布局工作原理并不容易,所以应思考“这个系统允许哪些可能的布局”,而非“在某个系统中如何实现我的布局”。
缺点:浏览器默认样式
一篇没有任何 CSS 的纯 HTML 博客文章,在浏览器中打开会有一些样式,如文本有特定颜色、字体和大小,标题比正文大,链接带有下划线等,这些是浏览器的默认样式,很有用。但不同浏览器的默认样式存在差异,即使添加了自己的 CSS 且在自己浏览器中显示不错,别人看到的可能不同,因为可能不知不觉依赖了某个浏览器的默认样式,且问题出在未编写的代码上。
通用的解决办法是使用 CSS 重置或规范化,在 CSS 开头添加一组明确的规则,覆盖默认样式,这不是因为默认样式本身不好,而是因为它们不一致。作者不清楚实际中需要覆盖哪些规则,认为比较几个现有的 CSS 重置方案是个不错的主意。
这引出一个重要问题:是否应该对网页进行样式设计?对于 Web 平台存在两种观点,一些人将其视为灵活、自适应的视觉设计表达媒介,另一些人希望 Web 专注于内容传递,让用户自定义展示方式。作者个人答案比较务实,认为默认情况下,未样式化的页面可用性差且外观不佳,希望无 CSS 的页面能直接阅读,但现实中对内容进行样式设计很有帮助,同时允许高级用户使用自己的 CSS 也是好主意。要确保 HTML 标记合理,不要让 HTML 过度适配 CSS,且要保证页面在阅读器模式下能正常显示。
优点:无类 CSS
无法将样式完全重置为中性状态,即使把文本设置为不可见,这也是一种样式。所以不妨接受它,重置样式后,直接对常见的 HTML 元素进行样式设置。例如,为所有代码片段设置喜欢的字体。如果使用 `main`、`header`、`footer`、`nav` 标签,就可在不编写任何 CSS 选择器的情况下设置页面的整体布局。当然,这需要在 CSS 中对 HTML 的结构做出一些假设,但这是自己的 HTML 和 CSS,可随意操作,不满意可随时修改。
缺点:CSS 选择器
在编程领域,人们逐渐对继承持谨慎态度,更倾向于使用组合。默认的 CSS 类似超级强化版的继承,网页上的每个设计元素都会受多条规则影响,还可通过在 CSS 中追加规则来“修补”现有元素。CSS 的功能与实际想做的事情存在差距。有两种合理的方法:一是认为 CSS 选择器在错误方向上增加了抽象能力,坚持使用无类 CSS 和内联样式,可使用类似 Tailwind 的工具让内联样式编写更美观,使用类似 JSX 或其他支持组合的模板引擎来避免 HTML 中的重复代码;二是使用 CSS 嵌套来避免编写“影响范围过大”的选择器,按组件进行样式设计。
缺点:box - sizing
UI 由递归的矩形组成,布局是确定每个矩形位置的过程,这取决于矩形本身的大小。默认情况下,HTML 中大小的定义不符合直觉,元素的宽度和高度不包括元素的边框和内边距,会导致意外结果,增加内边距时整个布局会意外偏移。因此,`* { box - sizing: border - box; }` 应成为 CSS 重置的第一行代码,它能让元素具有封装性,添加边框只对局部产生影响。
好坏参半:外边距合并
想在一个元素周围留出 `8px` 的间隙,设置内边距属性会使相邻元素间间隙变大,因为内边距会叠加。而 `margin` 属性的工作方式类似社交距离规则,相邻的两个外边距会取最大值而不是相加。外边距合并很有用,但也可能让人意外,作者觉得子元素的外边距可能会超出父元素,对外边距的理解还不够直观,但能识别什么时候是外边距的问题。
外边距也是作者写这篇文章的间接灵感来源之一。Julia Evans 在文章中写道,通常不应在元素上设置外边距,而应让父元素使用猫头鹰选择器来控制子元素之间的外边距,即给 `section` 的除第一个子元素之外的所有子元素添加外边距。作者之前不知道这个方法,能理解这样做的原因,但烦恼于不成为“专业”的网页开发者或不逆向工程别人的 CSS 框架,就无法学到这些知识。
缺点:默认(流式)布局
布局是个棘手问题,因为没有通用的“布局算法”,只有一堆特殊情况。HTML 实际的默认布局算法可追溯到其作为文档语言的起源,过于针对生成纸质文档的场景,主要是带有插图的文本内容,文本可环绕图片排列,这对博客正文合适,但想真正控制页面上元素的空间排列,就需要其他方法。
优点:Flexbox
这是现代网页开发与过去的区别所在。过去实现“这个元素放在左边,那个元素放在右边”的布局,可能需要拥有 CSS 博士学位或使用复杂的 CSS 框架。Flexbox 布局允许将一系列元素垂直或水平排列,并根据可用空间进行自适应调整,它比较复杂,作者每次使用都得参考 MDN,但最终通常能实现想要的效果。
缺点:响应式设计
现代 CSS 允许查询屏幕尺寸并基于此实现条件逻辑,是一种能“响应”用户代理约束的设计。但 HTML 本身就具有响应式特性,与 PostScript 不同,改变窗口大小时,HTML 会自动重新排列段落。所以最好避免编写显式的响应式规则,依靠布局来实现合理效果,如这个博客在手机、平板和桌面设备上都能正常显示,无需任何显式的 `@media` 查询,只需无条件地为文本主列设置 `max - width` 即可。
亦正亦邪:像素
`1px` 能实现想要的效果,但它并非屏幕上一个物理像素的大小,而是一个视角的度量单位,在任何屏幕上的视觉效果应相同,会根据屏幕尺寸、像素密度和典型的观看距离转换为不同数量的物理像素。所以可直接用像素设置所有元素的大小,不必考虑不同显示器的像素密度。更奇怪的是,CSS 允许使用厘米或英寸等“实际”单位,但它们实际上也是角度单位,因为所有单位都是以像素为基础定义的。
极其糟糕:字体大小
Flexbox 是布局 UI 元素的好方法,流式布局在排列段落文本方面也还不错,但在单个行和字形层面上情况一团糟,对新手是个陷阱。写 `font - size: 16px` 时,`16px` 没有明确的对应物,它是字形周围虚拟盒子的大小,但盒子不贴合字形,且字形大小因字体而异。幸运的是,`font - size - adjust` 属性可解决这个问题,让 `font - size` 在不同字体间保持一致。不过,目前 `font - size - adjust` 比较小众,作者个人会把 `font - size - adjust: ex - height 0.53;` 和 `box - sizing` 放在一起使用,但很少有页面这么做。
`font - size` 的另一个问题是默认值的棘手问题。好消息是,它在不同浏览器中比较一致,`16px` 是绝大多数浏览器的默认值。坏消息是,根据字体不同,`16px` 可能显得比较小,接近可读的下限,一些默认字体特别小,如在苹果设备上,`font - family: serif` 看起来比 `sans - serif` 小很多,16px 的字号阅读起来几乎让人不舒服。
可以直接将 `font - size` 设置为 `18px` 或其他适合所选字体的值,但需要注意一些事项。现代浏览器支持两种放大页面文本的方式,在 CSS 中设置 `font - size` 会禁用修改默认字体大小这种方式。综合考虑,不要认为页面上的文本默认就能清晰可读,要检查不同的配置,设置 `font - size - adjust` 以减少变量,明确 `font - size` 的含义。如果使用所选或用户默认的字体和默认的 `16px` 字号时页面效果不错,那就没问题,否则将 `font - size` 设置为更大的值,之后还要检查页面在阅读器模式下是否可读。
缺点:行高
尽管名字叫“行高”,但 `line - height` 不是设置一行的高度,而是在相同字体下一组字形的高度。当所有文本使用相同字体时,两者一致,但如果有单词使用 `monospace` 字体,就会出现意外情况。虽然 `font - size - adjust` 可修复字形在盒子内的大小,但仍未明确字形的相对位置,所以当两行不同字体的文本垂直对齐以共享基线时,它们的行高框会相对偏移,整体行高会比预期的大,详细解释可参考相关文章。
缺点:垂直韵律
在网上搜索相关问题,会遇到垂直韵律的概念,即应确保不同段落中的行处于相同的相对位置,即使有标题、图片等元素,就好像网页后面有一张隐形的方格纸。但据作者所知,这纯粹是一种玄学,没什么实用性。对于双栏布局,希望两侧的行对齐有道理,但对于单栏布局,为实现这个效果而大费周章没有意义。
缺点:`word - break`
流式布局的精妙之处在于动态性,窗口变窄时,文本能自动整齐地换行。但这种魔法有局限性,只能在空格或连字符处换行,一些长字符串,如 `内联代码` 或 URL,可能无法换行,会导致在移动设备上出现溢出问题,且只有在发布作品后才会注意到。没有万能的解决方法,相关文章中有详细介绍。
作者表示目前能想到的就这些,再次呼吁有人能写一本 100 页左右的简短书籍,讲解足够的 HTML 和 CSS 知识,让大家能轻松搭建一个简单的博客,而不会被外边距问题搞得焦头烂额。