Material UI theming and style overrides - Part 3

Today I learned more about sharing styles across React components. Although CSS-in-JS is new to me, it is pretty easy to pick up (Although I have not formed a final opinion on it yet…). I think Material UI uses React JSS under the hood, so a style can be written as an object:

import React from 'react';
import { makeStyles } from '@material-ui/styles';

const useStyles = makeStyles({
  root: {
    backgroundColor: 'red',
  },
});

export default function MyComponent() {
  const classes = useStyles();
  return <div className={classes.root} />;
}

Refactor to be more DRY

I had three components that were pretty similar to each other, with enough unique parts to keep them seperate. But they all had a style object in them. I could see the style object was about 90% duplicated between each component. So after using a diff tool to see what the differences were, I extracted the shared styles out into a new file called Styles.js. That looks a little like this:

// Provide access to my Material UI theme
import CustomTheme from "../../Theme";

const Styles = {
  title: {
    "& .MuiFormLabel-root": {
      fontSize: CustomTheme.typography.pxToRem(24)
    },
    "& .MuiInputBase-input": {
      fontSize: CustomTheme.typography.pxToRem(24)
    },
    "& .MuiInputLabel-asterisk": {
      color: CustomTheme.palette.error.main
    }
  },
  paper: {
    marginTop: CustomTheme.spacing(4),
    paddingTop: CustomTheme.spacing(4),
    paddingLeft: CustomTheme.spacing(4),
    paddingRight: CustomTheme.spacing(4),
    borderLeft: `3px solid transparent`
  },
  paperActive: {
    boxShadow: CustomTheme.shadows[7],
    borderLeft: `3px solid ${CustomTheme.palette.primary.main}`
  },
  input: {
    marginTop: CustomTheme.spacing(1),
    marginBottom: CustomTheme.spacing(1),
    "& .MuiInputBase-input": {
      fontSize: CustomTheme.typography.pxToRem(14)
    }
  },
  button: {
    marginLeft: CustomTheme.spacing(1),
    marginRight: CustomTheme.spacing(1)
  },
  footer: {
    display: "flex",
    justifyContent: "flex-end",
    marginTop: CustomTheme.spacing(6),
    padding: CustomTheme.spacing(2),
    borderTop: `1px solid ${CustomTheme.palette.grey[300]}`
  },
  vertDivider: {
    width: 1,
    height: 48
  }
};

export default Styles;

A little bit of spread

I can then import that file into my component and combine the shared style object (using ES6 spread syntax) with some component specific styles like this:

// React & Material UI
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
// Material UI components
import Paper from "@material-ui/core/Paper";
import TextField from "@material-ui/core/TextField";
import Input from "@material-ui/core/Input";
// App custom
import CustomStyles from "./Shared/Styles";

// Combine both objects using spread
const componentStyleOverrides = {
  ...CustomStyles,
  input: {
    width: "40%"
  }
};
const useStyles = makeStyles(componentStyleOverrides);

function ShortAnswer(props) {
  // ...
  // Additonal functionaliy removed for brevity
  // ...

  return (
    <Paper
      className={
        hasFocus === true
          ? [classes.paper, classes.paperActive].join(" ")
          : classes.paper
      }
      onClick={handleFocus}
    >
      <form className={classes.form} noValidate>
        <TextField
          value={props.question}
          id="question"
          classes={{ root: classes.title }}
          label="Question"
          onChange={handleChange("question")}
          required
          fullWidth
          autoFocus
        />
        <TextField
          value={props.description}
          id="description"
          label="Description (optional)"
          onChange={handleChange("description")}
          margin="normal"
          fullWidth
        />
        <Input
          value="Short answer text"
          className={classes.input}
          disabled
        />
      </form>
    </Paper>
  );
}

export default ShortAnswer;

Conclusion

My components now have a common style, which is good. But I still need to assess what the advantages to this method are to me and my team. I am knowledgeable enough with regular CSS to know how to modularise it and keep the scope of styles targeted. I like how CSS-in-JS can take advantage of functions and logic, but a lot of that could be achieved via SCSS or PostCSS plugins. I also need to assess the overall architecture of this app, as other components will have a seperate style object of their own.