How to Customize Material-ui theme v3.2.0+ (Part 3)

siriwatknp
Bits and Pieces
Published in
6 min readOct 31, 2018

--

Override component variables can be done in 2 ways

  1. overrides default theme at the root of the app using MuiThemeProvider . This way will affect all components across the app.
  2. use withStyles to customize component in specific place. This approach only affect component that we define.

Let’s get started

The result from Tutorial Part 2

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

Comparing navigator differences between Us(left) and Firebase(right)

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;
Result of 1st attempt

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',
},
},
},
});
Result of 2nd attempt

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.

Result after override primary text

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)
Result after override sub-category item

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.

Next week, we will use react-emotion to make theming seems like a piece of cake, Stay tune.

--

--

Passionate in Design, Theming, React & Firebase. Focus on component reusability.