How to Customize Material-ui theme v3.2.0+ (Part 3)
- Part 1 : Understand MUI theme structure & How to read doc
- Part 2 : Override global variables & Tools
- Part 3 : Override component variables
- Part 4 : Override with external library react-emotion (Release 5/11)
Override component variables can be done in 2 ways
- overrides default theme at the root of the app using
MuiThemeProvider
. This way will affect all components across the app. - use
withStyles
to customize component in specific place. This approach only affect component that we define.
Let’s get started
Recap from Part 2, we separate component into 3 parts. Navigator
Header
Content
. We will focus only Navigator
for demonstration purpose.
This is the differences between us and Firebase
Navigator Background : 1st Attempt
add this code in your project to see changes.
// in src/theme/muiTheme.jsimport { createMuiTheme } from '@material-ui/core/styles';
const theme = createMuiTheme({
...
overrides: {
MuiDrawer: {
background: '#18202c',
},
},
});
export default theme;
Oops! nothing change on our screen. As you can see, our background css is not injected. ( ⌘+⌥+ i
to open chrome developer or right click on the screen and select inspect. For more detail)
Well, customisation is not that straightforward. In order to change css in components, you must first look at the component’s css api. This is the api for Drawer from Material-UI Page
Navigator Background : 2nd Attempt
We will change the Paper’s background in Drawer because the Drawer’s Paper has background(#ffffff) by default. You can look into the mui’s repo here to understand how the component works.
const theme = createMuiTheme({
...
overrides: {
MuiDrawer: {
paper: {
background: '#18202c',
},
},
},
});
Oh Yeah!. Now it works.
What happen behind the scene is that Material-ui deeply merges our theme to the default theme before it is generated to a style. That’s why the new background colour stay with default css.
Navigator Font Style
Now, we have to change icon color and typography color to white. Because everything in the navigator will be white, we are not going to change them one by one. Instead, we will put a single line of code and let the magic do their job.
const theme = createMuiTheme({
...
overrides: {
MuiDrawer: {
paper: {
background: '#18202c', // this is where magic happens
'& *': { color: 'rgba(255, 255, 255, 0.7)' },
},
},
},
});
That’s right, we can write css in the theme because material-ui use css-in-js under the hood.
Most recently, we have moved toward a CSS-in-JS solution. We think that it’s the future.
Read the explanation here or if you want to know more about CSS in Js, here is a great explanation from Indrek Lasn
Next, we will override font size
and weight
with another approach because it is not about Drawer
that we want to customize, it is about Typography
and List
.
Let’s import cx
and withStyles
// open file /src/parts/Navigator.jsimport cx from 'classnames';
import withStyles from '@material-ui/core/styles/withStyles';
- cx (classnames) is a tool that help concat className
- withStyles is a React Higher Order Component that inject classes to component.
Define theme that we want to customize.
// in the same fileconst styles = theme => ({
categoryHeaderText: {
fontSize: 15,
fontWeight: 500,
color: theme.palette.common.white,
},
itemText: {
fontSize: 14,
fontWeight: 500,
},
});
Finally, wrap withStyles
to our component and Navigator
will receive classes
as a prop that contains categoryHeaderText
and itemText
.
const Navigator = ({ classes }) => (
<Drawer variant="permanent">
<List>
<ListItem>
... // Firebase logo
</ListItem>
<ListItem>
... // Project Overview
</ListItem>
{categories.map(({ id, children }) => (
<React.Fragment key={id}>
<ListItem>
<ListItemText
classes={{
primary: classes.categoryHeaderText,
}}
>
{id}
</ListItemText>
</ListItem>
... // each sub-category
</React.Fragment>
))}
</List>
</Drawer>
);export default withStyles(styles)(Navigator)
For classes.categoryHeaderText
, we have to put it inside ListItemText
classes because we want to override the text inside, not the container.
However, when comes to ListItemText
in sub-category item, it is a bit confused. Because of the prop dense
we define in jsx, material-ui default use JSS to compile to .primary-74.textDense-76
If we don’t follow &$textDense
as default, we won’t be able to override because the order of our class is below the default. For example,
// I remove MuiListItemText-{...}-hash for readibility// default (2 consecutive)
.primary.textDense {
font-size: inherit; // win
}// override (only 1)
.primary {
font-size: 14px; // lose
}
To follow the default’s pattern, write this way.
const styles = theme => ({
...
itemText: {
fontSize: 14,
fontWeight: 500,
'&$textDense': {
fontSize: 14,
fontWeight: 500,
},
},
textDense: {},
});const Navigator = ({ classes }) => (
<Drawer variant="permanent">
<List>
... // Firebase logo
... // Project Overview
{categories.map(({ id, children }) => (
<React.Fragment key={id}>
... // category header
{children.map(({ id: childId, icon }) => (
<ListItem
button
dense // !!!
key={childId}
>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText
classes={{
primary: classes.itemText,
textDense: classes.textDense,
}}
>
{childId}
</ListItemText>
</ListItem>
))}
</React.Fragment>
))}
</List>
</Drawer>
);export default withStyles(styles)(Navigator)
Navigator Spacing & Icon
This is quite easy because all we have to do is following the same rules as before.
const styles = theme => ({
item: {
padding: '4px 24px',
'& svg': {
fontSize: 20,
},
},
itemIcon: {
margin: 0,
},
categoryHeader: {
padding: '24px 24px 16px',
},
...
});
and define classes in our component
// for category header
<ListItem className={classes.categoryHeader}>...</ListItem>// for sub-category item
<ListItem className={classes.item}>...</ListItem>// for all icon
<ListItemIcon className={classes.itemIcon}>...</ListItemIcon>
Wow, is this Firebase? haha, not exactly but quite similar.
In my opinion, customization in material-ui is still complex and not that straightforward. There is a lot of details that I have to dig into the official repo. This won’t be easy for new comers and beginners at all.
Sadly, I will not go any further because it will only make the story too big until no one want to read it. You’ve got everything you need to customize Material-ui theme.