mcpField
W
web-ui-mui

web-ui-mui

github.com/agents-inc设计与 UI2636 次安装2 天前更新
代码仓库

关于 web-ui-mui

web-ui-mui 是面向 React 开发者的 MUI 模式技能指南,涵盖主题、组件样式、布局、深色模式与 MUI X。它说明不同定制层的适用场景,并提供版本迁移、性能及常见问题参考。

主要功能

  1. 01配置主题与 ThemeProvider
  2. 02选择 sx、styled 和主题覆盖
  3. 03使用 slots 与 slotProps
  4. 04实现色彩方案和深色模式
  5. 05参考布局与 MUI X 组件模式

适用场景

  1. 01为 React 应用建立一致的 MUI 主题
  2. 02决定组件样式定制所用的层级
  3. 03实现兼顾服务端渲染的深色模式
  4. 04开发 DataGrid、日期选择器和图表界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill web-ui-mui

安装后在 Claude Code 中输入/web-ui-mui确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
core.md
data-grid.md
feedback.md
form-inputs.md
layout.md
navigation.md
styling.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 会接收该插槽自身的状态。请在渲染函数外定义插槽组件——内联箭头函数每次都会创建新的组件类型,导致每次渲染时插槽都重新挂载。

完整代码:examples/form-inputs.md


模式 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) 等于 16px
  • sx 数组会从左向右合并,因此后面的条目会覆盖前面的条目——条件样式正是借此实现的
  • TextField 实际上由三个组件组成(输入框、标签、辅助文本),因此要定位实际的 <input>,需使用 slotProps.input
  • 手动设置 zIndex 会与 MUI 自身的层级刻度冲突;模态框、抽屉、提示条和工具提示都已有指定值
  • useMediaQuery 在服务端渲染期间会返回 false,因此桌面优先的分支会在水合前渲染错误的内容
  • useColorScheme().mode 在首次渲染时为 undefined;根据它渲染依赖配色方案的图标会导致水合不匹配
  • 自定义调色板颜色缺少 TypeScript 扩展时,会在使用处报类型错误,而不是在主题处报错
  • v7 中 components 和 componentsProps 仍可使用,但已弃用

评论

支持 Markdown