CSS 架构与动画体系

共 61 题
📑 题目列表 61 题
#
★★★

1. PostCSS 插件链(autoprefixer、postcss-preset-env)

PostCSS 插件链如何工作?autoprefixer 与 postcss-preset-env 在工程中的作用与差异是什么?

  • PostCSS 用插件处理 CSS 语法
  • autoprefixer 按浏览器列表加厂商前缀
  • preset-env 编译新语法并聚合常用插件

PostCSS 是一个用 JS 插件转换 CSS 的工具链:插件把 CSS 解析为 AST 后做变换,再序列化输出。autoprefixer 根据 browserslist 配置自动为属性添加厂商前缀(如 -webkit-);postcss-preset-env 则将现代 CSS 语法(嵌套、逻辑属性、颜色函数等)编译为目标浏览器可理解的语法,并内置 autoprefixer 等能力。差异:autoprefixer 聚焦前缀,preset-env 聚焦新特性语法降级。工程上二者常配合,同时控制 browserslist 决定编译目标,配合 Lightning CSS 等可实现更快的同类能力。

考察 PostCSS 插件机制与两个核心插件的能力分工,属 CSS 构建工程题。回答时说明 PostCSS 插件机制与 browserslist 驱动,preset-env 管新语法、autoprefixer 管前缀的分工。

module.exports = {
  plugins: {
    'postcss-preset-env': { stage: 2 },
    autoprefixer: {}
  }
};
#
★★★

2. Sass/Less 变量的编译期作用域与 CSS 自定义属性运行时继承的差异

Sass/Less 变量与 CSS 自定义属性在作用域与求值时机上有什么本质差异?

  • 预处理器变量编译期替换
  • 自定义属性运行时继承与覆盖
  • 动态主题只能靠自定义属性

Sass/Less 变量在编译期被替换为具体值,作用域由源码块决定,编译后变量不复存在,无法在运行时改变;CSS 自定义属性保留在 CSS 中,沿 DOM 树运行时继承,可在 :root 或任意元素上动态覆盖,值在计算时展开。本质差异是求值时机:编译期静态 vs 运行时动态。因此响应式、主题切换、媒体查询内分支等动态场景只能依赖自定义属性;而编译期变量适合尺寸、数值等静态常量,可被预处理器用于控制流(循环、条件)与数学运算。

考察两类变量的编译期/运行时差异及适用场景,是样式架构核心题。回答时以编译期静态替换与运行时继承覆盖的对比为核心,说明动态主题只能依赖自定义属性。回答时按求值时机对比展开更清晰。

$spacing: 8px;  /* 编译期 */
:root {
  --gap: 8px; /* 运行时 */
}
#
★★★

3. Tailwind v4 的 CSS-first 配置(@theme)与 @apply 演进

Tailwind v4 的 CSS-first 配置如何工作?@theme 与 @apply 相比 v3 有什么演进?

  • @theme 在 CSS 中定义设计令牌
  • 配置从 JS 迁移到 CSS
  • @apply 支持更多指令与层级

Tailwind v4 采用 CSS-first 配置:用 @theme 在 CSS 中声明设计令牌,如 @theme { --color-brand: #0af; },编译器自动生成对应的工具类与 CSS 变量,替代 v3 的 tailwind.config.js 中 theme 对象。@apply 演进为可引用 @theme 变量、支持 CSS 变量插值,并可在 @layer 等结构中更规范地组合工具类。收益:配置与样式同源、IDE 跳转更直接、体积按需生成;边界:复杂逻辑(插件、内容扫描)仍需 JS 配置,迁移时需同步改写既有配置。

考察 Tailwind v4 CSS-first 配置的机制与 @apply 的演进,属原子化 CSS 工程题。回答时说明 @theme 生成工具类与变量、@apply 支持变量插值,迁移时注意 v3 配置与插件差异。

@theme {
  --color-brand: #0af;
  --spacing-xl: 2rem;
}

.btn {
  @apply bg-brand px-xl rounded;
}
#
★★★

4. composes 与 CSS Modules 共享样式的循环依赖风险

CSS Modules 的 composes 跨文件共享样式时可能产生什么循环依赖风险?如何治理?

  • composes from 跨文件引用类
  • 文件间相互 composes 形成循环
  • 按依赖方向分层与 lint 约束

composes 支持跨文件复用样式,如 composes: btn from './btn.module.css'。风险在于文件间相互 composes 会形成循环依赖,构建工具解析时可能报错或产生不确定结果;隐式耦合也让组件样式的依赖关系难以追踪。治理方式:规定 composes 只依赖基础/通用样式层,禁止业务组件互相 composes;将可复用样式抽到独立 tokens 或 base 模块;用构建插件或 lint 规则检测循环引用;必要时改用 CSS 变量、@layer 或类组合方式替代,保持依赖方向清晰。

考察 composes 跨文件复用的循环依赖风险与治理,属 CSS Modules 工程题。回答时说明跨文件相互 composes 形成循环依赖,治理上分层引用并加 lint 检测循环。

#
★★★

5. CSS Variable 的运行时覆盖(element.style.setProperty)与 SSR 水合一致性

运行时用 setProperty 覆盖 CSS 变量如何与 SSR 水合保持一致性?

  • 服务端渲染初始变量
  • 水合时避免样式闪烁
  • 客户端覆盖需与初始值一致

SSR 场景下,服务端返回的 HTML 应携带初始主题/尺寸变量(在

考察 CSS 变量主题与 SSR 水合一致性的配合,属服务端渲染与样式交叉题。回答时说明服务端输出初始变量、首帧前同步覆盖偏好的三态主题策略,避免水合闪烁。回答时补充内联脚本首帧设置的做法。

document.documentElement.style.setProperty('--bg', theme.bg);
#
★★★

6. @scope (--name) 局部作用域与原生 Shadow DOM 作用域样式的对比

@scope (--name) 命名作用域与 Shadow DOM 的样式隔离有什么异同?如何选型?

  • @scope 提供命名作用域
  • Shadow DOM 提供树边界隔离
  • 隔离强度与灵活性不同

@scope (--name) 为样式定义命名作用域,规则只作用于作用域根的后代,隔离基于选择器范围;Shadow DOM 则创建独立的 DOM 树边界,样式默认不穿透、也不被外部影响,隔离更强。差异:Shadow DOM 隔离样式来源与选择器,事件与表单行为也可能受边界影响;@scope 不改变 DOM 结构、外部样式仍可达,更轻量。选型:需要强封装与第三方隔离用 Shadow DOM;只需控制选择器作用范围、希望保持全局样式可达时用 @scope;也可结合,Shadow DOM 内部再按需使用 @scope。

考察两种作用域机制的隔离强度与适用边界,属组件样式隔离题。回答时说明 @scope 选择器级隔离与 Shadow DOM 树边界隔离的强度差异,按封装需求选型。回答时说明轻量场景优先 @scope 的结论。

#
★★★

7. vanilla-extract 与 Panda CSS 在零运行时 CSS-in-JS 的 SSR 注水一致性与构建产物体积对比

vanilla-extract 与 Panda CSS 如何保证零运行时与 SSR 水合一致?构建产物体积有什么差异?

  • 编译期生成静态 CSS
  • 类名与服务端一致避免水合错位
  • 按需生成体积可控

vanilla-extract 与 Panda CSS 都在构建期把 JS/TS 中定义的样式编译为静态 CSS 文件与类名,运行时无注入开销,因此 SSR 输出的 HTML 携带的类名与客户端一致,不存在运行时样式注入导致的注水差异。体积方面:两者都按使用生成样式,可 tree-shaking;vanilla-extract 产物接近手写 CSS 的紧凑度,Panda CSS 支持原子化与 recipe 系统,同样按需产出。差异在于 API 与生态:vanilla-extract 更贴近 CSS Modules 心智,Panda 提供样式属性与变体系统,选型取决于团队习惯与组件库需求。

考察两类零运行时 CSS-in-JS 的 SSR 一致性与体积特征,属 CSS 方案选型题。回答时说明两者编译期生成静态 CSS、类名 SSR 一致、按需生成,差异在 API 与生态。

#
★★★

8. UnoCSS 的预设与 shortcuts 与 Tailwind v4 的 @theme 在原子化灵活度与生态兼容性上的取舍

UnoCSS 的预设/shortcuts 与 Tailwind v4 的 @theme 在原子化方案的灵活度与生态上如何取舍?

  • UnoCSS 预设机制高度可扩展
  • shortcuts 组合原子类
  • Tailwind 生态成熟、v4 CSS-first

UnoCSS 以预设(presets)为核心,可自由组合原子化、图标、动画等能力,shortcuts 允许把多个原子类组合为语义类,灵活度极高、可按需扩展;Tailwind v4 采用 CSS-first 的 @theme 定义令牌并生成工具类,配置直观、文档与生态成熟,团队上手成本低。取舍:追求极致灵活与自定义规则、已有组件库需要深度定制时选 UnoCSS;需要稳定生态、大量现成组件与文档、低定制需求时选 Tailwind。二者都按需生成,体积表现接近,核心差异在扩展模型与生态成熟度。

考察两款原子化引擎的扩展机制差异与选型取舍,属原子化 CSS 方案题。回答时说明 UnoCSS 预设与 shortcuts 的高灵活度 vs Tailwind 生态成熟,按定制需求选型。

#
★★★

9. styled-components 的运行时注入在 React Server Components 与 Server Streaming 场景下的水合风险与替代方案

styled-components 运行时注入在 RSC 与 Server Streaming 下有什么水合风险?替代方案是什么?

  • 运行时注入依赖客户端执行
  • 流式渲染样式收集顺序
  • 静态提取方案规避风险

styled-components 在运行时把样式注入

考察运行时 CSS-in-JS 在 RSC/流式渲染下的风险与静态提取替代,属现代 React 样式题。回答时说明运行时注入在 RSC 与流式渲染的样式收集与水合风险,静态提取是主要替代。

#
★★★

10. CSS Modules 局部类名生成(hash 策略)在 SSR 与客户端产物的稳定性冲突如何解决

CSS Modules 的 hash 类名在 SSR 与客户端产物不一致时如何解决?

  • hash 由内容与配置决定
  • SSR/客户端使用同一构建配置
  • 固定种子或运行时统一映射

CSS Modules 的局部类名 hash 由文件内容与构建配置(如 css-loader 的 localIdentName)决定。若 SSR 与客户端使用不同构建配置或不同版本的依赖,类名生成规则不一致,服务端 HTML 中的类名与客户端打包的 CSS 对不上,导致样式丢失。解决方式:确保 SSR 与客户端共用同一份构建配置与依赖锁定版本;使用确定性 hash 策略(内容哈希或固定种子);必要时把类名映射表序列化到服务端与客户端共享,保证两端映射一致;升级构建工具后做全量回归验证。

考察 CSS Modules hash 类名在 SSR/客户端一致性上的治理,属构建一致性题。回答时说明 hash 由内容与配置决定,SSR 与客户端需共用配置与确定性 hash,必要时共享映射表。

#
★★★

11. Tailwind CSS v4(Oxide 引擎、Lightning CSS、@theme 与 CSS 变量设计令牌)

Tailwind v4 的 Oxide 引擎与 Lightning CSS 如何提升构建性能?@theme 与 CSS 变量令牌体系如何工作?

  • Oxide 用 Rust 实现扫描与生成
  • 内置 Lightning CSS 做编译与压缩
  • @theme 定义令牌生成变量与工具类

Tailwind v4 的 Oxide 引擎用 Rust 实现源码扫描与工具类生成,显著提升大型项目的构建速度与增量缓存效率;内置 Lightning CSS 处理前缀、转换与压缩,替代了部分 PostCSS 插件链。@theme 在 CSS 中声明设计令牌,编译器为每个令牌生成 CSS 变量与对应工具类,形成以 CSS 变量为载体的令牌体系,支持运行时主题切换与按需产出。整体收益是构建更快、配置更简洁、产物更小;迁移时需注意插件生态与 v3 配置的差异。

考察 Tailwind v4 内部引擎与令牌体系的技术构成,属构建工具题。回答时说明 Oxide 引擎提升扫描速度、内置 Lightning CSS 压缩、@theme 按需生成变量与工具类。

#
★★★

12. CSS Animation Worklet 与 Animation.commitStyles 的工程边界(含动画后状态保留)

CSS Animation Worklet 与 Animation.commitStyles 分别解决什么问题?动画结束后如何保留状态?

  • Animation Worklet 允许脚本驱动动画
  • commitStyles 提交当前样式到内联
  • fill: forwards 与 commitStyles 保留终态

CSS Animation Worklet(Houdini 的一部分)允许通过脚本控制动画每一帧的输出,适合需要复杂物理模拟或外部数据驱动的动画;Animation.commitStyles 把 Web Animations 当前计算出的样式提交为元素的内联样式,使动画终态在动画对象移除后仍然保留。工程边界:Worklet 支持度有限、调试复杂,多数场景用 CSS 动画或 WAAPI 即可;动画结束后保留状态常用 animation-fill-mode: forwards 或调用 commitStyles 固化终态,后者更适合后续继续用 JS 操作样式。

考察 Worklet 的能力定位与 commitStyles 的状态固化机制,属 Web 动画进阶题。回答时说明 Worklet 支持有限且调试复杂,commitStyles 固化终态便于后续 JS 操作,fill 模式与之互补。

const anim = el.animate([{ transform: 'scale(1)' }, { transform: 'scale(1.2)' }], { duration: 300, fill: 'forwards' });
anim.finished.then(() => anim.commitStyles());
#
★★★

13. animation-composition: accumulate | replace | add 在动画合成时的可中断控制

animation-composition 的 replace/add/accumulate 在动画合成时有什么区别?对可中断控制有什么作用?

  • replace 直接替换底层值
  • add 叠加值
  • accumulate 累加值

animation-composition 决定多个动画同时作用于同一属性时的合成方式:replace 用当前动画值替换底层值(默认);add 将动画值直接加到底层值上(如 transform 平移叠加);accumulate 按数值累加。工程价值在于可中断控制:当新动画接管正在进行的动画时,可用 add/accumulate 让新动画基于当前中间值继续,而不是跳回底层值,实现平滑的打断与接力;例如 hover 放大叠加位移时用 add 组合 transform,避免互相覆盖产生跳变。

考察动画合成模式对多动画叠加与中断接力的影响,属 WAAPI/CSS 动画进阶题。回答时说明 replace/add/accumulate 三者的合成语义,强调用 add/accumulate 实现动画打断时的平滑接力。

.item {
  animation: pulse 1s ease-in-out infinite, slide 2s linear;
  animation-composition: add;
}
#
★★★

14. will-change 的使用时机与图层爆炸风险

will-change 应该何时使用?为什么滥用会导致图层爆炸?

  • will-change 提前声明属性变化
  • 浏览器为元素创建独立合成层
  • 滥用导致内存与渲染开销

will-change 提示浏览器某属性即将变化,浏览器可提前把元素提升到独立合成层,使后续动画走合成器、避免重复布局。使用时机:仅在即将发生且持续的性能敏感动画前使用,动画结束后应及时移除。风险:每个提升元素都占用独立图层与 GPU 内存,大量元素同时 will-change 会造成图层爆炸、内存暴涨与合成开销,甚至比不优化更慢。正确做法是只对少数关键动画元素使用,配合 transform/opacity 动画,并避免在大量列表项上滥用。

考察 will-change 的合理时机与图层爆炸的代价,属动画性能题。回答时说明 will-change 提前提升图层但滥用造成内存与合成开销,动画结束及时移除并只用于关键元素。

.card:hover {
  will-change: transform;
}

.card {
  transition: transform 0.2s;
}
#
★★★

15. CSS transition 与 @keyframes 动画的合成器属性(transform、opacity)成本

为什么 transform 与 opacity 动画成本低?transition 与 @keyframes 在合成器属性上有何差异?

  • transform/opacity 走合成器不触发布局绘制
  • transition 需要属性可插值
  • @keyframes 可定义多关键帧

transform 与 opacity 的动画可以在合成器线程完成,不触发布局与绘制,因此成本远低于 top/left、width 等布局属性。transition 只需声明起始与结束值,浏览器在两值间插值;@keyframes 可定义多个关键帧与更复杂的时间轴。工程差异:简单状态切换用 transition,序列化、循环、复杂曲线用 @keyframes;两者都优先动画 transform/opacity 以保持 60fps,如位移用 translate 代替 top/left、显隐用 opacity+visibility 代替 display。

考察合成器属性与两类动画机制的差异,属动画性能基础题。回答时说明合成器属性不触发布局绘制,transition 适合简单切换、@keyframes 适合序列循环的选型。

.box {
  transition: transform 0.3s ease;
}

@keyframes pop {
  0% { transform: scale(0.8); }
  100% { transform: scale(1); }
}
#
★★★

16. Web Animations API 的 KeyframeEffect.composite/iterationComposite 在多动画叠加的工程价值

KeyframeEffect 的 composite 与 iterationComposite 在多动画叠加中各有什么作用?

  • composite 控制效果与底层值合成
  • iterationComposite 控制迭代间累加
  • 实现叠加与循环累积动画

KeyframeEffect.composite 控制该动画效果与元素当前底层值的合成方式:replace、add 或 accumulate,用于多个动画叠加(如位移与缩放独立叠加)时避免互相覆盖。iterationComposite 控制动画多次迭代之间如何合成:replace 每轮从基础值重算,accumulate 让每轮效果累加,可制作逐渐增强的循环动画(如旋转累积)。工程价值:把复杂动画拆分为多个独立效果叠加,互不干扰且可单独控制;迭代累加实现无限增长的循环效果而无需 JS 计算。

考察 WAAPI 合成控制的工程价值,属 Web 动画进阶题。回答时说明 composite 控制效果合成、iterationComposite 控制迭代累加,适合多动画叠加与递增循环。

const fx = new KeyframeEffect(el, [{ transform: 'translateX(0)' }, { transform: 'translateX(100px)' }], {
  duration: 500,
  iterationComposite: 'accumulate',
  composite: 'add'
});
new Animation(fx, document.timeline).play();
#
★★★

17. @scroll-timeline(Scroll-Linked Animations)

@scroll-timeline 是什么?与 animation-timeline: scroll() 有什么关系?

  • @scroll-timeline 声明滚动时间线
  • scroll() 是内联快捷方式
  • 通过 animation-timeline 绑定

@scroll-timeline 是在 CSS 中声明滚动时间线的规则:通过 source(滚动容器)与 orientation(滚动轴)定义时间线,再用 animation-timeline 绑定到动画;animation-timeline: scroll() 是更简洁的等价内联语法。两者本质相同,实现滚动驱动动画:动画进度随滚动位置变化。注意该特性经历了规范演进,旧语法与新语法并存,浏览器支持以 scroll() 为主,使用时应以现行规范为准。

考察滚动时间线的声明方式与新语法关系,属滚动驱动动画题。回答时说明 @scroll-timeline 声明时间线、scroll() 是等价内联语法,注意规范演进与浏览器支持差异。

@scroll-timeline --sc {
  source: auto;
  orientation: vertical;
}

.progress {
  animation: grow linear;
  animation-timeline: --sc;
}
#
★★★

18. CSS Modules 与 @layer 级联层结合在大型项目作用域管理的工程价值

CSS Modules 与 @layer 结合如何治理大型项目的样式作用域?

  • CSS Modules 提供类名隔离
  • @layer 提供优先级编排
  • 结合减少冲突与覆盖成本

CSS Modules 通过编译生成哈希类名,保证组件样式不互相泄漏;@layer 通过层顺序统一管理跨来源样式的优先级,如把 UI 库样式放入 vendor 层、业务组件放入 components 层。二者结合的价值:类名隔离解决命名冲突,层优先级解决覆盖关系,组件内局部样式用 CSS Modules 保持独立,跨组件/跨来源的覆盖用 @layer 有序编排,减少 !important 与深层选择器。工程上还可在 @layer 中按业务域继续分层,形成可审计、可预测的样式治理体系。

考察两种机制互补的工程价值,属大型项目样式治理题。回答时说明 CSS Modules 隔离命名、@layer 编排优先级,二者结合减少 !important 与深层覆盖。回答时给出分层治理的完整示例。

@layer base, components, vendor, overrides;

@layer components {
  .card { /* 来自 CSS Modules */ }
}
#
★★★

19. CSS Modules 的 composes 与 Sass @extend 在复用颗粒度的取舍

CSS Modules 的 composes 与 Sass @extend 在样式复用上有什么颗粒度差异?如何取舍?

  • composes 组合类到元素
  • @extend 生成选择器合并
  • 颗粒度与特异性影响不同

composes 把被组合的类规则一并应用到元素(编译后元素同时拥有多个类名),复用单位是完整的类规则,不改变选择器结构,特异性可控;@extend 把选择器合并进被扩展规则的选择器列表,复用单位是规则块,可能生成跨模块的选择器组合、增加特异性与耦合。取舍:需要组件级、无嵌套的样式组合用 composes;需要把语义类扩展为多个规则、且接受选择器合并时用 @extend;@extend 跨文件使用易产生意外匹配,大型项目更推荐 composes 或 CSS 变量。

考察两类复用机制的颗粒度与耦合差异,属样式复用选型题。回答时说明 composes 组合类规则、@extend 合并选择器,颗粒度与耦合差异决定选型。回答时说明跨文件使用 @extend 的风险。

#
★★★

20. CSS-in-JS 的运行时(styled-components)

styled-components 运行时如何工作?它的优点与性能/水合风险是什么?

  • 模板字符串生成样式
  • 运行时注入 style 标签
  • SSR 收集与动态 props 开销

styled-components 用标签模板语法把组件样式定义为样式组件,运行时生成类名、把 CSS 注入

考察运行时 CSS-in-JS 的机制与性能风险,属样式方案评估题。回答时说明运行时注入的机制与 SSR 收集、动态 props 开销,评估静态化或迁移替代方案。回答时说明评估指标与迁移路径。

import styled from 'styled-components';

const Button = styled.button`
  background: ${(p) => p.primary ? 'blue' : 'gray'};
`;
#
★★★

21. Tailwind v4 的 Oxide 引擎与 @theme 在编译产物的体积收益

Tailwind v4 的 Oxide 引擎与 @theme 如何带来编译产物体积收益?

  • 按需扫描生成工具类
  • @theme 令牌按使用产出
  • Lightning CSS 压缩

Tailwind v4 的 Oxide 引擎(Rust 实现)扫描源码中出现的工具类,只生成实际使用的类与相关 CSS 变量,避免 v3 中整包主题变量常驻产物的问题;@theme 定义的令牌按需生成变量与工具类,未使用的令牌不进入产物;内置 Lightning CSS 负责压缩与转换,进一步减小体积。收益体现为:小项目产物极小、大项目避免无用样式膨胀、CSS 变量令牌只在被引用时保留。工程上配合内容扫描配置可精确控制产物,达到接近手写 CSS 的体积。

考察 v4 按需生成与压缩带来的体积优化机制,属构建产物优化题。回答时说明按需扫描生成工具类与令牌变量,Lightning CSS 压缩,产物体积接近手写 CSS。回答时说明按需生成与压缩的机制。

#
★★★

22. 原子化 CSS(Tailwind v4 Oxide、UnoCSS、Panda CSS)

原子化 CSS 的核心思想是什么?Tailwind v4、UnoCSS、Panda CSS 各自的特点如何?

  • 原子类即单一用途工具类
  • 按需生成控制体积
  • 三者在扩展与心智上差异

原子化 CSS 把样式拆分为单一用途的工具类(如 p-4、flex、text-center),在 HTML 中组合使用,配合扫描按需生成,避免未用样式进入产物。Tailwind v4 以 Oxide 引擎与 @theme 提供成熟生态与 CSS-first 配置;UnoCSS 以预设机制提供极致灵活与多引擎兼容;Panda CSS 在原子化基础上提供 recipes 变体系统与静态生成。选型:生态与团队熟悉度优先 Tailwind,深度定制优先 UnoCSS,需要组件变体体系优先 Panda,三者都强调构建期生成与体积可控。

考察原子化 CSS 的通用思想与主流实现差异,属样式架构选型题。回答时说明原子类按需生成的核心思想,对比 Tailwind 生态、UnoCSS 灵活、Panda 变体体系。

#
★★★

23. CSS Modules 局部类名的生成原理与继承样式泄漏风险

CSS Modules 的局部类名如何生成?继承样式为什么可能泄漏?

  • 类名编译为哈希局部名
  • 全局选择器仍可命中
  • 继承属性沿 DOM 传递

CSS Modules 编译时把局部类名转换为带哈希的唯一名称(如 _card_x3f2),避免全局命名冲突;组合类(composes)与 :global 提供跨作用域能力。泄漏风险:哈希只隔离类名,不隔离继承属性——如 color、font-size 等可继承属性仍沿 DOM 树从祖先传到组件内部;全局选择器(body、*)与 :global 样式也可命中组件内部元素;第三方注入的全局样式同样可能覆盖。因此组件样式不能假设完全隔离,需对可继承属性显式声明,关键组件可结合 Shadow DOM 或 @scope。

考察 CSS Modules 隔离机制的边界与继承泄漏风险,属组件样式隔离题。回答时说明哈希类名只隔离命名,继承属性与全局选择器仍可能泄漏,需显式声明关键属性。回答时强调显式声明可继承属性。

#
★★★

24. vanilla-extract 在大型设计系统的工程价值

vanilla-extract 在大型设计系统中有哪些工程价值与约束?

  • 编译期类型安全样式
  • 样式契约与 tokens 共享
  • 无运行时成本与 SSR 友好

vanilla-extract 用 TypeScript 定义样式,在编译期生成静态 CSS 与类型安全的样式契约:类名、变量与 recipe 都有类型提示,重构时可被编译器发现错误。设计系统价值:design tokens 可作为 TS 对象与 CSS 变量双端共享,主题变体类型约束化;零运行时、SSR 友好、产物与手写 CSS 相当。约束:样式必须在编译期静态表达,无法依赖运行时计算;CSS-in-TS 的学习成本与生态相对较小;动态样式需显式枚举或用 CSS 变量承载。

考察 vanilla-extract 的类型安全与令牌共享价值及其静态约束,属设计系统工程题。回答时说明编译期类型安全、tokens 双端共享、零运行时与 SSR 友好,注意静态表达约束。

import { createTheme, style } from '@vanilla-extract/css';

export const [themeClass, vars] = createTheme({
  color: { brand: '#0af' }
});

export const card = style({ background: vars.color.brand });
#
★★★

25. PostCSS Preset Env 与 Lightning CSS 在源目标浏览器语法编译的工程价值

PostCSS Preset Env 与 Lightning CSS 在按目标浏览器编译 CSS 语法上各有什么价值?如何选型?

  • preset-env 按 browserslist 转译新语法
  • Lightning CSS 用 Rust 实现解析转换压缩
  • 选型看构建链与性能需求

postcss-preset-env 按 browserslist 目标把现代 CSS 语法(嵌套、逻辑属性、颜色函数、级联层等)转译为兼容语法,并附带 autoprefixer 能力,是 PostCSS 生态的经典选择;Lightning CSS 用 Rust 实现 CSS 解析、转换、前缀与压缩,速度远超 JS 实现,且原生支持 CSS Modules、级联层与容器查询等新特性的降级。价值都在于让团队按现代语法书写、按目标浏览器产出兼容产物。选型:已有 PostCSS 链且插件需求多选 preset-env;追求构建速度与一体化能力可选 Lightning CSS,也可二者配合。

考察两款编译器的定位差异与选型,属 CSS 构建工具题。回答时说明 preset-env 按 browserslist 转译、Lightning CSS 用 Rust 实现更快转换,按构建链选型。

#
★★

26. Tailwind v4 的 CSS-first 配置(@theme)与 JS 配置文件(tailwind.config.js)在 IDE 跳转与重构体验上的差异

Tailwind v4 的 @theme 与 v3 的 tailwind.config.js 在 IDE 跳转与重构体验上有什么差异?

  • @theme 与样式同文件可跳转
  • JS 配置与使用处分离
  • 重构时类型与引用追踪差异

v4 的 @theme 把设计令牌写在 CSS 中,与样式代码同源,IDE 中从工具类或变量可以直接跳转到 @theme 定义处,查看与修改令牌无需切换文件;v3 的 tailwind.config.js 把令牌集中放在 JS 中,使用处与定义处分离,跳转依赖插件支持且跨文件。重构体验:@theme 中修改变量名时,CSS 内的引用可由语言服务器联动提示;JS 配置提供类型与校验能力但重构追踪较间接。总体 v4 让令牌更贴近使用语境,适合样式驱动团队,复杂逻辑仍保留 JS 配置。

考察两代配置方式在开发体验上的差异,属 Tailwind 工程题。回答时说明 @theme 与样式同源利于跳转,JS 配置集中但跨文件,重构追踪体验不同。回答时结合团队协作场景说明差异。

#
★★

27. CSS-in-JS 静态提取(Linaria、vanilla-extract、Pigment CSS)

Linaria、vanilla-extract、Pigment CSS 如何实现 CSS-in-JS 静态提取?有什么共性与差异?

  • 编译期提取为静态 CSS
  • 消除运行时注入
  • 各自 API 与框架适配不同

静态提取方案在编译期分析样式代码并输出独立 CSS 文件,运行时不再注入样式:Linaria 用模板字符串定义样式并在构建时提取;vanilla-extract 用 TS 对象定义、编译为类名与 CSS;Pigment CSS 面向 MUI 生态,编译 styled/theme 调用为静态 CSS。共性:零运行时、SSR 一致、产物可控、性能好。差异在 API 形态与生态:Linaria 贴近 styled 语法、vanilla-extract 类型安全强、Pigment 深度集成 Material UI。选型取决于框架与组件库生态。

考察三类静态提取方案的能力与差异,属 CSS-in-JS 方案题。回答时说明三类静态提取方案共性是零运行时与 SSR 一致,差异在 API 与生态。回答时按框架生态与团队习惯选型。

#
★★

28. Open Props 这类 CSS 变量预设库在设计系统尚未建立时如何快速搭建视觉一致性

Open Props 如何帮助团队在未建立设计系统时快速获得视觉一致性?边界是什么?

  • 开箱即用的 CSS 变量令牌
  • 颜色/尺寸/动效预设
  • 作为过渡而非长期设计系统

Open Props 提供一套开箱即用的 CSS 自定义属性:颜色、间距、字号、阴影、缓动、动画等令牌,团队引入后即可用 var(--size-4)、var(--color-brand) 等统一取值,快速消除散落的魔法值,建立初步视觉一致性。边界:预设反映通用审美,不等于品牌设计系统,命名与语义与业务不完全匹配;应把它作为过渡垫脚石,在使用过程中沉淀自己的令牌层(覆盖或替换),逐步演进为定制化设计系统,避免长期依赖第三方预设导致品牌同质化。

考察变量预设库的快速起步价值与演进边界,属设计系统工程题。回答时说明 Open Props 作为过渡垫脚石,使用中沉淀自有令牌演进为定制设计系统。回答时说明沉淀自有令牌的演进路径。

@import "open-props/style";

.card {
  padding: var(--size-4);
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-3);
}
#
★★

29. CSS @scope (--name) 在隔离第三方样式与组件级样式泄漏的边界与 polyfill 现状

@scope (--name) 命名作用域在隔离第三方样式与组件泄漏上有什么边界?polyfill 现状如何?

  • @scope 基于选择器范围隔离
  • 无法阻止全局与继承泄漏
  • polyfill 支持有限

@scope (--name) 把规则限制在命名作用域根的后代内,可降低组件样式误伤外部元素的风险;但它是选择器级隔离,无法阻止可继承属性沿 DOM 传入、无法阻止全局选择器或 !important 的覆盖,隔离强度弱于 Shadow DOM。第三方样式如果使用无作用域的高特异性选择器,仍可能穿透。polyfill 现状:@scope 已有主流浏览器支持(Chrome 118+、Safari 17.2+、Firefox 118+),polyfill 主要面向旧浏览器,实现上依赖选择器重写,复杂情况(如动态作用域)支持有限,生产使用需结合特性检测与降级。

考察 @scope 的隔离边界与兼容处理,属组件样式隔离题。回答时说明 @scope 选择器级隔离无法阻止继承与全局覆盖,polyfill 能力有限需特性检测。回答时说明生产使用需特性检测与回退。

#
★★

30. StyleX(Meta)在大型项目(Facebook/Robinhood)

StyleX 在 Meta 大型项目中解决了什么问题?它的核心设计是什么?

  • 编译期确定性与可预测性
  • 原子化生成与合并
  • 静态分析消除运行时开销

StyleX 是 Meta 开源的样式方案,核心目标是在超大型项目(Facebook、Instagram 等)中提供可预测、可编译的样式:样式对象在编译期静态分析,生成确定性类名与原子化 CSS,运行时零注入;同名/同值规则自动合并,避免样式体积随组件数量线性膨胀;支持条件样式与主题变量。设计上强调确定性(相同输入产生相同输出)与可合并性,配合 Babel/编译插件在构建期完成,SSR 友好。它更适合对规模与性能有极致要求的团队,API 与生态相对小众。

考察 StyleX 的设计动机与工程价值,属大规模样式方案题。回答时说明 StyleX 编译期确定性、原子化合并与零运行时,适合超大规模项目。回答时说明其适合规模与性能敏感团队。

import stylex from '@stylexjs/stylex';

const styles = stylex.create({ base: { color: 'red' } });
<div className={stylex.props(styles.base).className} />
#
★★

31. CSS Modules composes from 跨文件复用与全局工具类(Tailwind)在团队协作的治理规范

CSS Modules 的 composes from 与 Tailwind 全局工具类在团队协作中如何治理?

  • composes from 跨文件依赖
  • 工具类直接写 HTML
  • 规范依赖方向与类职责

composes from 允许跨文件引用其他模块的类,复用方便但会形成隐式依赖图,需治理:约定只能引用基础层/通用模块,禁止业务组件互相 composes;保持依赖方向单向;用 lint 检测循环。Tailwind 工具类直接写在 HTML 中,灵活但易出现重复、无语义与不可维护的类串,治理方式:用 @apply 或组件抽象沉淀常用组合;通过 ESLint/Prettier 插件规范类顺序;约定业务样式优先用令牌与组件类,工具类只做微调。二者可结合:CSS Modules 管组件结构、Tailwind 管原子微调,关键是团队规范先行。

考察两类复用方式的协作治理,属团队样式规范题。回答时说明 composes from 需单向依赖约束,Tailwind 工具类需组件化沉淀与顺序规范。回答时说明两种方式可结合使用。

#
★★

32. Panda CSS 的 recipes 与 slot recipes 在定义可复用组件变体上的工程价值

Panda CSS 的 recipes 与 slot recipes 如何定义可复用组件变体?工程价值是什么?

  • recipes 组合变体与默认值
  • slot recipes 拆分多插槽部件
  • 类型安全与按需生成

Panda CSS 的 recipes 把组件样式组织为变体系统:定义 base 样式与 variants(如 size、variant、状态),调用时按 props 生成组合类,天然支持默认值、复合变体与运行时组合;slot recipes 进一步把组件拆分为多个插槽(如 root、label、icon),每个插槽有自己的变体,适合 Button、Card 等多部件组件。工程价值:组件变体声明式、类型安全,产物按需生成,配合静态提取零运行时,让组件库的样式 API 稳定且可预测,减少手写条件类拼接。

考察 Panda recipes 的变体建模能力,属组件库样式题。回答时说明 recipes 声明式变体、slot recipes 拆分多插槽,类型安全且按需生成。回答时说明其与原子化体系互补。

import { cva } from 'styled-system/css';

export const button = cva({
  base: { display: 'inline-flex' },
  variants: {
    size: { sm: { padding: '2' }, lg: { padding: '4' } }
  }
});
#
★★

33. Tailwind v4 的 @theme 在自定义动画 keyframes 与阴影令牌扩展的工程应用

Tailwind v4 如何用 @theme 扩展自定义动画 keyframes 与阴影令牌?

  • @theme 中定义 --animate-* 令牌
  • keyframes 在 @theme 内声明
  • 阴影令牌生成对应工具类

Tailwind v4 的 @theme 支持扩展非颜色令牌:定义 --animate-fade-in 并同时声明对应的 @keyframes fade-in,编译器会生成 animate-fade-in 工具类与动画;定义 --shadow-card 会生成 shadow-card 工具类。做法是在 @theme 内写出令牌名与值、keyframes 放在 @theme 内或全局,编译器识别前缀(animate、shadow 等)自动产出工具类。工程应用:把品牌动效与阴影沉淀为令牌,组件直接用工具类,保证一致性并可按需生成,避免在 JS 配置中重复维护。

考察 @theme 对动画与阴影令牌的扩展方式,属 Tailwind v4 工程题。回答时说明 @theme 可定义 animate/shadow 令牌并生成工具类,keyframes 随令牌声明。

@theme {
  --animate-fade-in: fade-in 0.4s ease-out;
  --shadow-card: 0 4px 12px rgb(0 0 0 / 0.12);

  @keyframes fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}
#
★★

34. Lightning CSS 作为 PostCSS 替代品在现代 Vite/Rspack 构建链中的 prefix 与 CSS Modules 合并策略

Lightning CSS 在 Vite/Rspack 构建链中如何替代 PostCSS?prefix 与 CSS Modules 如何配置合并?

  • Vite 内置 Lightning CSS 选项
  • browserslist 驱动 prefix
  • CSS Modules 与压缩合并处理

Vite 通过 css.transformer: 'lightningcss' 启用内置 Lightning CSS,替代 PostCSS 处理前缀、转换与压缩,性能更高;Rspack 同样提供 lightningcss-loader 或内置支持。配置上通过 browserslist 决定 prefix 与语法降级目标;CSS Modules 由构建工具先做类名局部化,再交给 Lightning CSS 做后续转换与压缩,两者顺序需保证:先 Modules 哈希,后前缀/压缩。合并策略:开发模式禁用压缩保留可读性,生产开启;与 postcss.config 共存时需明确两者职责边界,避免重复处理。

考察 Lightning CSS 在主流构建链中的接入与处理顺序,属构建工具题。回答时说明 Vite/Rspack 内置 Lightning CSS 的接入方式,Modules 先哈希再转换压缩的顺序。

#
★★

35. @keyframes 与 Web Animations API 的混合控制与 cancel/finish 生命周期

@keyframes 动画与 WAAPI 如何混合控制?cancel 与 finish 的生命周期如何处理?

  • WAAPI 可用 JS 控制 CSS 动画
  • cancel 中断并回到基础值
  • finish 跳到终态并触发事件

CSS @keyframes 定义的动画可通过 element.getAnimations() 获取对应 Animation 对象,用 WAAPI 统一控制 play/pause/cancel/finish,实现 CSS 声明与 JS 控制的混合。生命周期:cancel() 立即中断动画并回到基础值,触发 cancel 事件;finish() 跳到动画终点,触发 finish 事件;两者都会让动画结束,但语义不同——cancel 是放弃、finish 是完成。工程上中断交互动画用 cancel 并处理状态回退,完成任务动画用 finish 并提交终态,配合 fill 模式保证样式一致。

考察 CSS 动画与 WAAPI 混合控制的生命周期语义,属动画控制题。回答时说明 getAnimations 统一控制 CSS 与 JS 动画,cancel 与 finish 的生命周期语义不同。

const anims = el.getAnimations();
anims.forEach((a) => a.cancel());

// 或
anim.finished.then(() => anim.commitStyles());
#
★★

36. @starting-style 与 View Transitions 组合实现入场动画

@starting-style 与 View Transitions 如何组合实现元素入场动画?

  • @starting-style 定义首帧样式
  • View Transitions 提供过渡快照
  • 组合实现从无到有的平滑入场

@starting-style 声明元素首次渲染(或从 display:none 变为可见)时的起始样式,配合 transition 让元素从起始态平滑过渡到常规态,实现原生入场动画;View Transitions API 在 DOM 更新时对旧新状态拍照并生成过渡,适合页面级路由与列表重排的平滑切换。组合用法:用 View Transitions 包裹 DOM 更新以生成整体过渡,用 @starting-style 为新增元素提供入场起始样式,二者互补,实现既有页面级过渡又有元素级入场的完整动画。注意浏览器支持与降级。

考察两类动画机制的组合用法,属现代动画题。回答时说明 @starting-style 提供入场起始态、View Transitions 提供 DOM 更新过渡,组合实现完整入场。

.item {
  transition: opacity 0.3s, translate 0.3s;
}

@starting-style {
  .item {
    opacity: 0;
    translate: 0 8px;
  }
}
#
★★

37. transitionend 与 cancel 事件在动画被打断时的状态一致性

transition 被打断时 transitionend 与 cancel 事件如何保证状态一致?

  • 打断时不触发 transitionend
  • 显式移除过渡触发 cancel
  • 用事件与样式同步状态

transition 正常完成时触发 transitionend;若在过渡中途属性被再次修改(过渡被新过渡取代或值回到起点),transitionend 不会触发,容易造成状态监听缺失。cancel 事件在过渡被取消时触发(如 display:none 或显式移除 transition)。保证状态一致性:不要仅依赖 transitionend 收尾,可用 getComputedStyle 读取最终值、或在回调中结合 setTimeout 兜底;用 transitioncancel 处理中断并同步 UI 状态;框架中常用 transitionend 一次性监听并在 finally 中清理,避免多次触发与状态残留。

考察过渡事件在打断场景下的语义与状态同步策略,属动画事件题。回答时说明打断不触发 transitionend 而是 cancel,收尾需读取最终值加兜底,避免状态残留。

#
★★

38. animationstart/animationend/animationcancel 事件在动画编排的应用

animationstart/animationend/animationcancel 事件在动画编排中如何应用?

  • start/end 标记动画阶段
  • cancel 处理中断
  • 用于序列编排与状态同步

animationstart、animationend、animationcancel 分别标记 CSS 动画的开始、正常结束与取消。编排应用:用 animationend 触发下一步动画或移除加载态,实现动画序列;用 animationstart 做进入视口前的准备;用 animationcancel 处理动画被中断(如元素隐藏、display 切换)时的状态回退,避免残留中间态。注意多个动画属性可产生多次事件,事件对象有 animationName 可区分;依赖事件编排时需考虑循环动画不会触发 end,且事件在不同浏览器的触发时机有细微差异。

考察动画事件在编排与中断处理中的用法,属动画工程题。回答时说明 start/end/cancel 各标记阶段,用 animationName 区分多动画,循环动画不触发 end。

el.addEventListener('animationend', (e) => {
  if (e.animationName === 'fadeIn') {
    el.classList.add('done');
  }
});
#
★★

39. CSS 动画的 fill-mode: forwards/backwards/both 与帧外的最终状态保留

animation-fill-mode 的 forwards/backwards/both 各保留什么状态?动画结束后如何保留终态?

  • forwards 保留动画结束帧
  • backwards 在延迟期应用首帧
  • both 同时保留首尾帧

animation-fill-mode 控制动画在非播放期(延迟期与结束后)应用的样式:forwards 在动画结束后保留最后一帧;backwards 在延迟期间提前应用第一帧;both 同时应用首尾帧。动画结束后保留终态的常见需求用 forwards 或 both;但注意 forwards 只是样式层保留,若之后改变元素样式或被移除,终态可能被覆盖。需要把终态固化为元素真实状态时,可用 WAAPI 的 commitStyles 提交内联样式。fill-mode 不影响动画执行本身,只影响帧外取值。

考察 fill-mode 的语义差异与终态保留的边界,属 CSS 动画基础题。回答时说明 forwards 保留结束帧、backwards 延迟期应用首帧、both 兼备,commitStyles 可固化终态。

.box {
  animation: fadeIn 0.4s ease-out both;
}
#
★★

40. Scroll Snap 与滚动容器控制的协作

Scroll Snap 如何与滚动容器控制协作?JS 控制滚动与 snap 如何避免冲突?

  • scroll-snap-type 定义吸附
  • JS scrollTo 受 snap 影响
  • 用 scrollTo 选项与事件协调

Scroll Snap 通过 scroll-snap-type(容器)与 scroll-snap-align(子项)实现吸附;JS 调用 scrollTo/scrollBy 时同样受 snap 影响,可能被吸附到最近位置,导致精确滚动失败或抖动。协作方式:scrollTo 使用 behavior: smooth 与指定位置时,浏览器会结合 snap 目标;需要精确控制时可用 scroll-snap-stop 与 scroll-margin 调整,或临时禁用 snap;监听 scrollend 事件确认滚动结束。轮播实现中常让 JS 滚动与 snap 目标一致(按子项索引计算位置),避免两者争夺产生错位。

考察 Scroll Snap 与脚本滚动的交互协调,属滚动交互题。回答时说明 JS scrollTo 受 snap 吸附影响,滚动目标与 snap 位置一致并监听 scrollend 协调。

carousel.scrollTo({ left: item.offsetLeft, behavior: 'smooth' });
carousel.addEventListener('scrollend', () => { /* 更新索引 */ });
#
★★

41. CSS Transition 与 CSS Animation 在触发方式与时间轴控制的工程差异

CSS Transition 与 CSS Animation 在触发方式与时间轴控制上有什么工程差异?

  • transition 依赖属性变化触发
  • animation 主动播放并可循环
  • 复杂编排用 animation

Transition 是状态间插值:属性值改变(如 hover、类切换)时从旧值过渡到新值,触发被动、无中间帧定义、不可循环;Animation 由 @keyframes 主动驱动,可定义多关键帧、时长、延迟、循环与方向,时间轴控制更强。工程差异:简单交互状态变化用 transition(开销小、语义直观);序列化、循环、入场/离场编排用 animation;需要暂停/恢复/反向等精细控制时,二者都可配合 WAAPI 或事件。过渡动画被打断时会从当前值重新过渡,动画则按关键帧推进。

考察两类动画机制的触发与时间轴差异,属动画选型题。回答时说明 transition 被动插值、animation 主动多帧可循环,复杂编排用 animation。回答时说明时间轴控制能力的差异即可。

#
★★

42. prefers-reduced-motion 媒体查询在无障碍动画降级的现代实践

prefers-reduced-motion 如何降级动画?现代实践中要注意什么?

  • reduce 表示用户偏好减少动效
  • 用媒体查询关闭或简化动画
  • 保留必要的信息反馈

prefers-reduced-motion: reduce 表示用户偏好减少或关闭动效(如前庭障碍、光敏用户)。现代实践:用 @media (prefers-reduced-motion: reduce) 关闭大位移、闪烁与自动播放动画,保留必要但缓和的反馈(如短暂透明度变化);对自动播放的轮播、视差与滚动驱动动画提供静态降级;动画库与组件库提供统一开关。注意仅隐藏动画还不够,还需保证状态变化有非动画提示;同时避免把所有动画一刀切移除导致信息丢失,可提供 reduce 版本的简化过渡。

考察动效降级的完整实践,属无障碍动画题。回答时说明 reduce 下关闭大位移与自动动画并保留必要反馈,动画库提供统一开关。回答时强调保留必要反馈与统一开关。并覆盖轮播、视差等自动动画场景。

@media (prefers-reduced-motion: reduce) {
  .carousel {
    scroll-behavior: auto;
  }
  .banner {
    animation: none;
  }
}
#
★★

43. Web Animations API(WAAPI)在 JS 动画与 CSS 动画协作的统一控制

WAAPI 如何统一控制 JS 与 CSS 动画?在协作场景中的价值是什么?

  • element.animate 创建 JS 动画
  • getAnimations 获取全部动画
  • 统一播放状态与事件

WAAPI 提供 element.animate() 用 JS 创建动画,getAnimations() 获取元素上所有动画(含 CSS 声明创建的),Animation 对象统一暴露 play/pause/cancel/finish、currentTime、timeline 与事件。协作价值:JS 可以暂停、逆转、同步 CSS 动画;多个动画可在同一时间线上编排;动画完成后用 finished Promise 串联逻辑。工程上适合交互复杂的动效(拖拽、进度联动),与 CSS 声明式动画互补,避免手写 rAF 循环与状态管理,同时保持 CSS 动画的可读性。

考察 WAAPI 的统一控制能力,属动画 API 题。回答时说明 WAAPI 统一控制 CSS 与 JS 动画,finished Promise 串联编排逻辑。回答时说明 finished Promise 编排的用法。

const anims = el.getAnimations();
anims.forEach((a) => { a.pause(); a.currentTime = progress * a.effect.getTiming().duration; });
#
★★

44. transform 与 top/left 在 GPU 合成层动画的性能取舍

为什么 transform 动画比 top/left 性能好?GPU 合成层动画的原理是什么?

  • top/left 触发布局与绘制
  • transform 只走合成
  • 合成层避免主线程重排

修改 top/left 会改变布局几何,浏览器需要重排(layout)、重绘(paint)再合成,成本高;transform 的平移、缩放、旋转由合成器线程处理,元素提升为合成层后,动画只需每帧更新合成属性,不触发布局与绘制,可保持 60fps。取舍:需要移动元素优先用 translate 而非 top/left;同时避免过度提升图层(内存开销);注意 transform 对可访问性(元素实际布局位置不变,对布局影响与辅助技术语义)的影响,配合 will-change 谨慎使用。

考察合成器属性与布局属性的性能差异,属动画性能必考题。回答时说明 transform 走合成器不触发布局,top/left 触发重排重绘,注意图层内存开销。回答时说明合成层内存开销的权衡。

.item {
  transform: translate3d(0, 0, 0);
  transition: transform 0.3s;
}

.item.active {
  transform: translate3d(24px, 0, 0);
}
#
★★

45. CSS @keyframes 与 Motion Path(offset-path)在 SVG / CSS 路径动画的可访问性与降级边界

Motion Path(offset-path)路径动画在可访问性上有什么问题?如何降级?

  • offset-path 沿路径移动元素
  • 动画内容对辅助技术不可见
  • 提供静态终态与 reduce 降级

offset-path 与 offset-distance 让元素沿 SVG/CSS 路径运动,如 path() 或 circle(),可实现复杂轨迹动画。可访问性边界:动画本身是视觉效果,关键信息不能只靠动画传递;大位移、长时间运动的动画可能引发眩晕,应响应 prefers-reduced-motion 关闭或替换为淡入淡出;路径动画中元素的实际布局位置不变,键盘焦点与点击热区可能与视觉位置不一致,需要注意命中测试与焦点可达性。降级策略:提供动画终态的静态样式,reduce 时直接定位到终点,并保留语义信息。

考察路径动画的无障碍问题与降级策略,属动画可访问性题。回答时说明路径动画热区与视觉可能错位、大位移引发眩晕,reduce 时定位到静态终态。回答时说明静态终态与 reduce 降级做法。

@media (prefers-reduced-motion: reduce) {
  .fly {
    offset-distance: 100%;
    animation: none;
  }
}
#
★★

46. FLIP(First-Last-Invert-Play)技术实现流畅布局过渡的现代实践

FLIP 技术如何实现流畅的布局过渡?现代实践与替代方案是什么?

  • First/Last 记录起止位置
  • Invert 用 transform 反向补偿
  • Play 过渡到零位移

FLIP 的流程:First 记录元素初始位置,Last 记录目标状态(DOM 变更后)的位置,Invert 计算位移差并用 transform 反向补偿,Play 让 transform 过渡回 0,从而把昂贵的布局变化伪装成合成器动画,视觉流畅。现代实践:用 View Transitions API 实现类似效果(浏览器自动拍照与过渡),或用 FLIP 库;注意 FLIP 对连续快速变化(滚动中)容易抖动,需结合 rAF 批量处理;transform 补偿不改变实际布局,需在动画结束后保证最终样式与布局一致。

考察 FLIP 的原理与现代替代方案,属布局动画题。回答时说明 FLIP 四步原理与 transform 补偿,View Transitions 是现代替代,连续变化易抖动。回答时说明 rAF 批量处理与 View Transitions。

const first = el.getBoundingClientRect();
// ...DOM 变更...
const last = el.getBoundingClientRect();
el.animate([{ transform: `translate(${first.left - last.left}px, ${first.top - last.top}px)` }, { transform: 'none' }], { duration: 300 });
#
★★

47. 过渡缓动函数(cubic-bezier)在材质设计与微交互的工程取舍

cubic-bezier 缓动函数在微交互中如何选型?不同曲线的工程含义是什么?

  • ease-in/out 等预设曲线的感知差异
  • 自定义 cubic-bezier 模拟物理
  • 出/入动画用不同曲线

cubic-bezier 控制过渡速度曲线:ease-out 先快后慢适合元素进入、手感轻快;ease-in 先慢后快适合离场或强调;ease-in-out 对称适合中性过渡;自定义曲线(如 cubic-bezier(0.34, 1.56, 0.64, 1) 回弹)可模拟轻微物理弹性。工程取舍:进入动画多用 ease-out 让用户尽快看到结果,离场多用 ease-in,避免反向使用产生拖沓感;回弹曲线适合小元素微交互但不宜用于大位移;统一把曲线沉淀为令牌,避免各组件随手写值导致手感不一致;同时考虑 reduce 偏好。

考察缓动曲线的语义选型与令牌化,属微交互设计题。回答时说明进入 ease-out、离场 ease-in、自定义曲线模拟弹性,曲线令牌化统一手感。回答时说明曲线令牌化的团队价值。

:root {
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.pop {
  transition: transform 0.3s var(--ease-spring);
}
#
★★

48. scroll-behavior: smooth 与 JS 平滑滚动在可访问性的差异

scroll-behavior: smooth 与 JS 平滑滚动在可访问性上有什么差异?

  • CSS smooth 全局生效
  • JS 滚动可精确控制
  • reduce 偏好与即时跳转

scroll-behavior: smooth 让所有锚点与 scrollTo 滚动平滑,声明式、无需 JS;JS 平滑滚动(scrollTo 带 behavior: smooth 或使用滚动库)可精确控制目标与时长。可访问性差异:平滑滚动可能让依赖即时跳转的用户(如键盘导航、放大镜用户)感到困扰,因此应响应 prefers-reduced-motion 回退为 auto 即时滚动;JS 方案可在代码中判断偏好并选择 behavior;长页面锚点导航若滚动过慢影响效率,需提供跳过机制。工程上默认 CSS smooth 加 reduce 回退,复杂交互用 JS 并遵守偏好。

考察两种平滑滚动方式的无障碍处理,属滚动可访问性题。回答时说明平滑滚动需响应 reduce 回退即时跳转,JS 方案可程序化判断偏好。回答时说明 CSS smooth 加 reduce 回退组合。

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}
#
★★

49. transition-behavior: allow-discrete 让 display/visibility 等离散属性参与过渡的原理(dialog/popover 显隐动画的关键)

transition-behavior: allow-discrete 如何让 display/visibility 等离散属性参与过渡?

  • 离散属性默认不可过渡
  • allow-discrete 允许离散插值
  • 配合 @starting-style 实现显隐动画

默认情况下 transition 只对可插值的连续属性生效,display、visibility 等离散属性会在过渡开始时瞬间切换。transition-behavior: allow-discrete 允许离散属性参与过渡:visibility 可被插值为 visible 到 hidden 的平滑阶段,display 在动画结束时才切换(如 display: none 延后到过渡完成),从而支持 dialog、popover 的显隐动画。原理是浏览器在过渡时间轴内保留离散属性的关键帧,在动画边界做不连续跳变但整体时序受控。需配合 @starting-style 定义起始态,才能从 display:none 到可见时播放入场动画。

考察离散属性过渡的机制与显隐动画的配合,属现代动画进阶题。回答时说明 allow-discrete 让离散属性参与过渡、display 延后切换,配合 @starting-style 实现显隐动画。

.dialog {
  transition: opacity 0.3s, display 0.3s allow-discrete, visibility 0.3s;
  transition-behavior: allow-discrete;
}
#
★★

50. @starting-style 与 transition-behavior 组合实现元素插入 DOM 或从 display:none 变为可见时的入场动画

@starting-style 与 transition-behavior 如何组合实现元素插入 DOM 或 display:none 变为可见的入场动画?

  • @starting-style 定义初始样式
  • allow-discrete 控制 display 切换时机
  • 退出动画结束再隐藏

元素插入 DOM 或从 display:none 变为可见时,浏览器没有之前的状态可供过渡,默认不会播放动画。@starting-style 提供起始样式,让元素从起始态过渡到常规态,实现入场;transition-behavior: allow-discrete 使 display/visibility 参与过渡:入场时 display 立即变为可见值并播放动画,离场时动画结束后才切换为 display: none,保证退场动画可见。组合写法:在元素样式上声明 transition 与 allow-discrete,用 @starting-style 定义入场起始态,从而让弹层、列表项等原生显隐都带平滑动画。

考察入场动画的完整组合机制,属现代动画题。回答时说明插入 DOM 无起始状态可用 @starting-style 定义,allow-discrete 控制 display 切换时机。

.item {
  transition: opacity 0.3s, display 0.3s allow-discrete;
}

@starting-style {
  .item {
    opacity: 0;
  }
}
#
★★

51. popover 属性与 @starting-style/transition-behavior 组合实现原生弹层进出场动画的工程实践

popover 原生弹层如何与 @starting-style/transition-behavior 组合实现进出场动画?

  • popover 由浏览器管理显隐
  • 结合 @starting-style 定义入场态
  • allow-discrete 让退场可见

popover 属性让元素成为原生弹层(自动顶层、焦点管理、ESC 关闭),显隐由 showPopover/hidePopover 或 popovertarget 控制。为弹层加动画:常态样式定义目标态,@starting-style 定义 show 时的起始态,transition 声明 opacity/transform 与 display/visibility,transition-behavior: allow-discrete 使 hide 时先播完动画再 display:none,实现平滑进出场。实践上配合 prefers-reduced-motion 降级为瞬时显隐,并注意动画后状态一致。

考察原生弹层与动画组合的工程实现,属现代 UI 动画题。回答时说明 popover 原生显隐结合 @starting-style 入场与 allow-discrete 退场,注意 reduce 降级。

[popover] {
  transition: opacity 0.25s, transform 0.25s, display 0.25s allow-discrete;
  transform-origin: top center;
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: translateY(-8px);
  }
}
#
★★

52. View Transitions API 与 CSS transition/animation 的差异,SPA 路由过渡如何实现?

View Transitions API 与 CSS transition/animation 有什么差异?SPA 路由过渡如何实现?

  • View Transitions 对旧新状态拍照过渡
  • transition/animation 作用于单元素属性
  • SPA 用 document.startViewTransition 包裹路由更新

View Transitions API 在 DOM 更新时对旧状态与新状态分别拍照,并生成两个快照之间的过渡,作用范围是页面/区域级视觉过渡;CSS transition/animation 只对单个元素的具体属性做插值,无法自动感知 DOM 结构变化。SPA 路由过渡实现:在路由切换时调用 document.startViewTransition(() => 更新 DOM),浏览器自动捕捉新旧页面快照并播放默认淡入淡出,也可用 view-transition-name 为元素命名实现元素级联动动画。注意快照为伪元素,且需处理异步路由数据加载完成后再提交更新。

考察 View Transitions 与常规 CSS 动画的差异及 SPA 集成方式,属现代过渡题。回答时说明 View Transitions 对快照过渡而 transition/animation 对单元素属性,SPA 用 startViewTransition 包裹。

document.startViewTransition(async () => {
  await loadRoute();
  render(nextPage);
});
#
★★

53. 滚动驱动动画(scroll-driven animations)的实现与性能注意点?

滚动驱动动画如何实现?实现与性能上有什么注意点?

  • animation-timeline: scroll() 绑定滚动
  • animation-range 控制区间
  • 性能注意合成器属性与滚动容器

滚动驱动动画用 animation-timeline: scroll(container) 把动画进度绑定到滚动位置,animation-range 声明动画从滚动区间的哪个位置开始与结束;也可用 view() 时间线基于元素进入视口的进度。实现时动画属性仍推荐 transform/opacity 等合成器属性,避免每帧触发布局。性能注意点:绑定大量元素的滚动时间线会增加滚动期间的样式计算,应控制动画元素数量与范围;滚动容器选择要明确,避免监听意外祖先;配合 content-visibility 与懒渲染降低离屏成本,必要时用 rAF 节流或 IntersectionObserver 做开关。

考察滚动驱动动画的实现与性能治理,属滚动动画题。回答时说明 animation-timeline/range 绑定滚动进度,推荐合成器属性并控制动画元素数量。回答时说明合成器属性与元素数量控制。

.bar {
  animation: grow linear both;
  animation-timeline: scroll(root);
  animation-range: 0 100vh;
}
#

54. CSS Containment(contain: layout)在动画隔离与重排优化的边界

contain: layout 如何隔离重排?在动画场景中的边界是什么?

  • contain: layout 隔离内部布局影响
  • 子元素重排不扩散到外部
  • 尺寸变化仍需 size 包含

contain: layout 让元素的内部布局与外部隔离:内部子元素的重排不会传播到容器外部,浏览器可独立计算,减少动画或频繁更新时的全局重排范围。边界:layout 包含不限制元素自身尺寸变化对外部的影响,需要 contain: size(元素尺寸固定)才能避免尺寸变化扩散;且包含会改变定位与浮动的包含上下文,个别布局行为(如 position: fixed 相对包含块)会受影响。动画场景中常组合 contain: layout style 减少重算,但不要对需要自适应尺寸的容器使用 size。

考察 CSS containment 的隔离边界与组合使用,属性能优化题。回答时说明 layout 包含隔离内部重排,尺寸变化需 size 包含,注意包含上下文对定位的影响。

.list {
  contain: layout style;
}
#

55. 复合层动画与隐式合成在 Safari 与 Chrome 的实现差异

复合层动画与隐式合成在 Safari 与 Chrome 中有什么实现差异?

  • 各浏览器提升合成层的策略不同
  • 隐式合成可能意外创建大量层
  • 跨浏览器测试动画性能

复合层动画依赖元素被提升到独立合成层,浏览器对此的策略不同:Chrome 对 transform/opacity 动画、will-change 等积极提升图层,并提供层可视化工具;Safari 提升条件更保守,某些动画可能回退到主线程渲染,表现为掉帧。隐式合成指元素因与其他合成层元素重叠而被连带提升,Chrome 可能因此创建大量意外图层导致内存与合成开销上升,Safari 的处理也有差异。工程上避免过度使用 will-change 与 3D 变换,控制同屏合成层数量,并在目标浏览器实测帧率。

考察跨浏览器合成层策略差异与隐式合成风险,属动画性能题。回答时说明各浏览器提升合成层策略不同、隐式合成造成图层膨胀,需跨浏览器实测帧率。回答时说明控制合成层数量与实测帧率。

#

56. CSS 物理属性(inset-inline)在国际化布局与动画的工程价值

CSS 逻辑属性(如 inset-inline)在国际化布局与动画中有什么工程价值?

  • inset-inline 映射逻辑方向
  • 自动适配 RTL 书写模式
  • 动画中避免方向错位

inset-inline 等逻辑属性按书写模式(direction/writing-mode)映射物理方向:LTR 下 inset-inline-start 即 left,RTL 下自动变为 right,inset-block 对应垂直方向。工程价值:布局代码一次编写即可适配阿拉伯语等 RTL 页面,避免为镜像书写大量覆盖规则;动画中用它设置位移与定位,切换语言时动画方向自动镜像,保持语义一致。注意 transform 的 translateX 等仍为物理方向,复杂镜像动画需结合 direction 判断;新代码优先逻辑属性可降低国际化维护成本。

考察逻辑属性在 RTL 适配与动画方向的工程价值,属国际化动画题。回答时说明 inset-inline 随书写模式自动镜像,动画方向自动适配 RTL,transform 仍物理方向。

.slide {
  animation: enter 0.3s ease-out;
}

@keyframes enter {
  from { inset-inline-start: -16px; opacity: 0; }
  to { inset-inline-start: 0; opacity: 1; }
}
#

57. 动画帧率(FPS)与节流(requestAnimationFrame)在长任务中的取舍

动画帧率与 rAF 节流如何取舍?长任务场景下如何保持流畅?

  • rAF 每帧执行一次
  • 长任务阻塞主线程掉帧
  • 拆分任务与降低工作负载

浏览器按屏幕刷新率(通常 60Hz)调度 rAF,每帧回调执行动画更新;若回调或主线程其他长任务耗时超过帧预算(约 16.7ms),帧率下降出现卡顿。取舍:动画更新逻辑应轻量,避免在 rAF 中做重计算、DOM 批量写读;长任务应拆分(时间切片、Web Worker 或分帧处理),避免阻塞渲染;必要时降低动画复杂度或降帧(如后台标签页自动降频)。用 Performance 面板定位长任务,配合 requestIdleCallback 处理非紧急工作,保证关键动画帧稳定。

考察 rAF 与长任务对帧率的影响及优化思路,属动画性能题。回答时说明 rAF 每帧执行、长任务超帧预算掉帧,拆分任务与轻量回调保证流畅。回答时说明拆分任务与轻量回调思路。

function tick(ts) {
  update(ts);
  requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
#

58. 交错动画(Stagger Animation)通过 animation-delay 在列表动画的应用

如何用 animation-delay 实现列表交错动画?有什么注意点?

  • animation-delay 错开每个元素启动
  • 延迟递增形成波浪效果
  • 注意初始状态与 reduce 偏好

交错动画通过给列表元素设置递增的 animation-delay,让每个元素依次开始动画,形成波浪式的进入效果,如第 i 个元素 delay: i * 60ms。实现上可用 CSS 变量在行内设置 --i 并在动画中计算延迟,或用 JS 批量设置。注意点:延迟期间元素处于动画前状态,若首帧与常态差异大需配合 fill-mode: backwards 或初始样式,避免闪烁;延迟过大让用户久等,应限制交错时长;响应 prefers-reduced-motion 时直接取消交错;列表数量多时考虑只对可见项播放。

考察交错动画的实现与细节处理,属列表动画题。回答时说明递增 delay 实现交错、fill-mode 防闪烁、限制总时长并响应 reduce 偏好。回答时说明 fill-mode 防闪烁与总时长限制。

.item {
  animation: fadeUp 0.4s ease-out both;
  animation-delay: calc(var(--i) * 60ms);
}
#

59. CSS 弹性动画与物理引擎(弹簧动画)在交互动效的取舍

CSS 弹性动画与物理引擎(弹簧动画)在交互动效中如何取舍?

  • cubic-bezier 近似模拟回弹
  • 弹簧动画基于质量/刚度/阻尼
  • 复杂物理用引擎或 WAAPI

CSS 弹性动画用 cubic-bezier(如回弹曲线)或关键帧近似模拟弹性效果,实现简单、性能好,适合轻微回弹的小交互;弹簧动画基于质量、刚度与阻尼参数计算真实物理运动,能自然模拟拖拽释放、惯性等复杂手感。取舍:简单 UI 反馈(按钮按下、卡片弹出)用 CSS 曲线足够且零依赖;需要真实物理(拖拽、甩动、多对象碰撞)时用弹簧动画库(如 react-spring、motion)或自定义物理引擎。注意弹簧动画计算成本与动画可中断性,配合 reduce 偏好提供降级。

考察弹性/物理动画的实现路径选型,属动效实现题。回答时说明轻量回弹用 cubic-bezier,真实物理用弹簧库,注意计算成本与可中断性。回答时说明按交互复杂度选择方案。

#

60. View Transitions API 同文档与跨文档过渡在 Safari/Firefox 当前支持度下,如何用 @starting-style 与 animation-range 提供 fallback

View Transitions 在 Safari/Firefox 支持有限时如何用 @starting-style 与 animation-range 提供降级?

  • 同文档过渡支持优于跨文档
  • @starting-style 提供元素级入场
  • animation-range 提供滚动联动降级

View Transitions API 的同文档过渡(startViewTransition)在 Chrome/Edge 已可用,Safari 18.2+ 与 Firefox 已逐步支持;跨文档过渡支持更有限。降级策略:检测 document.startViewTransition 存在时启用路由快照过渡,不存在时回退为常规渲染;元素级入场用 @starting-style 加 transition 实现(主流浏览器支持),弥补页面级过渡缺失;需要滚动联动效果时用 animation-range 的滚动驱动动画替代部分视图过渡表现。整体遵循渐进增强:基础渲染优先,过渡作为增强层,并确保降级后无功能损失。

考察新特性在支持差异下的渐进增强降级,属兼容策略题。回答时说明同文档过渡支持优于跨文档,不支持时用 @starting-style 与常规动画渐进增强。回答时说明检测 startViewTransition 的写法。

const run = document.startViewTransition
  ? () => document.startViewTransition(render)
  : render;
run();
#

61. CSS 的 contain 属性如何用于性能优化,content-visibility 的收益与坑?

contain 属性如何用于性能优化?content-visibility 的收益与坑是什么?

  • contain 限制布局/绘制范围
  • content-visibility: auto 跳过离屏渲染
  • 需配合 contain-intrinsic-size

contain 通过限制元素的布局、样式、绘制与尺寸影响范围,减少子元素变化时向外扩散的重排与重绘,提升局部更新性能。content-visibility: auto 结合 contain 语义,跳过离屏元素的渲染,进入视口才渲染,显著提升长页面首屏性能。收益:减少初始渲染工作量、滚动更流畅。坑:跳过渲染时元素无尺寸,导致滚动跳动,必须配合 contain-intrinsic-size 占位;对需要精确尺寸、打印、查找与滚动定位的内容可能行为异常;慎用于表单与焦点元素,避免影响可访问性。

考察 contain 与 content-visibility 的优化原理与副作用,属性能优化综合题。回答时说明 content-visibility 跳过离屏渲染需 contain-intrinsic-size 占位,注意尺寸与可访问性副作用。

.long-list .row {
  content-visibility: auto;
  contain-intrinsic-size: auto 120px;
}