代表性面试主题

前端面试题:如何用 CSS @function 复用设计令牌?

前端困难
Offer.cc 编辑团队发布 更新

题干

请实现一个把间距令牌转换为响应式长度的 CSS @function,并说明它与自定义属性、Sass 函数及浏览器降级的边界。

1. 题目与适用场景

设计系统有一组间距令牌,组件需要根据密度和容器宽度计算 padding。团队想减少重复的 calc(),同时保留纯 CSS、主题覆盖和旧浏览器可用性。请使用 CSS @function 设计方案,并说明未支持浏览器如何获得安全回退。

2. 面试官考察点

  • 是否理解 @function 定义的是 CSS 自定义函数,调用名必须是带 -- 的 dashed-ident。
  • 是否能正确声明参数、默认值、可选类型和 result,并理解函数体不会像 JavaScript 一样遇到声明就立即返回。
  • 是否能区分自定义函数、自定义属性、Sass 编译期函数和运行时级联。
  • 是否能处理特性检测、主题继承、循环依赖、浏览器兼容和可访问的渐进增强。

3. 回答前需要澄清的问题

  1. 目标浏览器范围是否包含尚未实现 CSS Functions 的版本?
  2. 令牌是固定长度、百分比,还是允许 calc() 与容器单位?
  3. 函数的参数错误时应采用默认值、整条声明失效,还是由组件提供回退?
  4. 主题令牌来自 :root、组件作用域还是 Shadow DOM?

4. 30 秒回答框架

我会把函数定义与回退声明放在同一规则中:先写可工作的静态 padding,再用 @supports (top: --probe()) 覆盖为自定义函数结果。函数使用类型约束和默认参数,最后通过 result 产生长度。自定义属性负责主题输入,函数负责计算;旧浏览器忽略未知函数并保留前面的回退声明。

5. 分步骤深入解答

第一步:定义参数与返回值

css
@function --space(--step <integer>: 2, --base <length>: 4px) {
  result: calc(var(--base) * var(--step));
}

参数是局部自定义属性,可声明 CSS 类型和默认值。result 提供函数最终值;它不是 JavaScript 的提前 return,函数声明规则仍会继续按 CSS 语义处理。

第二步:把函数接到设计令牌

css
:root {
  --space-base: 4px;
}

.card {
  padding: 16px;
  padding: --space(3, var(--space-base));
}

第一条 padding 是旧浏览器回退,第二条只在能解析自定义函数时生效。令牌仍通过自定义属性级联,组件可以在主题或局部作用域覆盖 --space-base

第三步:处理类型和无效值

类型约束用于尽早拒绝不符合语法的数据;默认值只覆盖参数缺省,不会把任意字符串变成合法长度。应为负值、单位错误和缺少令牌定义设计测试,并避免把用户可控字符串直接拼进 CSS 函数参数。

第四步:理解作用域和依赖

函数参数在函数体内表现为局部自定义属性,调用点仍受级联、继承和层叠顺序影响。函数依赖的令牌不应再反向依赖函数结果,否则可能形成循环自定义属性,整条声明会变为无效。

第五步:特性检测与渐进增强

css
.card {
  padding: 16px;
}

@supports (padding: --space(3, 4px)) {
  .card {
    padding: --space(3, var(--space-base, 4px));
  }
}

@supports 只判断解析能力,不能证明所有函数分支都符合业务约束。仍应保留稳定回退、检查计算后的布局,并在不支持时确保文本、焦点和触控目标不被压缩。

6. 高质量示范回答

我会先提供静态 padding,再在 @supports 中使用 @function 计算令牌。自定义属性负责主题级联,函数参数声明长度或整数类型并通过 result 返回值。错误输入不能依赖字符串拼接;我会覆盖缺省参数、单位错误、负值和循环依赖。由于 CSS Functions 仍有兼容性差异,回退声明必须完整可用,并通过实际浏览器验证计算样式和可访问尺寸。

7. 常见错误

  • 把 CSS @function 当成 Sass 编译期函数 → 发布后才发现浏览器不解析 → 保留运行时回退并做 @supports 检测。
  • 在参数里传入无单位数字却声明 <length> → 整条声明失效 → 明确类型和默认单位。
  • 认为函数会修改自定义属性 → 主题覆盖不生效 → 把令牌输入与 result 输出分开。
  • 只写 @supports 没有基础声明 → 不支持的浏览器出现默认间距 → 先写可用回退。
  • 用函数相互引用 → 循环依赖使计算值无效 → 保持令牌依赖方向单向。

8. 追问及应对

追问一:它和自定义属性有什么区别?

自定义属性保存可级联的值;自定义函数接收参数并计算结果。函数可以复用计算逻辑,但输入仍可来自继承的自定义属性。

追问二:它和 Sass 函数有什么区别?

Sass 函数在构建时生成 CSS,无法读取运行时主题或容器状态;CSS 自定义函数在浏览器级联和计算阶段执行,能响应运行时令牌,但需要处理兼容性。

追问三:如何检测函数是否可用?

@supports (padding: --space(1, 4px)) 检测语法解析能力,同时保留前置回退。测试不能只看规则是否被接受,还要读取实际计算值。

追问四:如何避免循环依赖?

让基础令牌只依赖原始值,函数只读取令牌并输出派生值;禁止令牌再引用包含该函数结果的属性,并在 DevTools 中检查无效计算值。

公开来源

同类题目