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

[blog] Introducing callback support in style overrides #30668

Merged
merged 36 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6fe0ce6
add blog page
siriwatknp Jan 17, 2022
9d91130
add tag
siriwatknp Jan 17, 2022
6b73958
add description
siriwatknp Jan 17, 2022
085fd64
fix grammar
siriwatknp Jan 17, 2022
c41356a
yarn prettier
siriwatknp Jan 17, 2022
9d8e597
Merge branch 'master' of https://github.com/mui-org/material-ui into …
siriwatknp Jan 18, 2022
e97de4f
fix content
siriwatknp Jan 18, 2022
2692997
enable blog index
siriwatknp Jan 18, 2022
b7aec93
misc fix
siriwatknp Jan 18, 2022
085235c
use px
siriwatknp Jan 18, 2022
eceb9d0
Update docs/pages/blog/mui-material-v5-3.md
siriwatknp Jan 19, 2022
6664c86
Apply suggestions from code review
siriwatknp Jan 20, 2022
ca5de6c
run prettier
siriwatknp Jan 20, 2022
5991bac
update timestamp
siriwatknp Jan 20, 2022
154c63a
fix display tags logic
siriwatknp Jan 20, 2022
46e6c02
add variant theming deprecated comment
siriwatknp Jan 20, 2022
a11c5b9
replace variants with styleOverrides in migration docs
siriwatknp Jan 20, 2022
b322eee
Merge branch 'master' of https://github.com/mui-org/material-ui into …
siriwatknp Jan 20, 2022
c90ecf5
add runtime error message
siriwatknp Jan 20, 2022
36e6471
remove test for now
siriwatknp Jan 20, 2022
638c295
run extract error codes
siriwatknp Jan 20, 2022
bac3848
run prettier
siriwatknp Jan 20, 2022
cbeaae1
Apply suggestions from code review
siriwatknp Jan 20, 2022
f541dd0
add prettier to yarn error-code
siriwatknp Jan 20, 2022
22b36af
rename the blog post
siriwatknp Jan 20, 2022
682558d
Merge branch 'blog/v5.3' of github.com:siriwatknp/material-ui into bl…
siriwatknp Jan 20, 2022
529a2a7
remove target blank
siriwatknp Jan 20, 2022
3918ec6
revert changes
siriwatknp Jan 20, 2022
12787ed
rename blog post file
siriwatknp Jan 21, 2022
756da13
Apply suggestions from code review
siriwatknp Jan 21, 2022
56e8a13
remove deprecation
siriwatknp Jan 25, 2022
470cc58
remove variant mention from the post
siriwatknp Jan 25, 2022
9746286
Merge branch 'master' into blog/v5.3
siriwatknp Jan 25, 2022
358aa14
Apply suggestions from code review
siriwatknp Jan 26, 2022
979e77b
add example description
siriwatknp Jan 26, 2022
2bb4d30
Apply suggestions from code review
siriwatknp Jan 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/pages/blog/callback-support-in-style-overrides.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from 'react';
import TopLayoutBlog from 'docs/src/modules/components/TopLayoutBlog';
import { docs } from './callback-support-in-style-overrides.md?@mui/markdown';

export default function Page() {
return <TopLayoutBlog docs={docs} />;
}
189 changes: 189 additions & 0 deletions docs/pages/blog/callback-support-in-style-overrides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
title: Introducing callback support in style overrides
description: We're excited to introduce the callback support for global theme overrides in this minor version update!
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
date: 2022-01-20T00:00:00.000Z
Copy link
Member

@oliviertassinari oliviertassinari Jan 26, 2022

Choose a reason for hiding this comment

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

Are we good with the date? Do we want to use the release date of v5.3.0 or the blog post publish date?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, I will mark the next Monday as the blog post released date.

siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
authors: ['siriwatknp']
tags: ['MUI Core', 'News']
card: false
---

MUI Core [v5.3.0](https://github.com/mui-org/material-ui/releases/tag/v5.3.0) introduces the ability to write a callback in style overrides (the global theming), giving you full control of component customization at the theme level.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

Why using a callback is better than the existing plain object? Let me explain from the beginning.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

## The problems

In v4, the style engine library was JSS which had some limitations.
Style overrides were not able to support dynamic props via a callback so we relied on using classes. Take a look at the [`Chip` classes](https://github.com/mui-org/material-ui/blob/97d32b0ff3fae4537c20c79e619f132f4a5c5cbb/packages/mui-material/src/Chip/chipClasses.ts) for example, there are more than 20 classes that are incomplete if we count the permutation of elements (`root | avatar | icon | label | deleteIcon`), size (`small | medium | large`), and color (`primary | secondary | ...`).
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
This leads to a bad theming experience because developers need to know the specific key to customize.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

We believe it would be better for developers if they could create custom styles by reading the component props, without ever needing to know what key they should use.

Fortunately, it is now possible in v5 because of the new style engine powered by emotion. Theming is simpler and more flexible. You only need to know the component's slot name and then provide an **object** (static overrides) or a **callback** (dynamic overrides).

## Using callback in `styleOverrides`

The callback gives you the `props` that the slot received. Most of the time, you would use:
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

- `props.ownerState`: the combination of runtime props and internal states.
- `props.theme`: the object you provide to `ThemeProvider` or the default one.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

```jsx
import { ThemeProvider, createTheme } from '@mui/material/styles';

<ThemeProvider
theme={createTheme({
components: {
MuiChip: {
styleOverrides: {
// you can now use the theme without creating the initial theme!
root: ({ ownerState, theme }) => ({
padding: {
small: '8px 4px',
medium: '12px 6px',
large: '16px 8px',
}[ownerState.size],
...(ownerState.variant === 'outlined' && {
borderWidth: '2px',
...(ownerState.variant === 'primary' && {
borderColor: theme.palette.primary.light,
Copy link
Member

Choose a reason for hiding this comment

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

OT: How circular references prevented/resolved?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, I don't quite understand. Can you give more details?

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 that there is any risk for circular references.

}),
}),
}),
label: {
padding: 0,
},
},
},
},
})}
>
...your app
</ThemeProvider>;
```

> 💡 The side benefit of using a callback is that you can use the runtime theme without creating the outer scoped variable.
Copy link
Member

Choose a reason for hiding this comment

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

the outer scoped variable

Will developer know what this means? (I might just be missing some context.)

Copy link
Member Author

Choose a reason for hiding this comment

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

My point is some developers might do this:

const theme = createTheme();

<ThemeProvider theme={createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          // just to give an example where I want to access theme in styleOverrides.
          // I have to create theme variable at the outer scope.
          backgroundColor: theme.palette.common.black,
        }
      }
    }
  }
})}>

Copy link
Member Author

Choose a reason for hiding this comment

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

Will developer know what this means?

Good point, I guess not everyone would understand. I add it without an actual code sample because I think it is just a side benefit which is not the main purpose.

Copy link
Member

@oliviertassinari oliviertassinari Jan 26, 2022

Choose a reason for hiding this comment

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

I think it is just a side benefit which is not the main purpose.

We have multiple threads where developers complain about this. But from what I remember, it was a step before. The main pain is for building the core set of design tokens, not so much for the customization of components.


### TypeScript

The callback is type-safe.

- `ownerState`: `ComponentProps` interface, eg. `ButtonProps`, `ChipProps`, etc.
- `theme`: `Theme` interface from `@mui/material/styles`.

```tsx
{
MuiChip: {
styleOverrides: {
// ownerState: ChipProps
// theme: Theme
root: ({ ownerState, theme }) => ({...}),
},
}
}
```

If you extend the interface via module augmentation like this:

```ts
declare module '@mui/material/Button' {
interface ButtonPropsVariantOverrides {
dashed: true;
}
}
```

you will be able to see those props in `ownerState.variant` 🎉. `theme` can be augmented as well.

## Experimental `sx` function

Initially, `sx` was designed to be a prop that enables you to inject styles with a shorthand notation to components created with the `styled` API:

```jsx
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';

const Label = styled('span')({
fontWeight: 'bold',
fontSize: '0.875rem',
})

<Box sx={{ display: 'flex' }}>
<Label sx={{ color: 'text.secondary' }}>Label</Label>
</Box>;
```

> 💡 All MUI components are created with `styled` API, so they accept `sx` prop by default.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
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
> 💡 All MUI components are created with `styled` API, so they accept `sx` prop by default.
> 💡 All MUI components are created with the `styled` API, so they accept `sx` prop by default.

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
> 💡 All MUI components are created with `styled` API, so they accept `sx` prop by default.
> 💡 All MUI components are created with the `styled` API, so they accept `sx` prop by default.


`sx` helps developers write less code and be more productive once they are familiar with the API. With the callback support in `styleOverrides`, it is now possible to use `sx` like syntax at the global theme overrides.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
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
`sx` helps developers write less code and be more productive once they are familiar with the API. With the callback support in `styleOverrides`, it is now possible to use `sx` like syntax at the global theme overrides.
`sx` helps developers write less code and be more productive once they are familiar with the API. With the callback support in `styleOverrides`, it is now possible to use an `sx`-like syntax in global theme overrides.


All you need is to use the [`experimental_sx`](/system/styled/#how-can-i-use-the-sx-syntax-with-the-styled-utility) function:

```jsx
import {
ThemeProvider,
createTheme,
experimental_sx as sx,
} from '@mui/material/styles';

<ThemeProvider
theme={createTheme({
components: {
MuiChip: {
styleOverrides: {
root: sx({
px: '12px', // shorthand for padding-left & right
py: '6px', // shorthand for padding-top & bottom
fontWeight: 500,
borderRadius: '8px',
}),
label: {
padding: 0,
},
},
},
},
})}
>
...your app
</ThemeProvider>;
```

If you want to create a dynamic style based on props, you can return an array from the callback:
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps explain the example? Either: "In the following example" or with inline comments (or both). Also consider if other examples would benefit from further explanation.


```js
{
root: ({ ownerState }) => [
sx({
px: '12px',
py: '6px',
fontWeight: 500,
borderRadius: '8px',
}),
ownerState.variant === 'outlined' && ownerState.color === 'default' &&
sx({
borderColor: 'text.secondary',
}),
ownerState.size === 'small' &&
sx({
fontSize: { xs: '0.875rem', sm: '0.75rem' },
})
],
// ...other keys
}
```

<hr />

**That's it for today!** Happy styling 💅.

I hope this small update makes your customization experience better than before. Don't forget to share this update with your friends and/or colleagues.
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

To get more updates like this in the future, **subscribe to our newsletter** at the bottom of this page.

## Read more

- [Component theming](/customization/theme-components/)
- [All supported shorthands in `sx`](/system/the-sx-prop/#theme-aware-properties)
- [`sx` performance tradeoff](/system/basics/#performance-tradeoff)
- [`sx` with `styled`](/system/styled/#difference-with-the-sx-prop)
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ If you are not familiar `sx`, first check out [the concept](/system/the-sx-prop/

## Adding new component variants

> ⚠️ This API has been **deprecated** and will likely be removed in the next major release. If you want to apply styles based on props, take a look at [Overrides based on props](#overrides-based-on-props) instead.
>
> If you are interested to see the reasoning behind this change, check out [issue #30412](https://github.com/mui-org/material-ui/issues/30412)

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.

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
18 changes: 8 additions & 10 deletions docs/src/pages/guides/migration-v4/migration-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,7 @@ As the core components use emotion as their style engine, the props used by emot

- The props: `alignItems` `alignContent` and `justifyContent` and their `classes` and style overrides keys were removed: "align-items-xs-center", "align-items-xs-flex-start", "align-items-xs-flex-end", "align-items-xs-baseline", "align-content-xs-center", "align-content-xs-flex-start", "align-content-xs-flex-end", "align-content-xs-space-between", "align-content-xs-space-around", "justify-content-xs-center", "justify-content-xs-flex-end", "justify-content-xs-space-between", "justify-content-xs-space-around" and "justify-content-xs-space-evenly".
These props are now considered part of the system, not on the `Grid` component itself.
If you still wish to add overrides for them, you can use the `theme.components.MuiGrid.variants` options.
If you still wish to add overrides for them, you can use the [callback as a value in `styleOverrides`](/customization/theme-components/#overrides-based-on-props).

> ✅ This is handled in the [preset-safe codemod](#preset-safe).

Expand All @@ -1459,12 +1459,11 @@ As the core components use emotion as their style engine, the props used by emot
- marginTop: '20px',
- },
- },
+ variants: {
+ props: { alignItems: "flex-end" },
+ style: {
+ styleOverrides: ({ ownerState }) => ({
+ ...ownerState.alignItems === 'flex-end' && {
+ marginTop: '20px',
+ },
+ }],
+ }),
},
},
});
Expand Down Expand Up @@ -2364,7 +2363,7 @@ As the core components use emotion as their style engine, the props used by emot

- The following `classes` and style overrides keys were removed: "colorInherit", "colorPrimary", "colorSecondary", "colorTextPrimary", "colorTextSecondary", "colorError", "displayInline" and "displayBlock".
These props are now considered part of the system, not on the `Typography` component itself.
If you still wish to add overrides for them, you can use the `theme.components.MuiTypography.variants` options.
If you still wish to add overrides for them, you can use the [callback as a value in `styleOverrides`](/customization/theme-components/#overrides-based-on-props).
For example:

```diff
Expand All @@ -2376,12 +2375,11 @@ As the core components use emotion as their style engine, the props used by emot
- marginTop: '20px',
- },
- },
+ variants: [{
+ props: { color: 'secondary' },
+ style: {
+ styleOverrides: ({ ownerState }) => ({
+ ...ownerState.color === 'secondary' && {
+ marginTop: '20px',
+ },
+ }],
+ }),
},
},
});
Expand Down