1. 题目与适用场景
设计系统有一组间距令牌,组件需要根据密度和容器宽度计算 padding。团队想减少重复的 calc(),同时保留纯 CSS、主题覆盖和旧浏览器可用性。请使用 CSS @function 设计方案,并说明未支持浏览器如何获得安全回退。
2. 面试官考察点
- 是否理解
@function定义的是 CSS 自定义函数,调用名必须是带--的 dashed-ident。 - 是否能正确声明参数、默认值、可选类型和
result,并理解函数体不会像 JavaScript 一样遇到声明就立即返回。 - 是否能区分自定义函数、自定义属性、Sass 编译期函数和运行时级联。
- 是否能处理特性检测、主题继承、循环依赖、浏览器兼容和可访问的渐进增强。
3. 回答前需要澄清的问题
- 目标浏览器范围是否包含尚未实现 CSS Functions 的版本?
- 令牌是固定长度、百分比,还是允许
calc()与容器单位? - 函数的参数错误时应采用默认值、整条声明失效,还是由组件提供回退?
- 主题令牌来自
:root、组件作用域还是 Shadow DOM?
4. 30 秒回答框架
我会把函数定义与回退声明放在同一规则中:先写可工作的静态 padding,再用 @supports (top: --probe()) 覆盖为自定义函数结果。函数使用类型约束和默认参数,最后通过 result 产生长度。自定义属性负责主题输入,函数负责计算;旧浏览器忽略未知函数并保留前面的回退声明。
5. 分步骤深入解答
第一步:定义参数与返回值
@function --space(--step <integer>: 2, --base <length>: 4px) {
result: calc(var(--base) * var(--step));
}参数是局部自定义属性,可声明 CSS 类型和默认值。result 提供函数最终值;它不是 JavaScript 的提前 return,函数声明规则仍会继续按 CSS 语义处理。
第二步:把函数接到设计令牌
:root {
--space-base: 4px;
}
.card {
padding: 16px;
padding: --space(3, var(--space-base));
}第一条 padding 是旧浏览器回退,第二条只在能解析自定义函数时生效。令牌仍通过自定义属性级联,组件可以在主题或局部作用域覆盖 --space-base。
第三步:处理类型和无效值
类型约束用于尽早拒绝不符合语法的数据;默认值只覆盖参数缺省,不会把任意字符串变成合法长度。应为负值、单位错误和缺少令牌定义设计测试,并避免把用户可控字符串直接拼进 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 中检查无效计算值。