跳到主要內容
+

樣式庫互操作性

雖然您可以使用 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 並且有 StyleSheetManager 與自訂 target,請確保目標是 HTML <head> 中的第一個元素。如果您好奇想了解如何完成此操作,您可以查看 @mui/styled-engine-sc 套件中 StyledEngineProvider 的實作。

更深層的元素

如果您嘗試為 Slider 設定樣式,您可能需要影響 Slider 的某些子元素,例如拇指。在 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 並且有 StyleSheetManager 與自訂 target,請確保目標是 HTML <head> 中的第一個元素。如果您好奇想了解如何完成此操作,您可以查看 @mui/styled-engine-sc 套件中 StyledEngineProvider 的實作。

更深層的元素

如果您嘗試為 Slider 設定樣式,您可能需要影響 Slider 的某些子元素,例如拇指。在 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 的某些子元素,例如拇指。在 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 開始編輯

入口

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 並且有 StyleSheetManager 與自訂 target,請確保目標是 HTML <head> 中的第一個元素。如果您好奇想了解如何完成此操作,您可以查看 @mui/styled-engine-sc 套件中 StyledEngineProvider 的實作。

更深層的元素

如果您嘗試為 Slider 設定樣式,您可能需要影響 Slider 的某些子元素,例如拇指。在 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. 將 Tailwind CSS 新增到您的專案,請按照 https://tailwind.dev.org.tw/docs/installation/framework-guides 中的指示操作。
  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.dev.org.tw/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 並且有 StyleSheetManager 與自訂 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 的 active 狀態設定樣式的範例

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 文件