样式库的互通性
虽然你可以使用 Material-UI 提供的基于 emotion 的样式解决方案来自定义你的应用程序,但你也可以使用你已知和喜欢的方案(从普通的 CSS 到 styled-components)。
本指南旨在归档当前比较流行的一些替代方案,但是您会发现在这里运用的法则,也可以在其他库里适用。 我们为以下的样式方案提供了一些示例:
纯 CSS
没有什么特别花哨的,只是纯 CSS。
PlainCssSlider.css
.slider {
color: #20b2aa;
}
.slider:hover {
color: #2e8b57;
}
PlainCssSlider.js
import * as React from 'react';
import Slider from '@material-ui/core/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 注入顺序。 Here's a demo of how it can be done in Material-UI:
import * as React from 'react';
import { StyledEngineProvider } from '@material-ui/core/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. 现在你可以覆盖 Material-UI 的样式。 */}
</StyledEngineProvider>
);
}
Note: If you are using emotion and have a custom cache in your app, that one will override the one coming from Material-UI. In order for the injection order to still be correct, you need to add the prepend option. 下面是一个示例:
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}>
{这里编写你的组件树。 现在你可以覆盖 Material-UI 的样式。 */}
</CacheProvider>
);
}
Note: If you are using styled-components and have StyleSheetManager
with a custom target
, make sure that the target is the first element in the HTML <head>
. If you are curious to see how it can be done, you can take a look on the StylesProvider
implementation in the @material-ui/styled-engine-sc
package.
更深层的元素
如果你试图自定义滑块的样式,那么很可能会影响到滑块的一些子元素,例如滚动条的箭头(thumb)。 在 Material-UI 中,所有的子元素都增加了两层的特定类:.parent .child {}
。 所以在编写覆盖样式的时候,你也需要这样做。
以下示例除了覆盖滑块本身的自定义样式外,还覆盖了滑块的 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 '@material-ui/core/Slider';
import './PlainCssSliderDeep1.css';
export default function PlainCssSliderDeep1() {
return (
<div>
<Slider defaultValue={30} />
<Slider defaultValue={30} className="slider" />
</div>
);
}
上面的演示依赖于 默认的className
值,但是你也可以使用 componentsProps
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 '@material-ui/core/Slider';
import './PlainCssSliderDeep2.css';
export default function PlainCssSliderDeep2() {
return (
<div>
<Slider defaultValue={30} />
<Slider
defaultValue={30}
className="slider"
componentsProps={{ thumb: { className: 'thumb' } }}
/>
</div>
);
}
全局 CSS
明确向提组件提供类名是不是太大费周章了? 您可以定位到由 Material-UI 生成的类名。
GlobalCssSlider.css
.MuiSlider-root {
color: #20b2aa;
}
.MuiSlider-root:hover {
color: #2e8b57;
}
GlobalCssSlider.js
import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './GlobalCssSlider.css';
export default function GlobalCssSlider() {
return <Slider defaultValue={30} />;
}
CSS 注入顺序⚠️
注意: 大多数的 CSS-in-JS 解决方案是在 HTML <head>
的底部注入它们的样式,这会导致你的自定义样式被 Material-UI 的样式规则所覆盖。 如果你有移除 !important 的需求,那么就需要改变 CSS 注入顺序。 Here's a demo of how it can be done in Material-UI:
import * as React from 'react';
import { StyledEngineProvider } from '@material-ui/core/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. 现在你可以覆盖 Material-UI 的样式。 */}
</StyledEngineProvider>
);
}
Note: If you are using emotion and have a custom cache in your app, that one will override the one coming from Material-UI. In order for the injection order to still be correct, you need to add the prepend option. 下面是一个示例:
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}>
{/* 这里编写你的组件树。 现在你可以覆盖 Material-UI 的样式。 */}
</CacheProvider>
);
}
Note: If you are using styled-components and have StyleSheetManager
with a custom target
, make sure that the target is the first element in the HTML <head>
. If you are curious to see how it can be done, you can take a look on the StylesProvider
implementation in the @material-ui/styled-engine-sc
package.
更深层的元素
如果你试图自定义滑块的样式,那么很可能会影响到滑块的一些子元素,例如滚动条的箭头(thumb)。 在 Material-UI 中,所有的子元素都增加了两层的特定类:.parent .child {}
。 所以在编写覆盖样式的时候,你也需要这样做。
以下示例除了覆盖滑块本身的自定义样式外,还覆盖了滑块的 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 '@material-ui/core/Slider';
import './GlobalCssSliderDeep.css';
export default function GlobalCssSliderDeep() {
return <Slider defaultValue={30} />;
}
Styled Components
改变默认的样式引擎
默认情况下,Material-UI 组件使用 emotion 来作为它们的样式引擎。 但是,如果你想使用 styled-components
的话,那么你可以参考这个 示例项目 来配置你的应用程序。 按照这种方法来配置的话,则可以减少捆绑包的大小,并且无需配置 CSS 注入顺序。
After the style engine is configured properly, you can use the styled()
utility from @material-ui/core/styles
and have direct access to the theme.
import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import { styled } from '@material-ui/core/styles';
const CustomizedSlider = styled(Slider)`
color: #20b2aa;
:hover {
color: #2e8b57;
}
`;
export default function StyledComponents() {
return <CustomizedSlider defaultValue={30} />;
}
更深层的元素
如果你试图自定义滑块的样式,那么很可能会影响到滑块的一些子元素,例如滚动条的箭头(thumb)。 在 Material-UI 中,所有的子元素都增加了两层的特定类:.parent .child {}
。 所以在编写覆盖样式的时候,你也需要这样做。
以下示例除了覆盖滑块本身的自定义样式外,还覆盖了滑块的 thumb
样式。
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Slider from '@mui/material/Slider';
import Box from '@mui/material/Box';
const CustomizedSlider = styled(Slider)`
color: #20b2aa;
:hover {
color: #2e8b57;
}
& .MuiSlider-thumb {
border-radius: 1px;
}
`;
export default function StyledComponentsDeep() {
return (
<Box sx={{ width: 300 }}>
<Slider defaultValue={30} />
<CustomizedSlider defaultValue={30} />
</Box>
);
}
上面的演示依赖于 默认的className
值,但是你也可以使用 componentsProps
API 来提供你自己的类名。
import * as React from 'react';
import { styled } from '@material-ui/core/styles';
import Slider from '@material-ui/core/Slider';
const CustomizedSlider = styled((props) => (
<Slider componentsProps={{ 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 主题提供者(theme provider),该主题也可以在样式引擎的主题上下文中可用(emotion 或 styled-components,取决于你的配置)。
⚠️如果你已经使用了 styled-component 或 emotion 驱动的自定义主题,那么它可能会不兼容 Material-UI 的主题规范。 如果它不兼容,那么你需要先渲染 Material-UI 的 ThemeProvider。 这样做就可以确保主题结构的隔离。 这对于想要在代码库中渐进式地使用 Material-UI 组件是非常理想的。
我们鼓励你在 Material-UI 和你项目的其他部分之间共享相同的主题对象。
const CustomizedSlider = styled(Slider)(
({ theme }) => `
color: ${theme.palette.primary.main};
:hover {
color: ${darken(theme.palette.primary.main, 0.2)};
}
`,
);
<ThemeProvider theme={customTheme}>
<CustomizedSlider defaultValue={30} />
</ThemeProvider>
Portals(传送门组件)
传送门组件提供了一种一流的方法,它将子元素渲染在其父组件的 DOM 层次结构之外的 DOM 节点中。 当您使用这样的 styled-components 规范其 CSS 的方式时,可能会遇到一些无法附着样式的问题。
For example, if you attempt to style the tooltip
generated by the Tooltip component, you will need to pass along the className
property to the element being rendered outside of it's DOM hierarchy. 下面的示例演示了一个变通办法:
import * as React from 'react';
import { styled } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Tooltip from '@material-ui/core/Tooltip';
const StyledTooltip = styled(({ className, ...props }) => (
<Tooltip {...props} classes={{ popper: className }} />
))`
& .MuiTooltip-tooltip {
background: navy;
}
`;
<StyledTooltip title="I am navy">
<Button variant="contained" color="primary">
Styled tooltip
</Button>
</StyledTooltip>
CSS Modules
鉴于它全权依赖于大家使用的打包方案,我们很难得知 此种样式方案 的市场占有率。
CssModulesSlider.module.css
.slider {
color: #20b2aa;
}
.slider:hover {
color: #2e8b57;
}
CssModulesSlider.js
import * as React from 'react';
import Slider from '@material-ui/core/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 注入顺序。 Here's a demo of how it can be done in Material-UI:
import * as React from 'react';
import { StyledEngineProvider } from '@material-ui/core/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. 现在你可以覆盖 Material-UI 的样式。 */}
</StyledEngineProvider>
);
}
Note: If you are using emotion and have a custom cache in your app, that one will override the one coming from Material-UI. In order for the injection order to still be correct, you need to add the prepend option. 下面是一个示例:
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}>
{/* 这里编写你的组件 现在你可以覆盖 Material-UI 的样式。 */}
</CacheProvider>
);
}
Note: If you are using styled-components and have StyleSheetManager
with a custom target
, make sure that the target is the first element in the HTML <head>
. If you are curious to see how it can be done, you can take a look on the StylesProvider
implementation in the @material-ui/styled-engine-sc
package.
更深层的元素
如果你试图自定义滑块的样式,那么很可能会影响到滑块的一些子元素,例如滚动条的箭头(thumb)。 在 Material-UI 中,所有的子元素都增加了两层的特定类:.parent .child {}
。 所以在编写覆盖样式的时候,你也需要这样做。
以下示例除了覆盖滑块本身的自定义样式外,还覆盖了滑块的 thumb
样式。
CssModulesSliderDeep1.module.css
.slider {
color: #20b2aa;
}
.slider:hover {
color: #2e8b57;
}
.slider .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 '@material-ui/core/Slider';
export default function CssModulesSliderDeep1() {
return (
<div>
<Slider defaultValue={30} />
<Slider defaultValue={30} className={styles.slider} />
</div>
);
}
上面的演示依赖于 默认的className
值,但是你也可以使用 componentsProps
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 '@material-ui/core/Slider';
export default function CssModulesSliderDeep2() {
return (
<div>
<Slider defaultValue={30} />
<Slider
defaultValue={30}
className={styles.slider}
componentsProps={{ thumb: { className: styles.thumb } }}
/>
</div>
);
}
Emotion
css
属性
Emotion 的 css() 方法与 Material-UI 无缝协作。
/* eslint-disable react/react-in-jsx-scope -- Unaware of jsxImportSource */
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import Slider from '@mui/material/Slider';
import Box from '@mui/material/Box';
export default function EmotionCSS() {
return (
<Box sx={{ width: 300 }}>
<Slider defaultValue={30} />
<Slider
defaultValue={30}
css={css`
color: #20b2aa;
:hover {
color: #2e8b57;
}
`}
/>
</Box>
);
}