Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] Revise "Component theming" and "How to customize" guides #31997

Merged
merged 24 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
25b712c
initial commit
danilo-leal Mar 22, 2022
100e7a3
iteration
danilo-leal Mar 23, 2022
974ea77
general revision
danilo-leal Mar 26, 2022
b5307a1
component theming revision
danilo-leal Mar 26, 2022
f29a278
Jun's review
danilo-leal Mar 28, 2022
5f2bb54
Sam's first batch of reviews
danilo-leal Mar 30, 2022
273cd2a
Jun's review
danilo-leal Mar 30, 2022
e3b4ce1
trimming inspiration
danilo-leal Mar 30, 2022
832c3ec
Sam's second batch of reviews
danilo-leal Mar 30, 2022
15e12f0
remove excerpt about Link's color prop
danilo-leal Mar 30, 2022
24b4bb2
Jun's review: update code snippet
danilo-leal Mar 30, 2022
c72125e
Sam's review
danilo-leal Apr 1, 2022
260faa1
clarifying about specificity
danilo-leal Apr 6, 2022
9fd751e
Olivier's review on using CSS jargon
danilo-leal Apr 7, 2022
6ffc1c3
reordering content on "Overriding nested component styles", Olivier's…
danilo-leal Apr 7, 2022
b44e391
tackle dev tool blurriness
danilo-leal Apr 7, 2022
bda05ff
expand on using the experimental sx prop
danilo-leal Apr 7, 2022
5c0bc5d
line break for better review
danilo-leal Apr 7, 2022
08070da
explain why sx prop alone is stable but inside the theme is experimental
danilo-leal Apr 7, 2022
1429338
Sam's review
danilo-leal Apr 7, 2022
9cf2a44
Merge branch 'master' of https://github.com/mui/material-ui into how-…
danilo-leal Apr 8, 2022
af44cf2
Sam's review
danilo-leal Apr 8, 2022
36436d4
small final tweaks
danilo-leal Apr 12, 2022
ef3c447
yarn docs:typescript:formatted
danilo-leal Apr 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,54 @@ components: GlobalStyles

# How to customize

<p class="description">You can easily customize the appearance of a MUI component.</p>
<p class="description">Learn the many options for customizing the Material UI components.</p>
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

As components can be used in different contexts, there are several approaches to customizing them. Going from the narrowest use-case to the broadest, these are:
There are a few approaches you could choose for cusotmizing a given component styles. It will ultimately depend on your specific context to decide which is the most appropriate one. Let's go through them from the narrowest use case to the broadest:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

1. [One-off customization](#1-one-off-customization)
1. [Reusable style overrides](#2-reusable-style-overrides)
1. [Dynamic variation](#3-dynamic-variation)
1. [Global theme variation](#4-global-theme-variation)
1. [Global CSS override](#5-global-css-override)

## 1. One-off customization

You might need to change the style of a component for a specific implementation, for which you have the following solutions available:
If you want to change the styles of a component in a specific location and don't want it to be perpetuated to other instances of it, you have the following solutions available:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

### Use the `sx` prop
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

The easiest way to add style overrides for a one-off situation is to use the [`sx` prop](/system/basics/#the-sx-prop) available on all MUI components.
The easiest way to add style overrides for a one-off situation is to use the [`sx` prop](/system/basics/#the-sx-prop), which is available on all Material UI components.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
Here is an example:

{{"demo": "SxProp.js"}}
{{"demo": "SxProp.js" }}

Next you'll see how you can use global class selectors for accessing slots inside the component. You'll also learn how to easily identify the classes which are available to you for each of the states and slots in the component.
#### Overriding nested component styles

### Overriding nested component styles
If you want to customize very specific parts of a component, you can use classes provided by Material UI inside the `sx` prop. As an example, let's say you want to change the Slider's thumb from a circle to a square.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

You can use the browser dev tools to identify the slot for the component you want to override. It can save you a lot of time.
The styles injected into the DOM by MUI rely on class names that [follow a simple pattern](/styles/advanced/#class-names):
`[hash]-Mui[Component name]-[name of the slot]`.
First step would be using the browser dev tools to identify the class for the component slot you want to override:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

⚠️ These class names can't be used as CSS selectors because they are unstable,
however, MUI applies global class names using a consistent convention: `Mui[Component name]-[name of the slot]`.
<img src="/static/images/customization/dev-tools.png" alt="dev-tools" width="796" style="margin-bottom: 16px;" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change of docs/public/static/images/customization/dev-tools.png looks blurry on my screen. Am I the only one? I mean:

a. https://mui.com/customization/how-to-customize/#overriding-nested-component-styles
b. https://deploy-preview-31997--material-ui.netlify.app/customization/how-to-customize/#the-sx-prop

a. looks better on my end. I'm raising this because it's not the first time I see this problem. What do you think of limiting the resolution to x2, would it solve the problem? Otherwise, could we try a fixed integer increment? Currently, we are doing x3.28, maybe x3 or x4 would look crisp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh... looks a bit blurry to me too, now that I'm looking at it again. But I did export at 2x (don't know where you got x3.28 from!). Though the blurriness could be because I increased the image size a bit. Let me see if I can tweak it properly.


Let's go back to the above demo. How can you override the slider's thumb?
Then, copy the target class of `MuiSlider-thumb`, place it inside the styles witihn the `sx` prop, and add your overrides.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

<img src="/static/images/customization/dev-tools.png" alt="dev-tools" width="406" />
{{"demo": "DevTools.js"}}

In this example, the styles are applied with `.css-ae2u5c-MuiSlider-thumb` so the name of the component is `Slider` and the name of the slot is `thumb`.
The styles injected into the DOM by Material UI rely on class names that [follow a simple pattern](/styles/advanced/#class-names):
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
`[hash]-Mui[Component name]-[name of the slot]`.

You now know that you need to target the `.MuiSlider-thumb` class name for overriding the look of the thumb:
In this case, the styles are applied with `.css-ae2u5c-MuiSlider-thumb` but you only really need to target the `.MuiSlider-thumb`, where `Slider` is the component and `thumb` is the slot.

{{"demo": "DevTools.js"}}
> ⚠️ Note that these class names can't be used as CSS selectors because they are unstable.

### Overriding styles with class names

If you would like to override the styles of the components using classes, you can use the `className` prop available on each component. For overriding the styles of the different parts inside the component, you can use the global classes available for each slot, as described in the previous section.
If you want to override the component styles using custom classes, you can use the `className` prop, available on each component. Use the same approach described above if you want to override specific parts of the component.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

You can find examples of this using different styles libraries in the [Styles library interoperability](/guides/interoperability/) guide.
Go to the [Styles Library Interoperability](/guides/interoperability/) guide to find examples of this approach using different styling libraries.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

### State classes

The components special states, like _hover_, _focus_, _disabled_ and _selected_, are styled with a higher CSS specificity.
[Specificity is a weight](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) that is applied to a given CSS declaration.

In order to override the components' special states, **you need to increase specificity**.
Here is an example with the _disable_ state and the Button component using a pseudo-class (`:disabled`):
States like _hover_, _focus_, _disabled_ and _selected_, are styled with a higher CSS specificity. To customize them, you'll need to **increase specificity**. Here is an example with the _disable_ state and the Button component using a pseudo-class (`:disabled`):
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

```css
.Button {
Expand All @@ -76,9 +69,9 @@ Here is an example with the _disable_ state and the Button component using a pse
<Button disabled className="Button">
```

Sometimes, you can't use a CSS pseudo-class, as the state doesn't exist in the web specification.
Let's take the MenuItem component and its _selected_ state as an example.
In such cases you can use a MUI equivalent of CSS pseudo-classes - **state classes**.
Sometimes though, you won't be able to use a CSS pseudo-class, as the state doesn't exist in the web specification.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
Let's take the `MenuItem` component and its _selected_ state as an example.
In such cases you can use a Material UI equivalent of CSS pseudo-classes**state classes**.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
Target the `.Mui-selected` global class name to customize the special state of the `MenuItem` component:

```css
Expand All @@ -96,15 +89,17 @@ Target the `.Mui-selected` global class name to customize the special state of t
<MenuItem selected className="MenuItem">
```

If you'd like to learn more about CSS specificity, we recommend checking [MDN's guide](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) about it.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

#### Why do I need to increase specificity to override one component state?

By design, the CSS specification makes the pseudo-classes increase the specificity.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
For consistency with native elements, MUI increases the specificity of its custom state classes.
This has one important advantage, it allows you to cherry-pick the state you want to customize.
For consistency with native elements, Material UI increases the specificity of its custom state classes.
This has one important advantage: it allows you to cherry-pick the state you want to customize.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I fully understand the answer to this question. Here's what I gather:

  • pseudo-classes have a high level of specificity
  • MUI's state classes also have a high level of specificity - possibly more than CSS pseudo-classes?
  • this is a good thing because it makes it simple to target individual components using these classes

Is that correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • pseudo-classes have a high level of specificity ✅
  • MUI's state classes also have a high level of specificity possibly more than CSS pseudo-classes? they have the same as pseudo states, see https://codepen.io/mnajdova/pen/mdpwxyY
  • this is a good thing because it makes it simple to target individual components component's state using these classes

danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

#### What custom state classes are available in MUI?
#### What custom state classes are available in Material UI?

You can rely on the following [global class names](/styles/advanced/#with-material-ui-core) generated by MUI:
You can rely on the following [global class names](/styles/advanced/#with-material-ui-core) generated by Material UI:

| State | Global class name |
| :------------ | :------------------ |
Expand All @@ -119,10 +114,10 @@ You can rely on the following [global class names](/styles/advanced/#with-materi
| required | `.Mui-required` |
| selected | `.Mui-selected` |

> ⚠️ Never style these state classes' names directly:
> ⚠️ Never style these state classes' names directly. If you do that, it will impact all the components with unclear side-effects. Always target it together with a component.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

```css
/* ❌ NOT OK, impact all the components with unclear side-effects */
/* ❌ NOT OK */
.Mui-error {
color: red;
}
Expand All @@ -135,26 +130,20 @@ You can rely on the following [global class names](/styles/advanced/#with-materi

## 2. Reusable style overrides
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

If you find that you need the same overrides in multiple places across your application, you can use the [`styled()`](/system/styled/) utility to create a reusable component:
If you find that you need to reuse the same overrides in different locations across your application, you can use the [`styled()`](/system/styled/) utility to create a reusable component:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

{{"demo": "StyledCustomization.js", "defaultCodeOpen": true}}

With it, you have access to all of a component's props to dynamically style the component.

## 3. Dynamic variation
### Dynamic overrides

In the previous section, we learned how to override the style of a MUI component.
Now, let's see how we can make these overrides dynamic.
Here are four alternatives; each has its pros and cons.
Using the `styled()` utility above allows you to add dynamic styles based on the component's props. Here are two ways you can do that:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

### Dynamic CSS
#### Dynamic CSS

Using the `styled()` utility offers a simple way for adding dynamic styles based on props.
> ⚠️ Note that if you are using TypeScript you will need to update the prop's types of the new component.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> ⚠️ Note that if you are using TypeScript you will need to update the prop's types of the new component.
> ⚠️ Note that if you are using TypeScript you will need to update the prop types of the new component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should move this back right before the demo showing how it should be altered in TypeScript.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before or after? Haven't altered its position, its in the same spot as in the current version.


{{"demo": "DynamicCSS.js", "defaultCodeOpen": false}}

> ⚠️ Note that if you are using TypeScript you will need to update the prop's types of the new component.

```tsx
import * as React from 'react';
import { styled } from '@mui/material/styles';
Expand All @@ -174,27 +163,15 @@ const StyledSlider = styled(Slider, {
}));
```

### CSS variables
#### CSS variables

{{"demo": "DynamicCSSVariables.js"}}

## 4. Global theme variation

In order to promote consistency between components, and manage the user interface appearance as a whole, MUI provides a mechanism to apply global changes.

Please take a look at the theme's [global overrides page](/customization/theme-components/) for more details.

## 5. Global CSS override
## 3. Global theme overrides

Components expose [global class names](/styles/advanced/#with-material-ui-core) to enable customization with CSS.

```css
.MuiButton-root {
font-size: 1rem;
}
```
If you want to secure styling consistency between components, managing your user interface as a whole, Material UI provides a way to customize the components from the theme level. We have a dedicated guide about this approach, take a look at the [Component theming customization](/customization/theme-components/) page for more details.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

You can reference the [Styles library interoperability guide](/guides/interoperability/) to find examples of this using different styles libraries or plain CSS.
## 4. Global CSS override

If you just want to add some global baseline styles for some of the HTML elements, you can use the `GlobalStyles` component. Here is an example of how you can override styles for the `h1` elements.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Components

<p class="description">The theme's components key allows you to customize a component without wrapping it in another component. You can change the styles, the default props, and more.</p>
<p class="description">Customizing the components using its keys inside the theme enables you to achieve styling consistency across your application.</p>
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

## Default props

You can change the default of every prop of a MUI component.
A `defaultProps` key is exposed in the theme's `components` key for this use case.
Every Material UI component comes, out-of-the-box, with default values set for each of its props.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
If you want to change how your component looks by default, use the `defaultProps` key exposed in the theme's `components` key. Like so:
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

```js
const theme = createTheme({
Expand All @@ -27,7 +27,7 @@ To override lab component styles with TypeScript, check [this page](/components/

## Global style overrides

You can use the theme's `styleOverrides` key to potentially change every single style injected by MUI into the DOM.
If you want to print an entirely different design direction to the components, potentially changing every single style injected by Material UI into the DOM, use the theme's `styleOverrides` key.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

```js
const theme = createTheme({
Expand All @@ -48,9 +48,22 @@ const theme = createTheme({

{{"demo": "GlobalThemeOverride.js"}}

The list of each component's classes is documented under the **CSS** section of its API page.
Note that each component is composed of several different parts. If you want to know how to target a specific part of a given component, check the **CSS** section of its API page to see every class it has available. You should then use it inside the `stylesOverrides` key.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

To override a lab component's styles with TypeScript, check [this section of the documentation](/components/about-the-lab/#typescript).
```js
const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
containedPrimary: {
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
backgroundColor: '#202020',
color: '#fff',
},
},
},
},
});
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
```

### Overrides based on props

Expand All @@ -77,17 +90,62 @@ const finalTheme = createTheme({

{{"demo": "GlobalThemeOverrideCallback.js"}}

### Using `sx` (experimental) syntax
### Using the `sx` experimental syntax
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

If you are not familiar with the `sx` prop, make sure you first check out [the concept around it](/system/the-sx-prop/) and [the difference with the `styled` utility](/system/styled/#difference-with-the-sx-prop).
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

To have a similar shorthand CSS notation to the `sx` prop but within the theme, you can use `sx` inside the `stylesOverrides`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rephrasing.

Suggested change
To have a similar shorthand CSS notation to the `sx` prop but within the theme, you can use `sx` inside the `stylesOverrides`.
You can use the `sx` prop inside the `styleOverrides` key to modify styles within the theme using shorthand CSS notation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we elaborate on why a developer would want to do this? And this would also be a good place to explain why this is "experimental."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mnajdova anything you'd like to share about that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we elaborate on why a developer would want to do this?

Mainly for conistency. If a developer is already used to the sx syntax and it's shorthands, it's limiting that it can be used on only one place (the sx prop). More over, if they want to convert from sx prop to styled(), or move the styles to the theme overrides, they would have to manually change the styles to comply with the other regular syntax. This utility helps them by doing it automatically and providing a better DX along the way.

It's experimental as it was added after the v5 stable release, and we usually export API like this as an experimental first, to see if there will be some unforeseen bugs and stabilize them later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know what you think, tackled these aspects.


danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
{{"demo": "GlobalThemeOverrideSx.js", "defaultCodeOpen": false}}

```tsx
const finalTheme = createTheme({
components: {
MuiChip: {
styleOverrides: {
root: sx({
px: 1,
py: 0.25,
borderRadius: 1,
}),
label: {
padding: 'initial',
},
icon: sx({
mr: 0.5,
ml: '-2px',
}),
},
},
},
});
```

### Specificity

If you are not familiar `sx`, first check out [the concept](/system/the-sx-prop/) and [the difference with the `styled`](/system/styled/#difference-with-the-sx-prop).
If you use the theming approach to customize the components, you'll still be able to override them using the `sx` prop as it has a higher CSS specificity.
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved

`sx` is also compatible with theme style overrides if you prefer the shorthand notation.
Note that some components expose a `color` prop, which has a lower specificty than the theme's styles overrides. For example, say you have customized `Link`'s color.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danilo-leal I was wrong. The Link is actually a bug. This should have the highest priority <Link color="highest-priority-color">...</Link>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this part and I will create a fix in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! I was just thinking it was weird for the component color prop to have lower specificity... I'll correct this sentence!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@siriwatknp are you going to bump the specificity for each prop combination? Should we create an RFC for this and make it work consistent with Material UI? In this case, when would you ever need the props callback?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mnajdova I am not sure I follow you. From my understanding, this is a bug on the Link component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that the Typography (and Link) are basically the ones that work differently, in the other components that theme overrides take precedence, for example take a look at the button - https://codesandbox.io/s/shy-dust-sdylec?file=/src/App.tsx


{{"demo": "GlobalThemeOverrideSx.js"}}
```js
MuiLink: {
styleOverrides: {
root: {
color: '#0000FF', // blue
},
},
},
```

If you then try to customize the `Link`'s color using the `color` prop, nothing will happen since the theme's styles take precedence.

```js
<Link color="#A52A2A">Brown</Link> // the blue set in the theme's style overrides above will persist
```

## Adding new component variants
## Creating new component variants

You can use the `variants` key in the theme's `components` section to add new variants to MUI components. These new variants can specify what styles the component should have when specific props are applied.
You can use the `variants` key in the theme's `components` section to create new variants to Material UI components. These new variants can specify what styles the component should have when that specific variant prop value is applied.

The definitions are specified in an array, under the component's name. For each of them a CSS class is added to the HTML `<head>`. The order is important, so make sure that the styles that should win are specified last.

Expand Down
Binary file modified docs/public/static/images/customization/dev-tools.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/src/modules/components/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function useUniqueId(prefix) {
}

const Root = styled('div')(({ theme }) => ({
marginBottom: 40,
marginBottom: 24,
marginLeft: theme.spacing(-2),
marginRight: theme.spacing(-2),
[theme.breakpoints.up('sm')]: {
Expand Down