必须在自定义Sass入口中按顺序导入functions、variables,用map-merge扩展$utilities后导入utilities;新增项需严格遵循property→responsive→state→value四层结构,补全null及响应式断点,并避免class前缀与内置utility冲突。
$utilities Sass map 是唯一入口想加一个 .text-uppercase-soft 或 .bg-gradient-horiz?不能靠写独立 CSS 文件覆盖,必须修改 Bootstrap 的 $utilities——这是它 Utility API 的核心数据结构,所有 text-*、p-*, m-* 类都从这里生成。直接在 _utilities.scss 里追加条目无效,因为官方构建流程只读取你传给 utility-generator 的那个 map。
实操上,你需要在自定义 Sass 入口(比如 custom.scss)中:先 @import "bootstrap/scss/functions" 和 "bootstrap/scss/variables",再用 map-merge 扩展 $utilities,最后 @import "bootstrap/scss/utilities"。顺序错一步,新类就不会编译出来。
每个 utility 条目是四层嵌套 map:property → responsive → state → value。漏掉 responsive(比如没写 sm 或 null),该类就不会响应式;漏掉 state(如 hover),伪类就失效;value 不是字符串或 map,Sass 编译直接报错 Invalid CSS after "...": expected "{", was ";"。
常见错误示例:
// ❌ 错误:缺 responsive 层,.shadow-heavy 永远不生成$utilities: map-merge($utilities, ( "shadow-heavy": ( property: box-shadow, values: (heavy: 0 12px 24px rgba(0,0,0,.2)) )));<p>// ✅ 正确:补全 null(默认断点)和 hover 等可选状态$utilities: map-merge($utilities, ("shadow-heavy": (property: box-shadow,class: shadow,values: (null: 0 12px 24px rgba(0,0,0,.2),hover: 0 16px 32px rgba(0,0,0,.25)))));
Bootstrap 默认的 text-* 已经占用了 text 这个 class 前缀。如果你新增一个 "text-blur": (property: backdrop-filter, ...),最终生成的类名会是 .text-blur——但它和 .text-primary 共享 text- 前缀,CSS 优先级完全取决于源码顺序,不是你后写的就一定生效。
更危险的是:如果新 utility 的 property 和已有 utility 冲突(比如两个都设 property: color),后者会完全覆盖前者,且无任何警告。建议:
blur-、bgx-,避免和 text-、bg- 碰撞bootstrap/scss/_variables.scss 里的 $theme-colors 和 $utilities 原始定义,确认 key 名未被占用npx sass --watch src/custom.scss:dist/css/custom.css 后,打开生成的 CSS 搜索你的新 class 名,确认是否真的存在Bootstrap 5 的 $grid-breakpoints 定义了 xs/sm/md/lg/xl/xxl,但 $utilities 默认只对 sm 及以上生成媒体查询。如果你希望 .pad-vert-xs 在 xs 断点也生效,不能只写 xs: 1rem,还必须把 null(即无断点的基础版)也带上,否则 xs 版本会覆盖基础版,导致桌面端反而没 padding。
正确写法要分两步:
$utilities: map-merge($utilities, ( "pad-vert": ( property: padding-block, class: pad-vert, values: ( null: 0.5rem, // 基础值(<sm) xs: 0.5rem, // xs 断点(≥320px) sm: 1rem, // sm 断点(≥576px) md: 1.5rem // md 断点(≥768px) ) )));
真正容易被忽略的是:Sass 编译时不会校验你写的断点名是否真实存在于 $grid-breakpoints。填个 xxx: 12px,它就默默忽略,既不报错也不生成对应类——得靠人工核对或 grep 输出 CSS 验证。