Styling with React’s Material-UI v4 – Part 1

by | Aug 5, 2021 | 0 comments

Documentation Note: makeStyles is a function that links a style sheet with a function component using the hook pattern.

Throughout the article I will be explaining the above documentation note, in further detail.

*   *   *

This is a LENGTHY article, so please bear with me, if you are new to Material-UI, this is all I wished for when I’ve started to work with Material-UI.

I am currently focusing on Material-UI v4, and in later articles I will also discuss Material-UI v5.

 

import { makeStyles } from "@material-ui/core";

const useStyles = makeStyles({
    simpleButton: {
      background: "deepskyblue",  
      color: "white",
      fontWeight: "bold",
      width: "150px",
      padding: "10px"
    }
});

export const Button = () => {
  const classes = useStyles();
  return (
   <div className={classes.simpleButton} variant="contained">
       I Read. You Learn.
   </div>
  );
};
material-ui-div-styling

 (useStyles is Material-UI’s naming convention for the hook being returned).

 

// makeStyles:
// 1. Associates a new style sheet with our styles.
// 2. returns a hook to our useStyles variable.
const useStyles = makeStyles({
    simpleButton: {
      background: "deepskyblue",  
      color: "white",
      fontWeight: "bold",
      width: "150px",
      padding: "10px"
    }
});
  • Then I’ve used the class name generated for the property “simpleButton” and used it on the button as its className attribute.
export const Button = () => {
  // Here I'm calling our hook, 
  // and getting an object with generated class names.
  const classes = useStyles();
  // Using the generated class name inside className
  return (
   <div className={classes.simpleButton} variant="contained">
       I Read. You Learn.
   </div>
  );
};

The class name that will be returned from the useStyles hook:

{simpleButton: "makeStyles-simpleButton-1"}

When the page is loaded, Material-UI will inject our new styles at the bottom of the head tag:

material-ui-injected-dom-styles-head
material-UI-docs-css
import { Button, makeStyles } from "@material-ui/core";
// Using the reserved rule names: "root", "label".
const useStyles = makeStyles({
    root: {
      background: "deepskyblue",
      padding: "10px"
    },
    label: {
      width: "150px",
      color: "white",
      fontWeight: "bold",
    }
});

export const StyledButton = () => {
  const classes = useStyles();
{/*Passing the classes to the "classes" property.*/}
  return (
  <Button classes={classes} variant="contained">
    I Read. You Learn.
  </Button>
  );
};
material-ui-simple-button-styling
{root: "makeStyles-root-15", label: "makeStyles-label-16"}
const useStyles = makeStyles((theme) => { 
  return { 
    root: {
      background: "skyblue",
      padding: "10px",
    }
  }
},{name: "ireadyoulearn"});
material-ui-unique-id

You might encounter issues with applying your styles with a more complicated Material-UI components, causing you to use !important to override it’s styles or you will need a more complex CSS selectors to reach your nested DOM elements.

<Button className="add-class" variant="contained">
    I Read. You Learn.
</Button>;

// Then access your "add-class" using an imported SCSS / CSS file.
const useStyles = makeStyles({
  testOne: {
    background: "red"
  },
  testTwo: {
    background: "green"
  }
})
const useStylesTwo = makeStyles({
  testThree: {
    background: "blue"
  },
})

export const StyledButton = () => {
  const classes = useStyles();
  const classesTwo = useStylesTwo();
  // Using all the created classes:
  return <div className={`
              ${classes.test} 
              ${classesTwo.testThree} 
              ${classes.testTwo}
              `}>
    Test Order Of Precedence
  </div>
}
const useStyles = makeStyles((theme) => {
  return { 
    root: {
      background: hookData => hookData.backgroundColor,
      padding: "10px"
    },
    label: {
      width: "150px",
      color: "white",
      fontWeight: "bold"
    }
  }
});

export const ChangedStylesButton = () => {
  const [backgroundColor,setBackgroundColor] = useState("deepskyblue")
  const classes = useStyles({ backgroundColor });
  
  const changeColor = () => {
    setBackgroundColor("red")
  }

  return (
    <div>
      <Button 
        onClick={changeColor} 
        classes={classes} 
        variant="contained">
        I Read. You Learn.
      </Button>
    </div>
  );
};

I’ve also passed an object containing the background color to our returned hook.

const [backgroundColor,setBackgroundColor] = useState("deepskyblue")
// Passing the backgroundColor state to the hook:
const classes = useStyles({ backgroundColor });
const useStyles = makeStyles((theme) => {
  return { 
    root: {
      // Passed a function to access the hook data
      background: hookData => hookData.backgroundColor,
      padding: "10px"
    }
  }
}
----------------------------------------------------------------------
  const [backgroundColor,setBackgroundColor] = useState("deepskyblue")
  // Passed the background color to makeStyles.
  const classes = useStyles({ backgroundColor });
const useStyles = makeStyles((theme) => { 
  return { 
    root: {
      background: "deepskyblue",
      padding: "10px",
// Using parent selector to reach the h4 element
      "& h4": {
        width: "150px",
        color: "white",
        fontWeight: "bold",
        margin: 0
      }
    }
  }
});

export const ChangedStylesButton = () => {
  const classes = useStyles();  
  return (
    <div>
      <Button 
        classes={classes} 
        variant="contained">
        {/* Our nested element */}
        <h4>I Read. You Learn.</h4>
      </Button>
    </div>
  );
};
material-ui nested element styling
const useStyles = makeStyles((theme) => {
  return {
    root: {
      background: "skyblue",
      padding: "10px",
      "&:disabled": {
        // Activated pointer events to allow changing
        // background color on hover.
        pointerEvents: "auto",
        background: "black"
      },
      "&:hover": {
        "&:disabled": {
          background: "red"
        }
      }
    },
    label: {
      color: "white",
      fontWeight: "bold"
    }
  };
});

export const ChangedStylesButton = () => {
  const classes = useStyles();
  return (
    <div>
      {/*Notice the disabled property*/}
      <Button disabled classes={classes} variant="contained">
        I Read. You Learn
      </Button>
    </div>
  );
};
.makeStyles-root-181 {
  padding: 10px;
  background: skyblue;
}
.makeStyles-root-181:disabled {
  background: black;
  pointer-events: auto;
}
.makeStyles-root-181:hover:disabled {
  background: red;
}
.makeStyles-label-182 {
  color: white;
  font-weight: bold;
}
const useStyles = makeStyles((theme) => {
  return {
    root: {
      background: "skyblue",
      padding: "10px",
    },
    "disabled": {
      "&$root": {
        pointerEvents:"auto",
      },
      "&$root:hover": {
        background: "red"
      }
    }
  };
});
.makeStyles-root-177 {
  padding: 10px;
  background: skyblue;
}
.makeStyles-disabled-178.makeStyles-root-177 {
  pointer-events: auto;
}
.makeStyles-disabled-178.makeStyles-root-177:hover {
  background: red;
}
const styles = {
  container: {
    // Reference the local rule "button".
    '&:hover $button': {
      color: 'blue'
    }
  },
  button: {
    color: 'grey'
  }
}
.container-0:hover .button-1 {
  color: blue;
}

All that you’ve learned in this article is also available using the Higher-Order Component withStyles. Except for passing information to your hook component and accessing it with a function as an argument.

That is why I would not go in-depth about it here.