关于 web-ui-mui
web-ui-mui 是面向 React 开发者的 MUI 模式技能指南,涵盖主题、组件样式、布局、深色模式与 MUI X。它说明不同定制层的适用场景,并提供版本迁移、性能及常见问题参考。
主要功能
- 01配置主题与 ThemeProvider
- 02选择 sx、styled 和主题覆盖
- 03使用 slots 与 slotProps
- 04实现色彩方案和深色模式
- 05参考布局与 MUI X 组件模式
适用场景
- 01为 React 应用建立一致的 MUI 主题
- 02决定组件样式定制所用的层级
- 03实现兼顾服务端渲染的深色模式
- 04开发 DataGrid、日期选择器和图表界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-ui-mui安装后在 Claude Code 中输入/web-ui-mui确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/core.mddata-grid.mdfeedback.mdform-inputs.mdlayout.mdnavigation.mdstyling.md详细介绍
MUI(Material UI)模式
快速指南: MUI 是一个基于主题构建的预样式 React 组件库。
createTheme+ThemeProvider定义了所有组件读取的设计令牌;sx用于设置单个实例的样式,styled()用于创建可复用组件,theme.components用于更改所有实例的样式,slots/slotProps则用于访问 内部元素。当前版本:v7.x(2025 年 3 月)——CSS layers、slots/slotProps已全面标准化, Grid v2 已升级为Grid,并兼容 React 19。MUI X v8 涵盖 DataGrid、DatePicker 和 Charts。Emotion 是样式引擎;Pigment CSS 仍处于 alpha 阶段,目前暂停推进。
详细资源:
- examples/core.md — 配色方案主题、组件覆盖、深色模式菜单、TypeScript 扩展,以及使用 CSS layers 的 SSR 和 SPA 设置
- examples/styling.md —
styled()、通过shouldForwardProp设置自定义属性、适配主题的sx、响应式形式和回调形式 - examples/form-inputs.md — TextField、Select、Autocomplete、slots 和 slotProps
- examples/layout.md — Grid、Stack、Box、Container、响应式卡片网格
- examples/navigation.md — AppBar、Drawer、Tabs、响应式仪表板框架
- examples/feedback.md — Dialog、Snackbar、Alert、Skeleton、CircularProgress
- examples/data-grid.md — DataGrid 列、分页、单元格渲染
- reference.md — 组件表、主题结构、
sx简写、断点、软件包列表、v6 → v7 迁移、反模式代码
<critical_requirements>
编写 MUI 代码前
用 createTheme() 实例将应用包裹在 ThemeProvider 中。 如果没有,组件会回退到 MUI 的默认主题,虽然能正确渲染,但样式不对——不会报错,只是用了别人的设计。
从路径导入,而不是从汇总入口导入——使用 @mui/material/Button,而不是从
"@mui/material" 导入 { Button }。汇总入口会让开发服务器在首次加载时解析整个包,而
单是 @mui/icons-material 就有 2000 多个模块。
使用 slots 和 slotProps 访问内部元素。 它们是所有组件通用的 v7 API;
components 和 componentsProps 已弃用,并计划移除。
将暗色模式分支写成 theme.applyStyles("dark", { … })。 它会生成两组规则,由
CSS 选择,因此服务端和客户端的结果一致。theme.palette.mode === "dark" 会在渲染时作出判断,
这正是水合前闪现错误主题的原因。
</critical_requirements>
自动检测: MUI、Material UI、@mui/material、@mui/system、@mui/icons-material、 @mui/x-data-grid、@mui/x-date-pickers、createTheme、ThemeProvider、CssBaseline、sx prop、styled、 useTheme、useColorScheme、colorSchemes、cssVariables、applyStyles、slots、slotProps、 shouldForwardProp、GridColDef、StyledEngineProvider、enableCssLayer
适用范围:
- 基于 MUI 组件集构建,并通过主题进行定制,而不是通过分叉修改
- 针对特定覆盖场景,在
sx、styled()、theme.components和slots/slotProps之间作出选择 - 配色方案和暗色模式,包括可在服务端渲染中正常工作的 CSS 变量方案
- 通过模块扩充为自定义调色板颜色和字体排版变体添加类型
- MUI X——DataGrid、日期选择器、图表
其他内容另行处理:
- MUI 自身主题以外的 CSS 方法论和设计令牌架构——
enableCssLayer会将 MUI 样式放入命名图层, 让外部样式表能够设置与其之间的顺序;至于如何编写该样式表,则属于其他内容 - 无样式原语——此库自带完整样式,而想要掌控每个像素的项目应改用无头原语
- 表单状态和验证——
TextField接受error和helperText来呈现错误,也接受inputRef将底层<input>交给按 ref 注册的库;这些值由什么决定不属于本技能的讨论范围 - 服务端状态——组件通过 props 接收数据
主题就是产品。 调色板、字体排版、间距、阴影、断点、z-index 和过渡效果都集中在同一个对象中,每个组件都会读取它。在调用处编写覆盖意味着以后需要重复作出这一决定;将同一决定写入 theme.components,则只需作出一次。
定制分层进行,选择哪一层本身就是一种决策。 sx 用于当前实例,styled() 用于可复用变体,theme.components 用于所有实例,slots/slotProps 用于访问你不拥有的组件内部元素。使用超出当前需求的更重层级,通常会导致样式日后无法修改。
MUI 组件是客户端组件。 它们使用上下文和副作用,因此服务端渲染的应用需要在 ThemeProvider 上方放置缓存提供程序,并在使用这些组件的页面周围设置客户端边界。
<decision_framework>
选择哪种样式层
Used in one place → sx prop
Reused, same shape each time → styled()
Every instance in the app → theme.components.MuiX
An element inside the component → slots / slotProps
选择哪种布局组件
One axis, even spacing → Stack
Twelve-column grid → Grid (size prop)
Centred page with a max width → Container
Anything else that needs sx → Box
配色方案
No dark mode → one palette in createTheme
Follow the system only → cssVariables: true
User can choose → colorSchemes + useColorScheme
Both, without a flash → cssVariables: { colorSchemeSelector: "data" } + useColorScheme
MUI X 数据展示
Under ~100 rows, read-only → Table
Sorting, filtering, pagination → DataGrid
Very large datasets → DataGridPro (row virtualisation)
Date or time entry → DatePicker / DateTimePicker
Charts → MUI X Charts
Hierarchy → TreeView
</decision_framework>
核心模式
模式 1:主题和提供程序
const theme = createTheme({
palette: { primary: { main: "#1976d2" } },
typography: {
fontFamily: '"Inter", sans-serif',
button: { textTransform: "none" },
},
shape: { borderRadius: 8 },
spacing: 8,
});
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>;
CssBaseline 会将 MUI 的规范化样式以及主题的背景色和文字颜色应用于 <body>——
否则组件周围的页面仍会保留浏览器默认样式。
完整代码:examples/core.md
模式 2:CSS 变量和深色模式
const theme = createTheme({
cssVariables: { colorSchemeSelector: "data" },
colorSchemes: {
light: { palette: { primary: { main: "#1976d2" } } },
dark: { palette: { primary: { main: "#90caf9" } } },
},
});
const { mode, setMode } = useColorScheme(); // "light" | "dark" | "system"
使用 cssVariables 时,两套调色板都会作为自定义属性输出,并通过 data-* 属性在两者之间切换,因此配色方案会在 React 运行前确定。首次渲染时,mode 为 undefined——在渲染任何依赖配色方案的内容前,先检查它是否有值。
完整代码:examples/core.md
模式 3:sx 属性
<Box
sx={{
p: 3, // theme.spacing(3)
bgcolor: "background.paper", // theme.palette.background.paper
borderRadius: 1, // theme.shape.borderRadius
boxShadow: 3, // theme.shadows[3]
width: { xs: "100%", md: "50%" },
"&:hover": { boxShadow: 6 },
}}
/>
数值会通过主题的刻度解析,带点号的字符串会根据调色板解析,因此主题值无需重复写成字面量。断点对象可以替代媒体查询,而 sx 对象数组会从左向右合并——这样无需重新构建对象,就能添加条件样式。
完整代码:examples/styling.md
模式 4:styled()
const StyledCard = styled(Card)(({ theme }) => ({
padding: theme.spacing(3),
"&:hover": { boxShadow: theme.shadows[8] },
...theme.applyStyles("dark", { backgroundColor: theme.palette.grey[900] }),
}));
同一种样式会在多个地方出现时,使用这种方式。第二个参数可以接收 shouldForwardProp,以阻止仅用于样式的属性作为未知属性传递到 DOM。
完整代码:examples/styling.md
模式 5:插槽和 slotProps
<Autocomplete
slots={{ paper: CustomPaper }}
slotProps={{
paper: { elevation: 8, sx: { borderRadius: 2 } },
listbox: { sx: { maxHeight: 300 } },
input: ({ open }) => ({
sx: { borderColor: open ? "primary.main" : "divider" },
}),
}}
renderInput={(params) => <TextField {...params} label="Framework" />}
/>
slots 用于替换内部组件,slotProps 用于配置内部组件,而以回调形式编写的 slotProp 会接收该插槽自身的状态。请在渲染函数外定义插槽组件——内联箭头函数每次都会创建新的组件类型,导致每次渲染时插槽都重新挂载。
模式 6:布局
<Grid container spacing={3}>
<Grid size={{ xs: 12, md: 4 }}><Sidebar /></Grid>
<Grid size={{ xs: 12, md: 8 }}><MainContent /></Grid>
</Grid>
<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
<Button variant="contained">Save</Button>
</Stack>
<Container maxWidth="lg" sx={{ py: 4 }}>{children}</Container>
在 v7 中,Grid 就是之前的 Grid2,使用一个 size 属性,而不是单独的 xs/sm/md 属性;之前的组件仍可通过 GridLegacy 使用。
完整代码:examples/layout.md
模式 7:TypeScript 扩展声明
declare module "@mui/material/styles" {
interface Palette {
neutral: Palette["primary"];
}
interface PaletteOptions {
neutral?: PaletteOptions["primary"];
}
}
declare module "@mui/material/Button" {
interface ButtonPropsColorOverrides {
neutral: true;
}
}
需要两处扩展声明,缺一不可:第一处让主题中存在该令牌,第二处让组件可以将其用作属性值。若省略第二处,即使主题中包含该令牌,每个调用处的 color="neutral" 仍会产生类型错误。
完整代码:examples/core.md
模式 8:服务端渲染配置
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<GlobalStyles styles="@layer theme, base, mui, components, utilities;" />
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</AppRouterCacheProvider>
Emotion 会在组件渲染时生成样式,因此服务端渲染的应用需要在 ThemeProvider 上方设置缓存提供程序,以便收集样式并随流式 HTML 一同输出——否则页面标记到达时没有样式,并会在水合时重新应用样式。MUI 会为每种受支持的服务端框架发布一个适配器包;仅客户端运行的应用则在相同位置使用 StyledEngineProvider。enableCssLayer 会将 MUI 的输出放入一个具名图层,而 @layer 声明用于确定该图层与页面上其他内容之间的顺序。
完整代码,包含两种配置方式:examples/core.md
性能
按路径导入。 import Button from "@mui/material/Button" 可跳过对 barrel 的解析;使用 barrel 形式会让开发服务器启动耗时增加数倍。部分打包器和框架会自动重写 barrel 导入——请先检查,再判断这一成本是否适用。使用限制 ^@mui/[^/]+$ 模式的 lint 规则,可以防止这种导入方式再次出现。
凡是组件会将其视为标识的内容,都要保持引用稳定。 DataGrid 的 columns、插槽组件以及 Dialog 的 TransitionProps 都会按引用进行比较:请在模块作用域中定义它们,或使用 memoise。若以内联方式定义,它们会在父组件每次渲染时触发重新渲染或重新挂载。
createTheme 不会影响打包体积——它是数据,而不是组件。打包体积取决于导入了哪些组件。
<red_flags>
易出问题之处
运行时会出问题:
- 在样式函数中使用
theme.palette.mode === "dark"——该值在渲染时解析,因此服务端和客户端首次绘制时的结果不一致,会短暂闪现错误的主题 - 在
slots中使用内联箭头函数——每次渲染都会创建新的组件类型,因此插槽会重新挂载并丢失状态和焦点 - 在 DataGrid 中内联定义
columns——每次渲染都会创建新数组,导致整个网格重新渲染 - 从超过一层的深层路径导入,例如
@mui/material/styles/createTheme——v7 的 ESM 包布局不会暴露这些路径 - 为 Alert、Skeleton 或 Autocomplete 导入
@mui/lab——它们在 v7 中已迁移到@mui/material - 在服务端渲染 MUI 组件,却没有设置客户端边界——这些组件需要上下文和副作用;边界应设在页面上,而不应只设在根布局上
容易出乎意料的行为:
spacing是乘数,不是像素值:默认基数为 8 时,spacing(2)等于 16pxsx数组会从左向右合并,因此后面的条目会覆盖前面的条目——条件样式正是借此实现的TextField实际上由三个组件组成(输入框、标签、辅助文本),因此要定位实际的<input>,需使用slotProps.input- 手动设置
zIndex会与 MUI 自身的层级刻度冲突;模态框、抽屉、提示条和工具提示都已有指定值 useMediaQuery在服务端渲染期间会返回false,因此桌面优先的分支会在水合前渲染错误的内容useColorScheme().mode在首次渲染时为undefined;根据它渲染依赖配色方案的图标会导致水合不匹配- 自定义调色板颜色缺少 TypeScript 扩展时,会在使用处报类型错误,而不是在主题处报错
- v7 中
components和componentsProps仍可使用,但已弃用