返回博客

MUI 桥接 CSS 变量 palette 时 theme.alpha 抛 Unsupported color

最近在把 MUI 组件库接入一套用 CSS 自定义属性驱动的设计系统时踩了一个很隐蔽的坑。桥主题创建完毕,简单的展示件(RatingStepper)跑得好好的,一上 DatePickerPagination 就炸了。最坑的地方: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,只支持 #hexrgb()hsl()color() 这几种格式,完全不会处理 var(--x)——于是直接抛 Unsupported color。

坑:禁用涟漪和补齐 augmentColor 字段都救不了你

直觉上以为加 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() 安全流过去。