🎨 卷① 现代 CSS 布局与视觉
20 条。适用场景:这个布局用现在的 CSS 该怎么写,而不是用五年前的办法。
「要点」「我的补充」为本人撰写,非原文摘录。
一、布局基础:两份该收藏的完整指南
1. A Complete CSS Flexbox Layout Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:Flexbox 的属性你记不住哪个加在容器、哪个加在子项——这份是查表用的。
要点
- Flexbox 是一维布局:主轴由
flex-direction决定,交叉轴垂直于它,所有对齐属性都是相对这两个轴而言 - 容器属性(
justify-content、align-items、flex-wrap)与子项属性(flex-grow、flex-shrink、flex-basis、align-self)分工明确,混淆是最常见的错误 flex: 1是flex: 1 1 0%的简写,与flex: auto(1 1 auto)行为不同——后者会考虑内容尺寸
我的补充:最经典的坑是 flex 子项不收缩到比内容更小:flex-shrink: 1 也拦不住,因为默认 min-width: auto。文本溢出撑破容器时加 min-width: 0(或 overflow: hidden)就好,这一条能省你几个小时。另外要垂直居中现在直接 display: flex; align-items: center; justify-content: center 就够,不需要 transform 那套老办法。
2. A Complete CSS Grid Layout Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:需要二维布局(行列同时控制)时,Grid 的术语和语法一次查清。
要点
- Grid 是二维布局,能同时定义行与列,这是它与 Flexbox 的根本分野
grid-template-areas用字符画的方式描述布局,可读性极好,适合页面级框架minmax()、auto-fit/auto-fill、fr单位组合起来能做出无需媒体查询的响应式网格
我的补充:选择判据很简单:一个方向排列用 Flexbox,需要行列对齐用 Grid。最实用的一行是 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))——卡片列表自动按容器宽度换行,完全不用写断点。这个博客的多个页面用的就是它。auto-fit 与 auto-fill 的区别在于空轨道是否折叠,多数情况你想要 auto-fit。
3. CSS Container Queries
来源:CSS-Tricks · css-tricks.com 读它解决什么:组件需要根据自己所在容器的宽度变样式,而不是视口宽度。
要点
- 媒体查询问的是"视口多宽",容器查询问的是"我的父容器多宽",后者才是组件化的正确粒度
- 用法分两步:容器上声明
container-type: inline-size,子元素里写@container (min-width: 400px) container-type会创建包含块并影响布局,声明后容器的尺寸计算方式会变
我的补充:这是组件库开发的分水岭——同一个卡片组件放在侧栏和主区域能自动适配,不必再靠外部传 size prop。两个坑:一是 container-type: size 需要显式高度否则塌陷,多数情况该用 inline-size;二是容器查询不能查询自身,必须查父容器,所以要多包一层。配合 container-name 可以指定查询哪一级。
4. CSS Anchor Positioning Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:做下拉菜单、tooltip、弹出框时,想让元素相对另一个元素定位而不用 JS 算坐标。
要点
- 锚点定位让元素声明"我锚定在某个元素上",浏览器负责算位置,替代了 Popper.js 这类库的核心职责
anchor-name声明锚点,position-anchor与anchor()函数消费它position-try-fallbacks处理边缘溢出:空间不够时自动换到备选位置
我的补充:这个特性能删掉前端项目里相当可观的一坨代码——tooltip 定位逻辑历来是 bug 温床(滚动、resize、溢出翻转都要手写)。但浏览器支持还不均衡,Safari 跟进较慢。落地策略是渐进增强:用 @supports (anchor-name: --x) 包起来,不支持时退回原来的 JS 方案或简单的 position: absolute。这个博客的 tooltip 目前还是 JS 实现的。
5. Cascade Layers Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:样式被覆盖了却找不到是谁覆盖的——级联层能从根上治这个病。
要点
@layer显式声明优先级顺序,层内的选择器权重不会越过层的边界- 由此可以把第三方库放进低优先级层,自己的样式放高优先级层,不必再靠提高选择器权重或
!important打架 - 声明顺序决定层优先级,未分层的样式优先级最高
我的补充:最大价值是终结 !important 军备竞赛。实践模式:@layer reset, vendor, base, components, utilities; 一次定好顺序,之后 @layer components { ... } 往里填。引入第三方 CSS 时用 @import url(...) layer(vendor),它的样式就永远压不过你的。注意未分层样式优先级最高这点很反直觉,迁移中的项目要留意。
6. CSS Custom Properties Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:CSS 变量的作用域、继承与回退规则,一次搞清。
要点
- 自定义属性是运行时的,会继承、能被 JS 读写,这与 Sass 变量(编译期替换)本质不同
var(--x, fallback)的回退只在变量未定义时生效,定义了但值非法会走"无效时的保证值"(unset)- 可以用
@property注册类型,从而让自定义属性支持动画过渡
我的补充:主题切换的标准做法就是它::root 定义一套,[data-theme="dark"] 覆盖,切换只需改一个属性。未注册的自定义属性无法过渡动画——想让颜色渐变必须用 @property { syntax: '<color>'; } 注册。JS 侧读取用 getComputedStyle(el).getPropertyValue('--x'),注意返回值带前导空格,要 .trim(),这个坑我踩过。
7. CSS Media Queries Guide
来源:CSS-Tricks · css-tricks.com 读它解决什么:媒体查询除了宽度还能查什么——有些能力被长期忽略。
要点
- 除了
width,还能查prefers-color-scheme(深色偏好)、prefers-reduced-motion(减少动画)、hover(是否支持悬停)、orientation、dynamic-range - 区间语法(
@media (400px <= width <= 800px))比min-width/max-width组合可读性好得多 prefers-reduced-motion是可访问性要求而不是可选项
我的补充:两条应该成为默认习惯。一是所有动画外面包一层 @media (prefers-reduced-motion: no-preference),或者反过来在 reduce 时禁用——前庭功能障碍用户会因为视差滚动产生眩晕。二是用 @media (hover: hover) 包住 hover 效果,否则触屏上会出现"点一下之后样式卡住"的诡异状态。这两条改起来很便宜,收益却实在。
8. CSS Color Functions
来源:CSS-Tricks · css-tricks.com 读它解决什么:需要在 CSS 里生成配色(变亮、变暗、混合),不想靠预处理器。
要点
- 现代颜色函数支持更宽的色域与更符合感知的色彩空间:
oklch()、lab()比hsl()更适合做等亮度调色 color-mix()直接在 CSS 里混合两色,light-dark()按主题给两个值- 相对颜色语法可以基于一个基色派生整套(改亮度、改饱和度)
我的补充:oklch() 的实际好处是同一亮度值在不同色相上看起来真的一样亮——用 hsl() 做主题色时,黄色和蓝色在相同 L 值下亮度感知差很多,这是配色难调的技术原因。落地建议:主色用 oklch() 定义,派生色用 color-mix() 或相对颜色语法,比手工列一堆十六进制好维护。老浏览器降级用 @supports 或直接给两次声明。
9. CSS Length Units
来源:CSS-Tricks · css-tricks.com 读它解决什么:vh 在手机上不对、em 和 rem 什么时候用哪个——单位的选择有明确依据。
要点
em相对当前元素字号(会嵌套累积),rem相对根字号(稳定),做间距体系用rem更可控- 视口单位家族要分清:
vh是大视口高度,svh(小)、lvh(大)、dvh(动态)针对移动端浏览器工具栏伸缩 ch、ex与字体度量相关,适合排版场景(比如限制正文每行字符数)
我的补充:移动端 100vh 撑出滚动条的经典问题,正解是 100dvh(动态视口高度,随地址栏收放变化),以前只能用 JS 监听 resize 写 CSS 变量。注意 dvh 会导致滚动时布局变化,做全屏遮罩用 100dvh,而固定高度区块用 100svh 更稳。正文宽度限制推荐 max-width: 65ch,比固定 px 值在不同字号下都合适。
10. CSS Selectors
来源:CSS-Tricks · css-tricks.com 读它解决什么:选择器除了类和标签还有一大批被忽略的,尤其新的函数式伪类。
要点
:is()与:where()都能合并选择器列表,区别是:where()权重为零,:is()取列表中最高权重:has()是"父选择器":.card:has(img)选中包含图片的卡片,这是长期缺失的能力:not()现在支持复杂选择器列表
我的补充::where() 是写库时的关键工具——用它包住默认样式,使用者不必提高权重就能覆盖。:has() 的实际用法比想象中多:form:has(:invalid) 给含错误的表单加样式、body:has(dialog[open]) 打开弹窗时锁滚动,都能省掉 JS 加 class 的往返。性能上 :has() 已经不是问题,但别写 :has(* * *) 这种大范围匹配。
二、动画与滚动
11. A First Look at Scroll-Triggered Animations
来源:CSS-Tricks · css-tricks.com 读它解决什么:元素滚动进视口时播放一次动画,想不用 IntersectionObserver。
要点
- 滚动触发(trigger)与滚动驱动(driven)是两件事:前者进入视口就播完整动画,后者动画进度跟着滚动位置走
- 触发式更符合"入场动画"的直觉需求,而此前 CSS 只提供了驱动式
- 纯 CSS 实现意味着动画在主线程之外,不受 JS 阻塞影响
我的补充:这正好解决了 IntersectionObserver 方案的老问题——JS 加载完之前元素停在初始态(通常是 opacity: 0),网络慢时用户看到大片空白。CSS 方案没有这个窗口。但要记得配 prefers-reduced-motion 降级,入场动画是最容易引起不适的一类。渐进增强写法:默认可见,@supports 里再加动画。
12. Using Scroll-Driven Animations for Opposing Scroll Directions
来源:CSS-Tricks · css-tricks.com 读它解决什么:想做两个元素向相反方向移动的滚动效果(视差类)。
要点
- 滚动驱动动画把时间轴换成滚动进度:
animation-timeline: scroll()或view() - 反向运动靠让两个元素在同一时间轴上跑相反的关键帧
scroll()跟踪滚动容器的整体进度,view()跟踪元素在视口中的可见进度,用途不同
我的补充:这类效果以前必须写 JS 监听 scroll 事件,而 scroll 事件在主线程上、高频触发,是滑动卡顿的常见来源——即使加了节流也会掉帧。CSS 版本跑在合成线程上,流畅度完全不同。视差效果是 prefers-reduced-motion 的首要适用对象,务必降级。判断支持:@supports (animation-timeline: scroll())。
13. Scroll-Driven, Scroll-Triggered, Scroll States, and View Transitions
来源:CSS-Tricks · css-tricks.com 读它解决什么:这几个名字相近的滚动特性到底各管什么,选型时不要混。
要点
- 四者定位不同:驱动(进度绑定滚动)、触发(进入即播)、滚动状态(
@container scroll-state()查询是否吸顶等)、视图过渡(页面/状态切换的连续动画) - 名字相似但适用场景几乎不重叠,混用会写出别扭的实现
- 滚动状态查询解决的是"元素
position: sticky吸顶时换个样式"这类需求
我的补充:这条值得当选型清单用。@container scroll-state(stuck: top) 特别实用——吸顶导航栏在吸住时加阴影,以前必须用 IntersectionObserver 配一个哨兵元素才能做到,是个人尽皆知的丑陋 hack。现在一条 CSS 搞定。这个博客的导航栏也可以这么改。
14. The "Most Hated" CSS Feature: cos() and sin()
来源:CSS-Tricks · css-tricks.com 读它解决什么:需要在 CSS 里做圆形排布、波形等几何计算。
要点
- CSS 已支持三角函数,配合
calc()与自定义属性可以在样式层完成几何布局 - 典型用途是把 N 个元素均匀排在圆周上,此前必须靠预处理器循环或 JS 生成
- 争议点在于可读性:数学表达式塞进 CSS 后维护成本上升
我的补充:配合 sibling-index() 才是完整形态——无需给每个子元素手写 --i,CSS 自己能拿到序号。做圆形菜单、雷达图刻度这类需求现在纯 CSS 可行。但别过度:复杂几何放 SVG 里更合适,CSS 三角函数的甜区是"简单规律的重复排布"。写的时候留一行注释说明公式,否则半年后没人看懂。
15. Animating CSS border-image
来源:CSS-Tricks · css-tricks.com 读它解决什么:想做渐变边框、动态描边效果。
要点
border-image能用图片或渐变填充边框,比border-color表达力强得多- 渐变边框的传统做法是双层背景加
background-clip,border-image更直接 - 动画化需要注意可过渡的属性有限
我的补充:渐变边框有三种做法,各有代价:border-image(简单但不支持圆角)、双层 background + background-clip: padding-box, border-box(支持圆角,代码略绕)、伪元素叠加(最灵活但要管层级)。要圆角渐变边框就用双层背景那套,这是目前最可靠的方案。新特性 border-shape 值得关注,能解决圆角限制。
16. Get Ready For the Powerful CSS border-shape Property
来源:CSS-Tricks · css-tricks.com 读它解决什么:需要非矩形的边框形状(斜角、缺口、异形卡片)。
要点
border-shape让边框跟随任意形状,而不是只能圆角矩形- 此前做异形卡片只能用
clip-path,但它会裁掉阴影和边框,效果受限 - 有了它,异形元素也能有正常的边框与阴影
我的补充:clip-path 裁掉 box-shadow 这个限制困扰了很多设计实现——票券式缺口卡片、斜切卡片都只能靠伪元素拼或者直接上 SVG 背景。这个属性还很新,现在别用在生产,先关注。当下要做异形卡片仍推荐 SVG 或者 mask 配合额外的阴影层。
三、细节与实践
17. Gap Decorations Are Now Available
来源:CSS-Tricks · css-tricks.com 读它解决什么:想在 Grid/Flex 的间隙里画分隔线,不想加空 div。
要点
- 间隙装饰允许直接给
gap区域绘制线条,语法类似border - 此前的做法是给子项加边框再修掉首尾,或者插入分隔元素——两者都脆弱
- 与
gap联动,子项数量变化时自动正确
我的补充:这个需求太常见了:卡片网格中间要细分隔线,换行时首尾不能多画。老办法(:not(:last-child) 加 border)在换行的 Grid 里就是错的,因为行末元素并不是 :last-child。这个特性直接消掉那类 hack。同样属于比较新的能力,用 @supports 保护。
18. Styling Counters in CSS
来源:CSS-Tricks · css-tricks.com 读它解决什么:需要自定义列表编号(章节号、多级编号、非数字序号)。
要点
- CSS 计数器(
counter-reset、counter-increment、counter())独立于 HTML 的<ol>,可以给任意元素编号 @counter-style可以定义自己的编号系统(符号、汉字、循环)- 多级编号靠
counters()(复数形式)拼接
我的补充:技术文档的"1.2.3"式多级标题编号可以纯 CSS 实现,作者写 Markdown 时完全不用手写编号——这对长文档维护是质的差别,插入一节不必重编后面所有号。用 counters(section, ".") 拼接。中文场景可以用 @counter-style 定义"一、二、三",比 list-style-type: cjk-ideographic 更可控。
19. Dark mode toggles: two states are enough
来源:CSS-Tricks · css-tricks.com 读它解决什么:深色模式切换该做两态还是三态(浅/深/跟随系统)。
要点
- 三态设计(含"跟随系统")增加了 UI 复杂度,而多数用户的实际需求是"现在给我我想要的那个"
- 两态方案的关键是默认跟随系统,用户手动切换后才记住偏好
- 涉及
prefers-color-scheme、localStorage与首屏闪烁的处理
我的补充:这条与这个博客直接相关。两态实现的核心难点是避免首屏闪白(FOUC):主题判断脚本必须内联在 <head> 且在 CSS 之前执行,不能放进打包后的 JS——否则用户会看到浅色一闪。VitePress 已内置处理好了。另外 color-scheme: light dark 这个属性别忘了,它让浏览器的滚动条、表单控件也跟着变色。
20. 5 CSS Properties You Should Know for Better Text Designs
来源:Frontend Masters · frontendmasters.com 读它解决什么:正文排版看起来不专业,但不知道该调什么。
要点
- 排版质量往往取决于几个不常用的属性:
text-wrap: balance(标题换行均衡)、text-wrap: pretty(避免孤字)、hanging-punctuation、text-box-trim(裁掉字体的额外行高空间) line-height建议用无单位值(如1.5),带单位会导致子元素继承计算值出错- 中文排版还要考虑
word-break与标点挤压
我的补充:text-wrap: balance 用在标题上是立竿见影的改善——避免出现"最后一行只有一个词"的难看换行,一行 CSS 换来明显的观感提升。正文用 text-wrap: pretty(代价是排版计算稍多)。中文场景注意 text-wrap: balance 有字符数上限,长段落无效。这个博客的文章标题可以直接加上。
本卷小结
三条最值钱的:
- flex 子项溢出加
min-width: 0。默认min-width: auto让它不收缩到比内容小,这是最高频的 Flexbox 疑难。 - 移动端全屏用
100dvh不用100vh。地址栏伸缩造成的多余滚动条问题,一个单位就解决。 @layer终结!important军备竞赛。第三方 CSS 用@import url(...) layer(vendor)引入,永远压不过你自己的样式。
