跳到内容
+

样式库互操作性

虽然您可以使用 Material UI 提供的基于 Emotion 的样式解决方案,但您也可以使用您已经熟悉的方案,从纯 CSS 到 styled-components。

本指南旨在记录最流行的替代方案,但您应该发现此处应用的原则可以适用于其他库。以下是一些样式解决方案的示例

纯 CSS

没什么特别的,只是纯 CSS。

Edit Button

PlainCssSlider.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}
PlainCssSlider.css
import * as React from 'react';
import Slider from '@mui/material/Slider';
import './PlainCssSlider.css';

export default function PlainCssSlider() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className="slider" />
    </div>
  );
}

CSS 注入顺序 ⚠️

注意: 大多数 CSS-in-JS 解决方案将其样式注入到 HTML <head> 的底部,这使得 Material UI 优先于您的自定义样式。为了消除对 !important 的需求,您需要更改 CSS 注入顺序。这是一个演示如何在 Material UI 中完成此操作的示例

import * as React from 'react';
import { StyledEngineProvider } from '@mui/material/styles';

export default function GlobalCssPriority() {
  return (
    <StyledEngineProvider injectFirst>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </StyledEngineProvider>
  );
}

注意: 如果您正在使用 Emotion 并在您的应用程序中使用了自定义缓存,那么该缓存将覆盖来自 Material UI 的缓存。为了使注入顺序仍然正确,您需要添加 prepend 选项。这是一个示例

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function PlainCssPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </CacheProvider>
  );
}

注意: 如果您正在使用 styled-components 并且具有带有自定义 targetStyleSheetManager,请确保 target 是 HTML <head> 中的第一个元素。如果您想了解如何完成此操作,可以查看 @mui/styled-engine-sc 包中的 StyledEngineProvider 实现。

更深层的元素

如果您尝试样式化 Slider,您可能需要影响 Slider 的一些子元素,例如 thumb。在 Material UI 中,所有子元素的特异性都增加了 2:.parent .child {}。在编写覆盖时,您需要执行相同的操作。

以下示例除了 slider 本身的自定义样式外,还覆盖了 slider 的 thumb 样式。

PlainCssSliderDeep1.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .MuiSlider-thumb {
  border-radius: 1px;
}
PlainCssSliderDeep1.js
import * as React from 'react';
import Slider from '@mui/material/Slider';
import './PlainCssSliderDeep1.css';

export default function PlainCssSliderDeep1() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className="slider" />
    </div>
  );
}

上面的演示依赖于默认的 className,但您可以使用 slotProps API 提供您自己的类名。

PlainCssSliderDeep2.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .thumb {
  border-radius: 1px;
}
PlainCssSliderDeep2.js
import * as React from 'react';
import Slider from '@mui/material/Slider';
import './PlainCssSliderDeep2.css';

export default function PlainCssSliderDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        className="slider"
        slotProps={{ thumb: { className: 'thumb' } }}
      />
    </div>
  );
}

全局 CSS

显式地为组件提供类名太麻烦了吗? 您可以定位 Material UI 生成的类名

Edit Button

GlobalCssSlider.css
.MuiSlider-root {
  color: #20b2aa;
}

.MuiSlider-root:hover {
  color: #2e8b57;
}
GlobalCssSlider.js
import * as React from 'react';
import Slider from '@mui/material/Slider';
import './GlobalCssSlider.css';

export default function GlobalCssSlider() {
  return <Slider defaultValue={30} />;
}

CSS 注入顺序 ⚠️

注意: 大多数 CSS-in-JS 解决方案将其样式注入到 HTML <head> 的底部,这使得 Material UI 优先于您的自定义样式。为了消除对 !important 的需求,您需要更改 CSS 注入顺序。这是一个演示如何在 Material UI 中完成此操作的示例

import * as React from 'react';
import { StyledEngineProvider } from '@mui/material/styles';

export default function GlobalCssPriority() {
  return (
    <StyledEngineProvider injectFirst>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </StyledEngineProvider>
  );
}

注意: 如果您正在使用 Emotion 并在您的应用程序中使用了自定义缓存,那么该缓存将覆盖来自 Material UI 的缓存。为了使注入顺序仍然正确,您需要添加 prepend 选项。这是一个示例

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function GlobalCssPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </CacheProvider>
  );
}

注意: 如果您正在使用 styled-components 并且具有带有自定义 targetStyleSheetManager,请确保 target 是 HTML <head> 中的第一个元素。如果您想了解如何完成此操作,可以查看 @mui/styled-engine-sc 包中的 StyledEngineProvider 实现。

更深层的元素

如果您尝试样式化 Slider,您可能需要影响 Slider 的一些子元素,例如 thumb。在 Material UI 中,所有子元素的特异性都增加了 2:.parent .child {}。在编写覆盖时,您需要执行相同的操作。

以下示例除了 slider 本身的自定义样式外,还覆盖了 slider 的 thumb 样式。

GlobalCssSliderDeep.css
.MuiSlider-root {
  color: #20b2aa;
}

.MuiSlider-root:hover {
  color: #2e8b57;
}

.MuiSlider-root .MuiSlider-thumb {
  border-radius: 1px;
}
GlobalCssSliderDeep.js
import * as React from 'react';
import Slider from '@mui/material/Slider';
import './GlobalCssSliderDeep.css';

export default function GlobalCssSliderDeep() {
  return <Slider defaultValue={30} />;
}

Styled Components

stars npm

更改默认的样式引擎

默认情况下,Material UI 组件使用 Emotion 作为其样式引擎。但是,如果您想使用 styled-components,您可以按照styled-components 指南配置您的应用程序,或从示例项目之一开始

遵循这种方法可以减小捆绑包大小,并消除配置 CSS 注入顺序的需要。

在正确配置样式引擎后,您可以使用来自 @mui/material/stylesstyled() 实用程序,并直接访问主题。

Edit Button

import * as React from 'react';
import Slider from '@mui/material/Slider';
import { styled } from '@mui/material/styles';

const CustomizedSlider = styled(Slider)`
  color: #20b2aa;

  :hover {
    color: #2e8b57;
  }
`;

export default function StyledComponents() {
  return <CustomizedSlider defaultValue={30} />;
}

更深层的元素

如果您尝试样式化 Slider,您可能需要影响 Slider 的一些子元素,例如 thumb。在 Material UI 中,所有子元素的特异性都增加了 2:.parent .child {}。在编写覆盖时,您需要执行相同的操作。

以下示例除了 slider 本身的自定义样式外,还覆盖了 slider 的 thumb 样式。

Enter 开始编辑

上面的演示依赖于默认的 className,但您可以使用 slotProps API 提供您自己的类名。

import * as React from 'react';
import { styled } from '@mui/material/styles';
import Slider from '@mui/material/Slider';

const CustomizedSlider = styled((props) => (
  <Slider slotProps={{ thumb: { className: 'thumb' } }} {...props} />
))`
  color: #20b2aa;

  :hover {
    color: #2e8b57;
  }

  & .thumb {
    border-radius: 1px;
  }
`;

export default function StyledComponentsDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <CustomizedSlider defaultValue={30} />
    </div>
  );
}

主题

通过使用 Material UI 主题提供器,主题也将在样式引擎(Emotion 或 styled-components,取决于您的配置)的主题上下文中可用。

我们鼓励您在 Material UI 和项目的其余部分之间共享相同的主题对象。

const CustomizedSlider = styled(Slider)(
  ({ theme }) => `
  color: ${theme.palette.primary.main};

  :hover {
    color: ${darken(theme.palette.primary.main, 0.2)};
  }
`,
);
Enter 开始编辑

Portals

MUI Base Portal 组件提供了一种将子元素渲染到父组件 DOM 层次结构之外的 DOM 节点中的一流方法。由于 styled-components 作用域其 CSS 的方式,您可能会遇到样式未应用的问题。

例如,如果您尝试样式化 Tooltip 组件生成的 tooltip,您需要将 className 属性传递给在其 DOM 层次结构之外渲染的元素。以下示例展示了一种解决方法

import * as React from 'react';
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Tooltip from '@mui/material/Tooltip';

const StyledTooltip = styled(({ className, ...props }) => (
  <Tooltip {...props} classes={{ popper: className }} />
))`
  & .MuiTooltip-tooltip {
    background: navy;
  }
`;
Enter 开始编辑

CSS 模块

stars

很难知道 这种样式解决方案 的市场份额,因为它取决于人们使用的捆绑解决方案。

Edit Button

CssModulesSlider.module.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}
CssModulesSlider.js
import * as React from 'react';
import Slider from '@mui/material/Slider';
// webpack, Parcel or else will inject the CSS into the page
import styles from './CssModulesSlider.module.css';

export default function CssModulesSlider() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className={styles.slider} />
    </div>
  );
}

CSS 注入顺序 ⚠️

注意: 大多数 CSS-in-JS 解决方案将其样式注入到 HTML <head> 的底部,这使得 Material UI 优先于您的自定义样式。为了消除对 !important 的需求,您需要更改 CSS 注入顺序。这是一个演示如何在 Material UI 中完成此操作的示例

import * as React from 'react';
import { StyledEngineProvider } from '@mui/material/styles';

export default function GlobalCssPriority() {
  return (
    <StyledEngineProvider injectFirst>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </StyledEngineProvider>
  );
}

注意: 如果您正在使用 Emotion 并在您的应用程序中使用了自定义缓存,那么该缓存将覆盖来自 Material UI 的缓存。为了使注入顺序仍然正确,您需要添加 prepend 选项。这是一个示例

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function CssModulesPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </CacheProvider>
  );
}

注意: 如果您正在使用 styled-components 并且具有带有自定义 targetStyleSheetManager,请确保 target 是 HTML <head> 中的第一个元素。如果您想了解如何完成此操作,可以查看 @mui/styled-engine-sc 包中的 StyledEngineProvider 实现。

更深层的元素

如果您尝试样式化 Slider,您可能需要影响 Slider 的一些子元素,例如 thumb。在 Material UI 中,所有子元素的特异性都增加了 2:.parent .child {}。在编写覆盖时,您需要执行相同的操作。重要的是要记住,CSS 模块为每个类添加了唯一的 id,并且该 id 不会出现在 Material UI 提供的子类上。为了摆脱这种情况,CSS 模块提供了一个功能,即 :global 选择器。

以下示例除了 slider 本身的自定义样式外,还覆盖了 slider 的 thumb 样式。

CssModulesSliderDeep1.module.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider :global .MuiSlider-thumb {
  border-radius: 1px;
}
CssModulesSliderDeep1.js
import * as React from 'react';
// webpack, Parcel or else will inject the CSS into the page
import styles from './CssModulesSliderDeep1.module.css';
import Slider from '@mui/material/Slider';

export default function CssModulesSliderDeep1() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className={styles.slider} />
    </div>
  );
}

上面的演示依赖于默认的 className,但您可以使用 slotProps API 提供您自己的类名。

CssModulesSliderDeep2.module.css
.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .thumb {
  border-radius: 1px;
}
CssModulesSliderDeep2.js
import * as React from 'react';
// webpack, Parcel or else will inject the CSS into the page
import styles from './CssModulesSliderDeep2.module.css';
import Slider from '@mui/material/Slider';

export default function CssModulesSliderDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        className={styles.slider}
        slotProps={{ thumb: { className: styles.thumb } }}
      />
    </div>
  );
}

Emotion

stars npm

css 属性

Emotion 的 css() 方法与 Material UI 无缝协作。

Enter 开始编辑

主题

它的工作方式与 styled components 完全相同。您可以使用相同的指南

styled() API

它的工作方式与 styled components 完全相同。您可以使用相同的指南

Tailwind CSS

stars npm

设置

如果您习惯使用 Tailwind CSS 并希望将其与 Material UI 组件一起使用,您可以首先克隆 Tailwind CSS 示例项目。如果您使用不同的框架,或者已经设置了您的项目,请按照以下步骤操作

  1. 按照 https://tailwind.org.cn/docs/installation/framework-guides 中的说明,将 Tailwind CSS 添加到您的项目中。
  2. 移除 Tailwind CSS 的 preflight 样式,以便它可以改用 Material UI 的 preflight (CssBaseline)。
tailwind.config.js
 module.exports = {
+  corePlugins: {
+    preflight: false,
+  },
 };
  1. 添加 important 选项,使用您的应用程序包装器的 id。例如,Next.js 的 #__next 和 CRA 的 "#root"
tailwind.config.js
 module.exports = {
   content: [
     "./src/**/*.{js,jsx,ts,tsx}",
   ],
+  important: '#root',
   theme: {
     extend: {},
   },
   plugins: [],
 }

Material UI 使用的大多数 CSS 的特异性为 1,因此这个 important 属性是不必要的。但是,在少数边缘情况下,Material UI 使用嵌套的 CSS 选择器来胜过 Tailwind CSS。使用此步骤有助于确保 更深层的元素 始终可以使用 Tailwind 的实用程序类进行自定义。有关此选项的更多详细信息,请参见 https://tailwind.org.cn/docs/configuration#selector-strategy

  1. 修复 CSS 注入顺序。大多数 CSS-in-JS 解决方案将其样式注入到 HTML <head> 的底部,这使得 Material UI 优先于 Tailwind CSS。为了减少对 important 属性的需求,您需要更改 CSS 注入顺序。这是一个演示如何在 Material UI 中完成此操作的示例
import * as React from 'react';
import { StyledEngineProvider } from '@mui/material/styles';

export default function GlobalCssPriority() {
  return (
    <StyledEngineProvider injectFirst>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </StyledEngineProvider>
  );
}

注意: 如果您正在使用 Emotion 并在您的应用程序中使用了自定义缓存,它将覆盖来自 Material UI 的缓存。为了使注入顺序仍然正确,您需要添加 prepend 选项。这是一个示例

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function PlainCssPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material UI's styles. */}
    </CacheProvider>
  );
}

注意: 如果您正在使用 styled-components 并且具有带有自定义 targetStyleSheetManager,请确保 target 是 HTML <head> 中的第一个元素。如果您想了解如何完成此操作,可以查看 @mui/styled-engine-sc 包中的 StyledEngineProvider 实现。

  1. 更改与 Portal 相关的元素的目标容器,以便它们被注入到在步骤 3 中用于在 Tailwind 配置中设置 important 选项的主应用程序包装器下。
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

const theme = createTheme({
  components: {
    MuiPopover: {
      defaultProps: {
        container: rootElement,
      },
    },
    MuiPopper: {
      defaultProps: {
        container: rootElement,
      },
    },
    MuiDialog: {
      defaultProps: {
        container: rootElement,
      },
    },
    MuiModal: {
      defaultProps: {
        container: rootElement,
      },
    },
  },
});

root.render(
  <StyledEngineProvider injectFirst>
    <ThemeProvider theme={theme}>
      <App />
    </ThemeProvider>
  </StyledEngineProvider>;
);

用法

现在一切都设置好了,您可以开始在 Material UI 组件上使用 Tailwind CSS 了!

Edit on StackBlitz

index.tsx
import * as React from 'react';
import Slider from '@mui/material/Slider';

export default function App() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className="text-teal-600" />
    </div>
  );
}

更深层的元素

例如,如果您尝试样式化 Slider,您可能需要自定义其子元素。

此示例演示如何覆盖 Slider 的 thumb 样式。

SliderThumbOverrides.tsx
import * as React from 'react';
import Slider from '@mui/material/Slider';

export default function SliderThumbOverrides() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        className="text-teal-600"
        slotProps={{ thumb: { className: 'rounded-sm' } }}
      />
    </div>
  );
}

样式化伪状态

如果您想样式化组件的伪状态,您可以在 classes 属性中使用适当的键。这是一个示例,说明如何样式化 Slider 的活动状态

SliderPseudoStateOverrides.tsx
import * as React from 'react';
import Slider from '@mui/material/Slider';

export default function SliderThumbOverrides() {
  return <Slider defaultValue={30} classes={{ active: 'shadow-none' }} />;
}

JSS TSS

JSS 本身在 Material UI 中不再受支持,但是,如果您喜欢 react-jss 提供的基于 hook 的 API (makeStylesuseStyles),您可以选择 tss-react

TSS 与 Material UI 集成良好,并提供比 JSS 更好的 TypeScript 支持。

import { render } from 'react-dom';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
import { ThemeProvider } from '@mui/material/styles';

export const muiCache = createCache({
  key: 'mui',
  prepend: true,
});

//NOTE: Don't use <StyledEngineProvider injectFirst/>
render(
  <CacheProvider value={muiCache}>
    <ThemeProvider theme={myTheme}>
      <Root />
    </ThemeProvider>
  </CacheProvider>,
  document.getElementById('root'),
);

现在您可以简单地 import { makeStyles, withStyles } from 'tss-react/mui'。将传递给您的回调函数的主题对象将是您通过 import { useTheme } from '@mui/material/styles' 获取的对象。

如果您想控制 theme 对象应该是什么,您可以从一个名为 makesStyles.ts 的文件中重新导出 makeStyleswithStyles

import { useTheme } from '@mui/material/styles';
//WARNING: tss-react require TypeScript v4.4 or newer. If you can't update use:
//import { createMakeAndWithStyles } from "tss-react/compat";
import { createMakeAndWithStyles } from 'tss-react';

export const { makeStyles, withStyles } = createMakeAndWithStyles({
  useTheme,
  /*
    OR, if you have extended the default mui theme adding your own custom properties:
    Let's assume the myTheme object that you provide to the <ThemeProvider /> is of
    type MyTheme then you'll write:
    */
  //"useTheme": useTheme as (()=> MyTheme)
});

然后,库像这样使用

import { makeStyles } from 'tss-react/mui';

export function MyComponent(props: Props) {
  const { className } = props;

  const [color, setColor] = useState<'red' | 'blue'>('red');

  const { classes, cx } = useStyles({ color });

  //Thanks to cx, className will take priority over classes.root
  return <span className={cx(classes.root, className)}>hello world</span>;
}

const useStyles = makeStyles<{ color: 'red' | 'blue' }>()((theme, { color }) => ({
  root: {
    color,
    '&:hover': {
      backgroundColor: theme.palette.primary.main,
    },
  },
}));

有关如何设置 SSR 或其他任何信息,请参阅 TSS 文档