mui-theming

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MUI Theming Skill

MUI主题技能

createTheme() API

createTheme() API

The
createTheme
function is the entry point for all MUI customization. It accepts a deeply partial theme object and merges it with the defaults.
tsx
import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  palette: { ... },
  typography: { ... },
  spacing: 8,          // base spacing unit in px (default: 8)
  shape: { borderRadius: 8 },
  breakpoints: { ... },
  shadows: [...],
  transitions: { ... },
  zIndex: { ... },
});

createTheme
函数是所有MUI自定义的入口点。它接受一个深度部分主题对象,并将其与默认值合并。
tsx
import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  palette: { ... },
  typography: { ... },
  spacing: 8,          // 基础间距单位(像素,默认值:8)
  shape: { borderRadius: 8 },
  breakpoints: { ... },
  shadows: [...],
  transitions: { ... },
  zIndex: { ... },
});

Palette

调色板

Standard palette colors

标准调色板颜色

tsx
const theme = createTheme({
  palette: {
    primary: {
      light: '#757ce8',
      main: '#3f50b5',
      dark: '#002884',
      contrastText: '#fff',
    },
    secondary: {
      light: '#ff7961',
      main: '#f44336',
      dark: '#ba000d',
      contrastText: '#000',
    },
    error:   { main: '#d32f2f' },
    warning: { main: '#ed6c02' },
    info:    { main: '#0288d1' },
    success: { main: '#2e7d32' },
  },
});
MUI auto-generates
light
,
dark
, and
contrastText
from
main
if you omit them.
tsx
const theme = createTheme({
  palette: {
    primary: {
      light: '#757ce8',
      main: '#3f50b5',
      dark: '#002884',
      contrastText: '#fff',
    },
    secondary: {
      light: '#ff7961',
      main: '#f44336',
      dark: '#ba000d',
      contrastText: '#000',
    },
    error:   { main: '#d32f2f' },
    warning: { main: '#ed6c02' },
    info:    { main: '#0288d1' },
    success: { main: '#2e7d32' },
  },
});
如果你省略
light
dark
contrastText
,MUI会自动从
main
值生成它们。

Custom palette colors with augmentColor

使用augmentColor自定义调色板颜色

tsx
const theme = createTheme({
  palette: {
    // augmentColor adds light/dark/contrastText automatically
    neutral: theme.palette.augmentColor({
      color: { main: '#64748b' },
      name: 'neutral',
    }),
    brand: theme.palette.augmentColor({
      color: { main: '#6366f1', light: '#818cf8', dark: '#4f46e5' },
      name: 'brand',
    }),
  },
});
Use a two-step createTheme call when augmentColor needs the base theme:
tsx
let theme = createTheme();
theme = createTheme(theme, {
  palette: {
    salmon: theme.palette.augmentColor({
      color: { main: '#FF5733' },
      name: 'salmon',
    }),
  },
});
tsx
const theme = createTheme({
  palette: {
    // augmentColor会自动添加light/dark/contrastText
    neutral: theme.palette.augmentColor({
      color: { main: '#64748b' },
      name: 'neutral',
    }),
    brand: theme.palette.augmentColor({
      color: { main: '#6366f1', light: '#818cf8', dark: '#4f46e5' },
      name: 'brand',
    }),
  },
});
当augmentColor需要基础主题时,使用两步式createTheme调用:
tsx
let theme = createTheme();
theme = createTheme(theme, {
  palette: {
    salmon: theme.palette.augmentColor({
      color: { main: '#FF5733' },
      name: 'salmon',
    }),
  },
});

Background and text

背景与文本

tsx
palette: {
  background: {
    default: '#f5f5f5',
    paper: '#ffffff',
  },
  text: {
    primary: 'rgba(0,0,0,0.87)',
    secondary: 'rgba(0,0,0,0.6)',
    disabled: 'rgba(0,0,0,0.38)',
  },
  divider: 'rgba(0,0,0,0.12)',
  action: {
    active: 'rgba(0,0,0,0.54)',
    hover: 'rgba(0,0,0,0.04)',
    selected: 'rgba(0,0,0,0.08)',
    disabled: 'rgba(0,0,0,0.26)',
    disabledBackground: 'rgba(0,0,0,0.12)',
    focus: 'rgba(0,0,0,0.12)',
  },
},

tsx
palette: {
  background: {
    default: '#f5f5f5',
    paper: '#ffffff',
  },
  text: {
    primary: 'rgba(0,0,0,0.87)',
    secondary: 'rgba(0,0,0,0.6)',
    disabled: 'rgba(0,0,0,0.38)',
  },
  divider: 'rgba(0,0,0,0.12)',
  action: {
    active: 'rgba(0,0,0,0.54)',
    hover: 'rgba(0,0,0,0.04)',
    selected: 'rgba(0,0,0,0.08)',
    disabled: 'rgba(0,0,0,0.26)',
    disabledBackground: 'rgba(0,0,0,0.12)',
    focus: 'rgba(0,0,0,0.12)',
  },
},

Typography

排版

tsx
const theme = createTheme({
  typography: {
    fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
    fontSize: 14,             // base font size (rem calculation root)
    htmlFontSize: 16,         // <html> font size for rem calculations
    fontWeightLight: 300,
    fontWeightRegular: 400,
    fontWeightMedium: 500,
    fontWeightBold: 700,

    h1: { fontSize: '2.5rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '-0.01562em' },
    h2: { fontSize: '2rem',   fontWeight: 700, lineHeight: 1.3 },
    h3: { fontSize: '1.75rem', fontWeight: 600, lineHeight: 1.3 },
    h4: { fontSize: '1.5rem', fontWeight: 600, lineHeight: 1.4 },
    h5: { fontSize: '1.25rem', fontWeight: 600, lineHeight: 1.5 },
    h6: { fontSize: '1rem',   fontWeight: 600, lineHeight: 1.6 },

    subtitle1: { fontSize: '1rem',    fontWeight: 400, lineHeight: 1.75 },
    subtitle2: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.57 },
    body1: { fontSize: '1rem',    fontWeight: 400, lineHeight: 1.5 },
    body2: { fontSize: '0.875rem', fontWeight: 400, lineHeight: 1.43 },
    button: { fontSize: '0.875rem', fontWeight: 600, textTransform: 'none' }, // disable ALL_CAPS
    caption: { fontSize: '0.75rem', fontWeight: 400, lineHeight: 1.66 },
    overline: { fontSize: '0.75rem', fontWeight: 400, textTransform: 'uppercase', letterSpacing: '0.08333em' },
  },
});
tsx
const theme = createTheme({
  typography: {
    fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
    fontSize: 14,             // 基础字体大小(rem计算的根值)
    htmlFontSize: 16,         // <html>字体大小,用于rem计算
    fontWeightLight: 300,
    fontWeightRegular: 400,
    fontWeightMedium: 500,
    fontWeightBold: 700,

    h1: { fontSize: '2.5rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '-0.01562em' },
    h2: { fontSize: '2rem',   fontWeight: 700, lineHeight: 1.3 },
    h3: { fontSize: '1.75rem', fontWeight: 600, lineHeight: 1.3 },
    h4: { fontSize: '1.5rem', fontWeight: 600, lineHeight: 1.4 },
    h5: { fontSize: '1.25rem', fontWeight: 600, lineHeight: 1.5 },
    h6: { fontSize: '1rem',   fontWeight: 600, lineHeight: 1.6 },

    subtitle1: { fontSize: '1rem',    fontWeight: 400, lineHeight: 1.75 },
    subtitle2: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.57 },
    body1: { fontSize: '1rem',    fontWeight: 400, lineHeight: 1.5 },
    body2: { fontSize: '0.875rem', fontWeight: 400, lineHeight: 1.43 },
    button: { fontSize: '0.875rem', fontWeight: 600, textTransform: 'none' }, // 禁用全大写
    caption: { fontSize: '0.75rem', fontWeight: 400, lineHeight: 1.66 },
    overline: { fontSize: '0.75rem', fontWeight: 400, textTransform: 'uppercase', letterSpacing: '0.08333em' },
  },
});

Responsive typography

响应式排版

tsx
typography: {
  h1: {
    fontSize: '2rem',
    [theme.breakpoints.up('md')]: { fontSize: '3rem' },
    [theme.breakpoints.up('lg')]: { fontSize: '4rem' },
  },
},

tsx
typography: {
  h1: {
    fontSize: '2rem',
    [theme.breakpoints.up('md')]: { fontSize: '3rem' },
    [theme.breakpoints.up('lg')]: { fontSize: '4rem' },
  },
},

Spacing

间距

The spacing scale is factor-based. Default unit is 8px.
tsx
const theme = createTheme({ spacing: 8 }); // default

theme.spacing(1)   // '8px'
theme.spacing(2)   // '16px'
theme.spacing(0.5) // '4px'
theme.spacing(1, 2)        // '8px 16px'
theme.spacing(1, 2, 3, 4)  // '8px 16px 24px 32px'

// Custom spacing function
const theme = createTheme({
  spacing: (factor: number) => `${0.25 * factor}rem`,
});

间距系统基于因子计算,默认单位为8px。
tsx
const theme = createTheme({ spacing: 8 }); // 默认值

theme.spacing(1)   // '8px'
theme.spacing(2)   // '16px'
theme.spacing(0.5) // '4px'
theme.spacing(1, 2)        // '8px 16px'
theme.spacing(1, 2, 3, 4)  // '8px 16px 24px 32px'

// 自定义间距函数
const theme = createTheme({
  spacing: (factor: number) => `${0.25 * factor}rem`,
});

Breakpoints

断点

tsx
const theme = createTheme({
  breakpoints: {
    values: {
      xs: 0,
      sm: 600,
      md: 900,
      lg: 1200,
      xl: 1536,
      // Add custom breakpoints:
      mobile: 0,
      tablet: 640,
      laptop: 1024,
      desktop: 1200,
    },
  },
});

// Usage in sx
<Box sx={{ fontSize: { xs: '1rem', md: '1.5rem', lg: '2rem' } }} />

// Usage in styles
theme.breakpoints.up('md')      // '@media (min-width:900px)'
theme.breakpoints.down('md')    // '@media (max-width:899.95px)'
theme.breakpoints.between('sm', 'md') // '@media (min-width:600px) and (max-width:899.95px)'
theme.breakpoints.only('md')    // '@media (min-width:900px) and (max-width:1199.95px)'

tsx
const theme = createTheme({
  breakpoints: {
    values: {
      xs: 0,
      sm: 600,
      md: 900,
      lg: 1200,
      xl: 1536,
      // 添加自定义断点:
      mobile: 0,
      tablet: 640,
      laptop: 1024,
      desktop: 1200,
    },
  },
});

// 在sx属性中使用
<Box sx={{ fontSize: { xs: '1rem', md: '1.5rem', lg: '2rem' } }} />

// 在样式中使用
theme.breakpoints.up('md')      // '@media (min-width:900px)'
theme.breakpoints.down('md')    // '@media (max-width:899.95px)'
theme.breakpoints.between('sm', 'md') // '@media (min-width:600px) and (max-width:899.95px)'
theme.breakpoints.only('md')    // '@media (min-width:900px) and (max-width:1199.95px)'

Shape, Shadows, Transitions, zIndex

形状、阴影、过渡效果、zIndex

tsx
const theme = createTheme({
  shape: {
    borderRadius: 8,  // default: 4
  },
  // shadows[0] = 'none', shadows[1-24] = elevation levels
  shadows: [
    'none',
    '0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)',
    // ... (24 levels total — override individual elevations selectively)
    ...Array(23).fill('none'),
  ],
  transitions: {
    duration: {
      shortest: 150,
      shorter: 200,
      short: 250,
      standard: 300,
      complex: 375,
      enteringScreen: 225,
      leavingScreen: 195,
    },
    easing: {
      easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
      easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)',
      easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
      sharp: 'cubic-bezier(0.4, 0, 0.6, 1)',
    },
  },
  zIndex: {
    mobileStepper: 1000,
    fab: 1050,
    speedDial: 1050,
    appBar: 1100,
    drawer: 1200,
    modal: 1300,
    snackbar: 1400,
    tooltip: 1500,
  },
});

tsx
const theme = createTheme({
  shape: {
    borderRadius: 8,  // 默认值:4
  },
  // shadows[0] = 'none',shadows[1-24] = 不同层级的阴影
  shadows: [
    'none',
    '0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)',
    // ...(共24个层级——可选择性覆盖单个层级)
    ...Array(23).fill('none'),
  ],
  transitions: {
    duration: {
      shortest: 150,
      shorter: 200,
      short: 250,
      standard: 300,
      complex: 375,
      enteringScreen: 225,
      leavingScreen: 195,
    },
    easing: {
      easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
      easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)',
      easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
      sharp: 'cubic-bezier(0.4, 0, 0.6, 1)',
    },
  },
  zIndex: {
    mobileStepper: 1000,
    fab: 1050,
    speedDial: 1050,
    appBar: 1100,
    drawer: 1200,
    modal: 1300,
    snackbar: 1400,
    tooltip: 1500,
  },
});

ThemeProvider and CssBaseline

ThemeProvider与CssBaseline

tsx
import { ThemeProvider, CssBaseline } from '@mui/material';
import { createTheme } from '@mui/material/styles';

const theme = createTheme({ ... });

function App() {
  return (
    <ThemeProvider theme={theme}>
      {/* CssBaseline normalizes browser styles and sets body background */}
      <CssBaseline />
      <YourApp />
    </ThemeProvider>
  );
}

tsx
import { ThemeProvider, CssBaseline } from '@mui/material';
import { createTheme } from '@mui/material/styles';

const theme = createTheme({ ... });

function App() {
  return (
    <ThemeProvider theme={theme}>
      {/* CssBaseline用于统一浏览器样式并设置body背景 */}
      <CssBaseline />
      <YourApp />
    </ThemeProvider>
  );
}

Dark Mode

暗黑模式

Static dark mode

静态暗黑模式

tsx
const darkTheme = createTheme({
  palette: {
    mode: 'dark',
    // MUI auto-adjusts all palette colors for dark mode
    // Override as needed:
    primary: { main: '#90caf9' },
    background: {
      default: '#121212',
      paper: '#1e1e1e',
    },
  },
});
tsx
const darkTheme = createTheme({
  palette: {
    mode: 'dark',
    // MUI会自动为暗黑模式调整所有调色板颜色
    // 按需覆盖:
    primary: { main: '#90caf9' },
    background: {
      default: '#121212',
      paper: '#1e1e1e',
    },
  },
});

Dynamic dark mode with ColorModeContext

使用ColorModeContext实现动态暗黑模式

tsx
import React, { createContext, useContext, useMemo, useState } from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

// Create context
const ColorModeContext = createContext({ toggleColorMode: () => {} });

export function useColorMode() {
  return useContext(ColorModeContext);
}

export function ColorModeProvider({ children }: { children: React.ReactNode }) {
  const [mode, setMode] = useState<'light' | 'dark'>('light');

  const colorMode = useMemo(
    () => ({
      toggleColorMode: () =>
        setMode((prev) => (prev === 'light' ? 'dark' : 'light')),
    }),
    []
  );

  const theme = useMemo(
    () =>
      createTheme({
        palette: { mode },
      }),
    [mode]
  );

  return (
    <ColorModeContext.Provider value={colorMode}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        {children}
      </ThemeProvider>
    </ColorModeContext.Provider>
  );
}

// In a component:
function DarkModeToggle() {
  const { toggleColorMode } = useColorMode();
  return <IconButton onClick={toggleColorMode}><Brightness4Icon /></IconButton>;
}
tsx
import React, { createContext, useContext, useMemo, useState } from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

// 创建上下文
const ColorModeContext = createContext({ toggleColorMode: () => {} });

export function useColorMode() {
  return useContext(ColorModeContext);
}

export function ColorModeProvider({ children }: { children: React.ReactNode }) {
  const [mode, setMode] = useState<'light' | 'dark'>('light');

  const colorMode = useMemo(
    () => ({
      toggleColorMode: () =>
        setMode((prev) => (prev === 'light' ? 'dark' : 'light')),
    }),
    []
  );

  const theme = useMemo(
    () =>
      createTheme({
        palette: { mode },
      }),
    [mode]
  );

  return (
    <ColorModeContext.Provider value={colorMode}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        {children}
      </ThemeProvider>
    </ColorModeContext.Provider>
  );
}

// 在组件中使用:
function DarkModeToggle() {
  const { toggleColorMode } = useColorMode();
  return <IconButton onClick={toggleColorMode}><Brightness4Icon /></IconButton>;
}

System preference detection

系统偏好检测

tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function App() {
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
  const [mode, setMode] = useState<'light' | 'dark'>(
    prefersDark ? 'dark' : 'light'
  );

  const theme = useMemo(() => createTheme({ palette: { mode } }), [mode]);
  // ...
}

tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function App() {
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
  const [mode, setMode] = useState<'light' | 'dark'>(
    prefersDark ? 'dark' : 'light'
  );

  const theme = useMemo(() => createTheme({ palette: { mode } }), [mode]);
  // ...
}

Component-Level Overrides

组件级重写

styleOverrides

styleOverrides

Override CSS for specific component slots:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: '8px',
          textTransform: 'none',
          fontWeight: 600,
        },
        contained: {
          boxShadow: 'none',
          '&:hover': { boxShadow: '0 2px 8px rgba(0,0,0,0.15)' },
        },
        sizeLarge: {
          padding: '12px 24px',
          fontSize: '1rem',
        },
      },
    },
    MuiTextField: {
      styleOverrides: {
        root: {
          '& .MuiOutlinedInput-root': {
            borderRadius: '8px',
          },
        },
      },
    },
    MuiPaper: {
      styleOverrides: {
        root: ({ theme }) => ({
          backgroundImage: 'none',
          ...(theme.palette.mode === 'dark' && {
            backgroundColor: theme.palette.grey[900],
          }),
        }),
      },
    },
  },
});
覆盖特定组件插槽的CSS:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: '8px',
          textTransform: 'none',
          fontWeight: 600,
        },
        contained: {
          boxShadow: 'none',
          '&:hover': { boxShadow: '0 2px 8px rgba(0,0,0,0.15)' },
        },
        sizeLarge: {
          padding: '12px 24px',
          fontSize: '1rem',
        },
      },
    },
    MuiTextField: {
      styleOverrides: {
        root: {
          '& .MuiOutlinedInput-root': {
            borderRadius: '8px',
          },
        },
      },
    },
    MuiPaper: {
      styleOverrides: {
        root: ({ theme }) => ({
          backgroundImage: 'none',
          ...(theme.palette.mode === 'dark' && {
            backgroundColor: theme.palette.grey[900],
          }),
        }),
      },
    },
  },
});

defaultProps

defaultProps

Set default prop values for all instances of a component:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      defaultProps: {
        variant: 'contained',
        disableElevation: true,
      },
    },
    MuiTextField: {
      defaultProps: {
        variant: 'outlined',
        size: 'small',
        fullWidth: true,
      },
    },
    MuiCircularProgress: {
      defaultProps: {
        size: 24,
        thickness: 4,
      },
    },
    MuiLink: {
      defaultProps: {
        underline: 'hover',
      },
    },
  },
});
为组件的所有实例设置默认属性值:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      defaultProps: {
        variant: 'contained',
        disableElevation: true,
      },
    },
    MuiTextField: {
      defaultProps: {
        variant: 'outlined',
        size: 'small',
        fullWidth: true,
      },
    },
    MuiCircularProgress: {
      defaultProps: {
        size: 24,
        thickness: 4,
      },
    },
    MuiLink: {
      defaultProps: {
        underline: 'hover',
      },
    },
  },
});

Custom variants

自定义变体

Add new named variants to existing components:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: 'dashed' },
          style: {
            border: '2px dashed currentColor',
            backgroundColor: 'transparent',
            '&:hover': {
              backgroundColor: 'rgba(0,0,0,0.04)',
            },
          },
        },
        {
          props: { variant: 'gradient' },
          style: ({ theme }) => ({
            background: `linear-gradient(135deg, ${theme.palette.primary.main} 0%, ${theme.palette.secondary.main} 100%)`,
            color: theme.palette.primary.contrastText,
            border: 'none',
          }),
        },
      ],
    },
  },
});

为现有组件添加新的命名变体:
tsx
const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: 'dashed' },
          style: {
            border: '2px dashed currentColor',
            backgroundColor: 'transparent',
            '&:hover': {
              backgroundColor: 'rgba(0,0,0,0.04)',
            },
          },
        },
        {
          props: { variant: 'gradient' },
          style: ({ theme }) => ({
            background: `linear-gradient(135deg, ${theme.palette.primary.main} 0%, ${theme.palette.secondary.main} 100%)`,
            color: theme.palette.primary.contrastText,
            border: 'none',
          }),
        },
      ],
    },
  },
});

Nested Themes and Theme Composition

嵌套主题与主题组合

tsx
// Merge two themes
import { deepmerge } from '@mui/utils';

const baseTheme = createTheme({ ... });
const extendedTheme = createTheme(deepmerge(baseTheme, {
  typography: { h1: { fontSize: '3rem' } },
}));

// Nested ThemeProvider (child theme overrides parent locally)
function AdminPanel() {
  const parentTheme = useTheme();
  const adminTheme = createTheme(parentTheme, {
    palette: {
      primary: { main: '#dc2626' }, // red for admin
    },
  });

  return (
    <ThemeProvider theme={adminTheme}>
      <AdminUI />
    </ThemeProvider>
  );
}

tsx
// 合并两个主题
import { deepmerge } from '@mui/utils';

const baseTheme = createTheme({ ... });
const extendedTheme = createTheme(deepmerge(baseTheme, {
  typography: { h1: { fontSize: '3rem' } },
}));

// 嵌套ThemeProvider(子主题会在局部覆盖父主题)
function AdminPanel() {
  const parentTheme = useTheme();
  const adminTheme = createTheme(parentTheme, {
    palette: {
      primary: { main: '#dc2626' }, // 管理员主题使用红色
    },
  });

  return (
    <ThemeProvider theme={adminTheme}>
      <AdminUI />
    </ThemeProvider>
  );
}

TypeScript Module Augmentation

TypeScript模块扩展

Extend the theme types to support custom colors and variables:
tsx
// theme-augmentation.d.ts (or in your theme file)
import '@mui/material/styles';
import '@mui/material/Button';

declare module '@mui/material/styles' {
  // Add custom palette colors
  interface Palette {
    neutral: Palette['primary'];
    brand: Palette['primary'];
    custom: {
      gradientStart: string;
      gradientEnd: string;
    };
  }
  interface PaletteOptions {
    neutral?: PaletteOptions['primary'];
    brand?: PaletteOptions['primary'];
    custom?: {
      gradientStart?: string;
      gradientEnd?: string;
    };
  }

  // Add custom theme variables
  interface Theme {
    custom: {
      navHeight: number;
      sidebarWidth: number;
    };
  }
  interface ThemeOptions {
    custom?: {
      navHeight?: number;
      sidebarWidth?: number;
    };
  }

  // Extend typography variants
  interface TypographyVariants {
    code: React.CSSProperties;
    label: React.CSSProperties;
  }
  interface TypographyVariantsOptions {
    code?: React.CSSProperties;
    label?: React.CSSProperties;
  }
}

// Allow usage of custom variants on Typography component
declare module '@mui/material/Typography' {
  interface TypographyPropsVariantOverrides {
    code: true;
    label: true;
  }
}

// Allow usage of custom palette colors on Button
declare module '@mui/material/Button' {
  interface ButtonPropsColorOverrides {
    neutral: true;
    brand: true;
  }
}
With augmentation in place, usage is fully typed:
tsx
const theme = createTheme({
  palette: {
    neutral: theme.palette.augmentColor({ color: { main: '#64748b' }, name: 'neutral' }),
    custom: { gradientStart: '#667eea', gradientEnd: '#764ba2' },
  },
  custom: { navHeight: 64, sidebarWidth: 240 },
  typography: {
    code: { fontFamily: 'monospace', fontSize: '0.85rem', backgroundColor: 'rgba(0,0,0,0.06)' },
  },
});

// Fully typed:
<Button color="neutral">Neutral Button</Button>
<Typography variant="code">const x = 1;</Typography>
const navH = theme.custom.navHeight; // typed as number

扩展主题类型以支持自定义颜色和变量:
tsx
// theme-augmentation.d.ts(或在你的主题文件中)
import '@mui/material/styles';
import '@mui/material/Button';

declare module '@mui/material/styles' {
  // 添加自定义调色板颜色
  interface Palette {
    neutral: Palette['primary'];
    brand: Palette['primary'];
    custom: {
      gradientStart: string;
      gradientEnd: string;
    };
  }
  interface PaletteOptions {
    neutral?: PaletteOptions['primary'];
    brand?: PaletteOptions['primary'];
    custom?: {
      gradientStart?: string;
      gradientEnd?: string;
    };
  }

  // 添加自定义主题变量
  interface Theme {
    custom: {
      navHeight: number;
      sidebarWidth: number;
    };
  }
  interface ThemeOptions {
    custom?: {
      navHeight?: number;
      sidebarWidth?: number;
    };
  }

  // 扩展排版变体
  interface TypographyVariants {
    code: React.CSSProperties;
    label: React.CSSProperties;
  }
  interface TypographyVariantsOptions {
    code?: React.CSSProperties;
    label?: React.CSSProperties;
  }
}

// 允许在Typography组件上使用自定义变体
declare module '@mui/material/Typography' {
  interface TypographyPropsVariantOverrides {
    code: true;
    label: true;
  }
}

// 允许在Button上使用自定义调色板颜色
declare module '@mui/material/Button' {
  interface ButtonPropsColorOverrides {
    neutral: true;
    brand: true;
  }
}
完成扩展后,使用时会获得完整的类型支持:
tsx
const theme = createTheme({
  palette: {
    neutral: theme.palette.augmentColor({ color: { main: '#64748b' }, name: 'neutral' }),
    custom: { gradientStart: '#667eea', gradientEnd: '#764ba2' },
  },
  custom: { navHeight: 64, sidebarWidth: 240 },
  typography: {
    code: { fontFamily: 'monospace', fontSize: '0.85rem', backgroundColor: 'rgba(0,0,0,0.06)' },
  },
});

// 完整类型支持:
<Button color="neutral">中性按钮</Button>
<Typography variant="code">const x = 1;</Typography>
const navH = theme.custom.navHeight; // 类型为number

Design Token Patterns

设计令牌模式

Centralize all design decisions as named tokens, then reference them throughout the theme:
tsx
// design-tokens.ts
export const tokens = {
  color: {
    brand: {
      50: '#eff6ff',
      100: '#dbeafe',
      500: '#3b82f6',
      600: '#2563eb',
      900: '#1e3a8a',
    },
    neutral: {
      50: '#f8fafc',
      100: '#f1f5f9',
      500: '#64748b',
      900: '#0f172a',
    },
    semantic: {
      success: '#16a34a',
      warning: '#d97706',
      error: '#dc2626',
      info: '#0284c7',
    },
  },
  spacing: {
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
    '2xl': 48,
    '3xl': 64,
  },
  typography: {
    fontFamily: {
      sans: '"Inter", system-ui, sans-serif',
      mono: '"JetBrains Mono", "Fira Code", monospace',
    },
    scale: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
      '4xl': '2.25rem',
    },
  },
  radius: {
    sm: 4,
    md: 8,
    lg: 12,
    xl: 16,
    full: 9999,
  },
} as const;

// theme.ts
import { createTheme } from '@mui/material/styles';
import { tokens } from './design-tokens';

export const theme = createTheme({
  palette: {
    primary: {
      light: tokens.color.brand[100],
      main: tokens.color.brand[500],
      dark: tokens.color.brand[900],
    },
    success: { main: tokens.color.semantic.success },
    warning: { main: tokens.color.semantic.warning },
    error:   { main: tokens.color.semantic.error },
    info:    { main: tokens.color.semantic.info },
  },
  typography: {
    fontFamily: tokens.typography.fontFamily.sans,
    h1: { fontSize: tokens.typography.scale['4xl'] },
    h2: { fontSize: tokens.typography.scale['3xl'] },
    h3: { fontSize: tokens.typography.scale['2xl'] },
    body1: { fontSize: tokens.typography.scale.base },
    body2: { fontSize: tokens.typography.scale.sm },
  },
  shape: { borderRadius: tokens.radius.md },
  spacing: (factor: number) => `${tokens.spacing.xs * factor}px`,
});

将所有设计决策集中为命名令牌,然后在整个主题中引用它们:
tsx
// design-tokens.ts
export const tokens = {
  color: {
    brand: {
      50: '#eff6ff',
      100: '#dbeafe',
      500: '#3b82f6',
      600: '#2563eb',
      900: '#1e3a8a',
    },
    neutral: {
      50: '#f8fafc',
      100: '#f1f5f9',
      500: '#64748b',
      900: '#0f172a',
    },
    semantic: {
      success: '#16a34a',
      warning: '#d97706',
      error: '#dc2626',
      info: '#0284c7',
    },
  },
  spacing: {
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
    '2xl': 48,
    '3xl': 64,
  },
  typography: {
    fontFamily: {
      sans: '"Inter", system-ui, sans-serif',
      mono: '"JetBrains Mono", "Fira Code", monospace',
    },
    scale: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
      '4xl': '2.25rem',
    },
  },
  radius: {
    sm: 4,
    md: 8,
    lg: 12,
    xl: 16,
    full: 9999,
  },
} as const;

// theme.ts
import { createTheme } from '@mui/material/styles';
import { tokens } from './design-tokens';

export const theme = createTheme({
  palette: {
    primary: {
      light: tokens.color.brand[100],
      main: tokens.color.brand[500],
      dark: tokens.color.brand[900],
    },
    success: { main: tokens.color.semantic.success },
    warning: { main: tokens.color.semantic.warning },
    error:   { main: tokens.color.semantic.error },
    info:    { main: tokens.color.semantic.info },
  },
  typography: {
    fontFamily: tokens.typography.fontFamily.sans,
    h1: { fontSize: tokens.typography.scale['4xl'] },
    h2: { fontSize: tokens.typography.scale['3xl'] },
    h3: { fontSize: tokens.typography.scale['2xl'] },
    body1: { fontSize: tokens.typography.scale.base },
    body2: { fontSize: tokens.typography.scale.sm },
  },
  shape: { borderRadius: tokens.radius.md },
  spacing: (factor: number) => `${tokens.spacing.xs * factor}px`,
});

Complete Theme Example

完整主题示例

tsx
// theme/index.ts
import { createTheme, responsiveFontSizes } from '@mui/material/styles';

let theme = createTheme({
  palette: {
    mode: 'light',
    primary:   { main: '#2563eb' },
    secondary: { main: '#7c3aed' },
    error:     { main: '#dc2626' },
    warning:   { main: '#d97706' },
    info:      { main: '#0284c7' },
    success:   { main: '#16a34a' },
    background: { default: '#f8fafc', paper: '#ffffff' },
  },
  typography: {
    fontFamily: '"Inter", "Roboto", sans-serif',
    fontWeightBold: 700,
    button: { textTransform: 'none', fontWeight: 600 },
  },
  shape: { borderRadius: 8 },
  components: {
    MuiButton: {
      defaultProps: { disableElevation: true },
      styleOverrides: {
        root: { borderRadius: 8, padding: '8px 20px' },
      },
    },
    MuiCard: {
      defaultProps: { elevation: 0 },
      styleOverrides: {
        root: { border: '1px solid', borderColor: 'divider', borderRadius: 12 },
      },
    },
  },
});

// Automatically scale typography for different screen sizes
theme = responsiveFontSizes(theme);

export default theme;

tsx
// theme/index.ts
import { createTheme, responsiveFontSizes } from '@mui/material/styles';

let theme = createTheme({
  palette: {
    mode: 'light',
    primary:   { main: '#2563eb' },
    secondary: { main: '#7c3aed' },
    error:     { main: '#dc2626' },
    warning:   { main: '#d97706' },
    info:      { main: '#0284c7' },
    success:   { main: '#16a34a' },
    background: { default: '#f8fafc', paper: '#ffffff' },
  },
  typography: {
    fontFamily: '"Inter", "Roboto", sans-serif',
    fontWeightBold: 700,
    button: { textTransform: 'none', fontWeight: 600 },
  },
  shape: { borderRadius: 8 },
  components: {
    MuiButton: {
      defaultProps: { disableElevation: true },
      styleOverrides: {
        root: { borderRadius: 8, padding: '8px 20px' },
      },
    },
    MuiCard: {
      defaultProps: { elevation: 0 },
      styleOverrides: {
        root: { border: '1px solid', borderColor: 'divider', borderRadius: 12 },
      },
    },
  },
});

// 自动为不同屏幕尺寸缩放字体
theme = responsiveFontSizes(theme);

export default theme;

Extensible Theme Packaging

可扩展主题打包

Ship a branded theme as an npm package for multi-app reuse.
ts
// @your-org/mui-theme/src/index.ts
import { createTheme, type ThemeOptions } from '@mui/material/styles';
import { tokens } from './tokens';
import { componentOverrides } from './components';

// Base branded theme
export const brandedThemeOptions: ThemeOptions = {
  palette: {
    primary: { main: tokens.color.brand[500] },
    secondary: { main: tokens.color.brand[700] },
  },
  typography: {
    fontFamily: tokens.typography.fontFamily.sans,
    button: { textTransform: 'none', fontWeight: 600 },
  },
  shape: { borderRadius: 8 },
  components: componentOverrides,
};

// Create with optional per-app overrides
export function createBrandedTheme(...overrides: ThemeOptions[]) {
  return createTheme(brandedThemeOptions, ...overrides);
}

// Pre-built themes
export const lightTheme = createBrandedTheme();
export const darkTheme = createBrandedTheme({
  palette: { mode: 'dark' },
});
Consumer app layers overrides on top:
ts
import { createBrandedTheme } from '@your-org/mui-theme';

const appTheme = createBrandedTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          '&:hover': { transform: 'translateY(-2px)' },
        },
      },
    },
  },
});
Array styleOverrides merging: When extending, use arrays to preserve base styles:
ts
root: [
  brandedComponents.MuiButton.styleOverrides.root,
  { '&:hover': { transform: 'translateY(-2px)' } },
]

将品牌主题作为npm包发布,用于多应用复用。
ts
// @your-org/mui-theme/src/index.ts
import { createTheme, type ThemeOptions } from '@mui/material/styles';
import { tokens } from './tokens';
import { componentOverrides } from './components';

// 基础品牌主题
export const brandedThemeOptions: ThemeOptions = {
  palette: {
    primary: { main: tokens.color.brand[500] },
    secondary: { main: tokens.color.brand[700] },
  },
  typography: {
    fontFamily: tokens.typography.fontFamily.sans,
    button: { textTransform: 'none', fontWeight: 600 },
  },
  shape: { borderRadius: 8 },
  components: componentOverrides,
};

// 创建主题时可传入可选的应用级覆盖配置
export function createBrandedTheme(...overrides: ThemeOptions[]) {
  return createTheme(brandedThemeOptions, ...overrides);
}

// 预构建主题
export const lightTheme = createBrandedTheme();
export const darkTheme = createBrandedTheme({
  palette: { mode: 'dark' },
});
消费应用可在基础主题上添加覆盖配置:
ts
import { createBrandedTheme } from '@your-org/mui-theme';

const appTheme = createBrandedTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          '&:hover': { transform: 'translateY(-2px)' },
        },
      },
    },
  },
});
数组式styleOverrides合并:扩展时使用数组保留基础样式:
ts
root: [
  brandedComponents.MuiButton.styleOverrides.root,
  { '&:hover': { transform: 'translateY(-2px)' } },
]

Theme as Rules Engine

主题作为规则引擎

Encode design decisions in the theme and enforce at compile time via module augmentation.
将设计决策编码到主题中,并通过模块扩展在编译时强制执行。

Restrict Allowed Variants

限制允许的变体

ts
// Disable 'text' variant for Buttons in your design system
declare module '@mui/material/Button' {
  interface ButtonPropsVariantOverrides {
    text: false;        // ← compile error if used
    dashed: true;       // ← new custom variant
    gradient: true;     // ← new custom variant
  }
  interface ButtonPropsColorOverrides {
    inherit: false;     // ← disable inherit
    brand: true;        // ← custom brand color
    neutral: true;      // ← custom neutral color
  }
  interface ButtonPropsSizeOverrides {
    extraLarge: true;   // ← custom size
  }
}
Now
<Button variant="text">
is a TypeScript error — design rules enforced at compile time.
ts
// 在你的设计系统中禁用Button的'text'变体
declare module '@mui/material/Button' {
  interface ButtonPropsVariantOverrides {
    text: false;        // ← 使用时会触发编译错误
    dashed: true;       // ← 新的自定义变体
    gradient: true;     // ← 新的自定义变体
  }
  interface ButtonPropsColorOverrides {
    inherit: false;     // ← 禁用inherit颜色
    brand: true;        // ← 自定义品牌颜色
    neutral: true;      // ← 自定义中性颜色
  }
  interface ButtonPropsSizeOverrides {
    extraLarge: true;   // ← 自定义尺寸
  }
}
现在
<Button variant="text">
会触发TypeScript错误——设计规则在编译时得到强制执行。

Register Custom Components in Theme

在主题中注册自定义组件

ts
// Register MuiStat in the theme like a built-in component
declare module '@mui/material/styles' {
  interface Components {
    MuiStat?: {
      defaultProps?: Partial<StatProps>;
      styleOverrides?: {
        root?: any;
        value?: any;
        label?: any;
      };
      variants?: Array<{
        props: Partial<StatProps>;
        style: any;
      }>;
    };
  }
}

// Now you can configure it globally via theme
const theme = createTheme({
  components: {
    MuiStat: {
      defaultProps: { trend: 'flat' },
      styleOverrides: {
        root: ({ theme }) => ({
          backgroundColor: theme.palette.background.paper,
          '&:hover': { borderColor: theme.palette.primary.main },
        }),
      },
    },
  },
});
ts
// 将MuiStat像内置组件一样注册到主题中
declare module '@mui/material/styles' {
  interface Components {
    MuiStat?: {
      defaultProps?: Partial<StatProps>;
      styleOverrides?: {
        root?: any;
        value?: any;
        label?: any;
      };
      variants?: Array<{
        props: Partial<StatProps>;
        style: any;
      }>;
    };
  }
}

// 现在你可以通过主题全局配置它
const theme = createTheme({
  components: {
    MuiStat: {
      defaultProps: { trend: 'flat' },
      styleOverrides: {
        root: ({ theme }) => ({
          backgroundColor: theme.palette.background.paper,
          '&:hover': { borderColor: theme.palette.primary.main },
        }),
      },
    },
  },
});

Function-Based Variant Props Matching

基于函数的变体属性匹配

ts
// Complex conditional variant matching
const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          // Function-based matching for complex rules
          props: (props) =>
            props.variant === 'dashed' && props.color !== 'secondary',
          style: {
            border: '2px dashed currentColor',
            backgroundColor: 'transparent',
          },
        },
      ],
    },
  },
});
ts
// 复杂条件变体匹配
const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          // 基于函数的匹配,支持复杂规则
          props: (props) =>
            props.variant === 'dashed' && props.color !== 'secondary',
          style: {
            border: '2px dashed currentColor',
            backgroundColor: 'transparent',
          },
        },
      ],
    },
  },
});