MUI 桥接 CSS 变量 palette 时 theme.alpha 抛 Unsupported color
最近在把 MUI 组件库接入一套用 CSS 自定义属性驱动的设计系统时踩了一个很隐蔽的坑。桥主题创建完毕,简单的展示件(Rating、Stepper)跑得好好的,一上 DatePicker 和 Pagination 就炸了。最坑的地方:disableRipple 完全没用,单测和真实浏览器都复现。
现象
报错长这样:
Error: MUI: Unsupported `var(--color-primary)` color.
The following formats are supported: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color().
受害组件的共同特征是——内部含有 MUI 核心 IconButton:DateCalendar 的翻月箭头、DatePicker 的日历打开按钮、Pagination 的页码箭头、Autocomplete 的清除叉、Chip 的删除按钮……只要用了这类组件,桥主题一挂上去必崩。
反过来,纯展示件不涉及 IconButton 的渲染,不触发这个路径,容易误判为「桥主题没问题」。
根因
MUI 核心组件的样式通过 memoTheme 在样式序列化阶段(不是运行时按需)eager 地对每个 palette 颜色调用:
theme.alpha(theme.palette[color].main, theme.palette.action.hoverOpacity)预先算出 hover/focus 的半透明底色。这个 theme.alpha 底层走的是 colorManipulator,只支持 #hex、rgb()、hsl()、color() 这几种格式,完全不会处理 var(--x)——于是直接抛 Unsupported color。
直觉上以为加 disableRipple 就能绕过 alpha 计算,但样式序列化发生在组件挂载前,跟运行时交互无关,ripple 根本没机会被禁用。
补齐 { main, light, dark, contrastText } 也解决不了这个问题——那是 augmentColor 的创建期逻辑,而 alpha 是 MUI 内部在消费期主动调用的,两个阶段完全不同。
正解
在创建桥主题后,把 theme.alpha 整体换成输出 color-mix() 的实现。color-mix() 是 CSS 原生函数,天生能吃 var():
// theme.ts —— createTheme 之后一次性打补丁
export const bridgeTheme = createTheme({
palette: {
primary: { main: "var(--color-primary)", light: "var(--color-primary-light)", dark: "var(--color-primary-dark)", contrastText: "var(--color-primary-contrast)" },
// ...其他 palette 槽位
},
});
// 关键:覆盖 alpha,让它产出 color-mix() 而非走 colorManipulator
bridgeTheme.alpha = (color: string, coefficient: string | number) =>
`color-mix(in srgb, ${color} ${Math.round(Number(coefficient) * 100)}%, transparent)`;coefficient 是 MUI 内部传的 0–1 透明度值,乘以 100 即为 color-mix 的百分比。
直接对已创建的 bridgeTheme 实例赋值 alpha 方法即可——MUI 的 Theme 类型把它声明为可写方法,TypeScript 不报错,不需要 as any。
把补丁写在 theme.ts 里统一做一次,而不是在每个出问题的组件里各自 createTheme 再套 <ThemeProvider>。后者能跑但很啰嗦,而且下次再来一个含 IconButton 的新组件还会漏。集中改一次,现有和未来的桥件都覆盖到。
实测守卫
把含 IconButton 的组件(比如 DateCalendar)渲染在 BridgeProvider(全局挂已打补丁主题的 ThemeProvider)下写一个单测。如果 alpha 没修,测试渲染时就会抛 Unsupported color,测试即守卫,杜绝回归。
若目标 MUI 版本支持 createTheme({ cssVariables: true }),让 MUI 自身走 CSS 变量体系,本补丁可以退役。但在「palette 直接填外部设计系统 var()」的桥接模式下,重写 alpha 是当前最小、最确定性的解。
一句话外卖
MUI 的
theme.alpha在样式序列化时 eager 消费 palette,不等渲染——桥接 CSS 变量 palette 时,必须把theme.alpha整体换成输出color-mix()的实现,才能让var()安全流过去。