Material-UI 样式系统 #

概述 #

MUI v5 使用 Emotion 作为默认的样式引擎,提供了多种灵活的样式方案。

text
样式方案
│
├── sx prop        内联样式(最常用)
├── styled API     CSS-in-JS 组件
├── styleOverrides 主题覆盖
├── className      传统 CSS 类
└── global CSS     全局样式

sx Prop #

sx prop 是 MUI 最强大的样式工具,允许直接在组件上编写 CSS。

基础用法 #

jsx
import { Box } from '@mui/material';

<Box
  sx={{
    width: 100,
    height: 100,
    bgcolor: 'primary.main',
    color: 'white',
    p: 2,
    m: 1,
  }}
>
  sx 样式
</Box>

主题值 #

jsx
<Box sx={{ color: 'primary.main' }}>主色调文字</Box>
<Box sx={{ color: 'secondary.light' }}>次色调浅色</Box>
<Box sx={{ bgcolor: 'error.dark' }}>错误背景</Box>
<Box sx={{ p: 2 }}>padding: 16px</Box>
<Box sx={{ m: 3 }}>margin: 24px</Box>
<Box sx={{ borderRadius: 1 }}>borderRadius: 8px</Box>

响应式值 #

jsx
<Box
  sx={{
    width: {
      xs: '100%',
      sm: '80%',
      md: '60%',
      lg: '50%',
    },
    p: {
      xs: 1,
      sm: 2,
      md: 3,
    },
    fontSize: {
      xs: '0.875rem',
      md: '1rem',
      lg: '1.25rem',
    },
  }}
>
  响应式样式
</Box>

伪类和伪元素 #

jsx
<Box
  sx={{
    '&:hover': {
      bgcolor: 'primary.dark',
      transform: 'translateY(-2px)',
    },
    '&:active': {
      transform: 'translateY(0)',
    },
    '&:focus': {
      outline: '2px solid primary.main',
    },
    '&::before': {
      content: '"→"',
      mr: 1,
    },
  }}
>
  带伪类的 Box
</Box>

嵌套选择器 #

jsx
<Box
  sx={{
    '& .child': {
      p: 2,
      bgcolor: 'grey.100',
    },
    '& .child:hover': {
      bgcolor: 'grey.200',
    },
    '& .active': {
      bgcolor: 'primary.light',
    },
  }}
>
  <div className="child">子元素 1</div>
  <div className="child active">子元素 2</div>
</Box>

条件样式 #

jsx
function ConditionalBox({ isActive, isDisabled }) {
  return (
    <Box
      sx={{
        bgcolor: isActive ? 'primary.main' : 'grey.200',
        opacity: isDisabled ? 0.5 : 1,
        cursor: isDisabled ? 'not-allowed' : 'pointer',
      }}
    >
      条件样式
    </Box>
  );
}

数组语法 #

jsx
<Box
  sx={[
    { bgcolor: 'primary.main' },
    isActive && { bgcolor: 'secondary.main' },
    isLarge && { p: 4 },
  ]}
>
  数组样式
</Box>

函数语法 #

jsx
<Box sx={(theme) => ({
  bgcolor: theme.palette.primary.main,
  p: theme.spacing(2),
  borderRadius: theme.shape.borderRadius,
})}>
  函数样式
</Box>

styled API #

styled API 用于创建可复用的样式组件。

基础用法 #

jsx
import { styled } from '@mui/material/styles';

const StyledBox = styled('div')({
  padding: 16,
  backgroundColor: '#f5f5f5',
  borderRadius: 8,
});

<StyledBox>样式组件</StyledBox>

使用主题 #

jsx
const StyledButton = styled('button')(({ theme }) => ({
  padding: theme.spacing(1, 2),
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.primary.contrastText,
  borderRadius: theme.shape.borderRadius,
  border: 'none',
  cursor: 'pointer',
  '&:hover': {
    backgroundColor: theme.palette.primary.dark,
  },
}));

扩展 MUI 组件 #

jsx
import { Button } from '@mui/material';

const CustomButton = styled(Button)(({ theme }) => ({
  borderRadius: 20,
  textTransform: 'none',
  fontWeight: 600,
  padding: theme.spacing(1.5, 3),
}));

动态样式 #

jsx
const DynamicBox = styled('div')(({ theme, $bgColor, $size }) => ({
  backgroundColor: $bgColor || theme.palette.primary.main,
  padding: $size === 'large' ? theme.spacing(4) : theme.spacing(2),
  borderRadius: theme.shape.borderRadius,
}));

<DynamicBox $bgColor="secondary.main" $size="large">
  动态样式
</DynamicBox>

嵌套选择器 #

jsx
const Card = styled('div')(({ theme }) => ({
  padding: theme.spacing(2),
  backgroundColor: theme.palette.background.paper,
  borderRadius: theme.shape.borderRadius,
  '& .title': {
    fontSize: '1.25rem',
    fontWeight: 600,
    marginBottom: theme.spacing(1),
  },
  '& .content': {
    color: theme.palette.text.secondary,
  },
  '&:hover': {
    boxShadow: theme.shadows[4],
  },
}));

组合样式 #

jsx
const baseStyles = ({ theme }) => ({
  padding: theme.spacing(2),
  borderRadius: theme.shape.borderRadius,
});

const primaryStyles = ({ theme }) => ({
  ...baseStyles({ theme }),
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.primary.contrastText,
});

const PrimaryCard = styled('div')(primaryStyles);

sx vs styled #

使用 sx 的场景 #

jsx
<Box sx={{ p: 2, bgcolor: 'primary.main' }}>
  快速原型开发
</Box>

<Button sx={{ mt: 2, borderRadius: 20 }}>
  一次性样式
</Button>

使用 styled 的场景 #

jsx
const CustomButton = styled(Button)(({ theme }) => ({
  borderRadius: 20,
  textTransform: 'none',
  fontWeight: 600,
}));

<CustomButton>可复用组件</CustomButton>

性能对比 #

特性 sx prop styled
性能 略慢 更快
可复用性
开发速度
类型安全

样式覆盖 #

通过 sx prop #

jsx
<Button
  sx={{
    bgcolor: 'secondary.main',
    '&:hover': {
      bgcolor: 'secondary.dark',
    },
  }}
>
  自定义按钮
</Button>

通过 styled #

jsx
const CustomTextField = styled(TextField)({
  '& .MuiOutlinedInput-root': {
    borderRadius: 12,
    '& fieldset': {
      borderColor: 'primary.main',
    },
    '&:hover fieldset': {
      borderColor: 'primary.dark',
    },
  },
});

通过主题 #

jsx
const theme = createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 20,
          textTransform: 'none',
        },
      },
    },
  },
});

通过 className #

jsx
import { styled } from '@mui/material/styles';

const CustomButton = styled(Button)`
  border-radius: 20px;
  text-transform: none;
`;

全局样式 #

GlobalStyles 组件 #

jsx
import { GlobalStyles } from '@mui/material';

<GlobalStyles
  styles={{
    '*': {
      boxSizing: 'border-box',
    },
    html: {
      scrollBehavior: 'smooth',
    },
    body: {
      margin: 0,
      padding: 0,
    },
    '::-webkit-scrollbar': {
      width: 8,
    },
    '::-webkit-scrollbar-track': {
      background: '#f1f1f1',
    },
    '::-webkit-scrollbar-thumb': {
      background: '#888',
      borderRadius: 4,
    },
  }}
/>

在主题中定义 #

jsx
const theme = createTheme({
  components: {
    MuiCssBaseline: {
      styleOverrides: {
        body: {
          scrollbarColor: '#6b6b6b #2b2b2b',
          '&::-webkit-scrollbar, & *::-webkit-scrollbar': {
            backgroundColor: '#2b2b2b',
          },
          '&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb': {
            borderRadius: 8,
            backgroundColor: '#6b6b6b',
          },
        },
      },
    },
  },
});

CSS 变量 #

启用 CSS 变量 #

jsx
const theme = createTheme({
  cssVariables: true,
  palette: {
    primary: {
      main: '#1976d2',
    },
  },
});

使用 CSS 变量 #

jsx
<Box sx={{ color: 'var(--mui-palette-primary-main)' }}>
  使用 CSS 变量
</Box>

自定义 CSS 变量 #

jsx
const theme = createTheme({
  cssVariables: {
    cssVarPrefix: 'my-app',
  },
  palette: {
    primary: {
      main: '#1976d2',
    },
  },
});

实战示例:设计系统组件 #

jsx
import { styled, alpha } from '@mui/material/styles';
import { Button, Card, TextField } from '@mui/material';

const PrimaryButton = styled(Button)(({ theme }) => ({
  borderRadius: 24,
  padding: theme.spacing(1.5, 3),
  textTransform: 'none',
  fontWeight: 600,
  boxShadow: 'none',
  '&:hover': {
    boxShadow: 'none',
    backgroundColor: alpha(theme.palette.primary.main, 0.9),
  },
}));

const GlassCard = styled(Card)(({ theme }) => ({
  backgroundColor: alpha(theme.palette.background.paper, 0.8),
  backdropFilter: 'blur(10px)',
  borderRadius: 16,
  border: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
  boxShadow: '0 8px 32px rgba(0, 0, 0, 0.1)',
}));

const RoundedTextField = styled(TextField)(({ theme }) => ({
  '& .MuiOutlinedInput-root': {
    borderRadius: 12,
    '& fieldset': {
      borderColor: alpha(theme.palette.primary.main, 0.2),
    },
    '&:hover fieldset': {
      borderColor: alpha(theme.palette.primary.main, 0.4),
    },
    '&.Mui-focused fieldset': {
      borderColor: theme.palette.primary.main,
    },
  },
}));

function DesignSystemDemo() {
  return (
    <Stack spacing={2}>
      <PrimaryButton variant="contained">Primary Button</PrimaryButton>
      <GlassCard sx={{ p: 3 }}>
        <Typography>Glass Card</Typography>
      </GlassCard>
      <RoundedTextField label="Rounded Input" />
    </Stack>
  );
}

下一步 #

继续学习 响应式设计,了解断点系统和响应式布局技巧!

最后更新:2026-03-28