score:3

Accepted answer

From https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity:

When multiple declarations have equal specificity, the last declaration found in the CSS is applied to the element.

So in your case where you are defining CSS classes in your custom component (e.g. TestButton) and in the code that uses that component, the specificity is determined by the order in which those CSS classes appear within the <head> element. This order is determined by an index that is set when makeStyles is called, so classes defined by later calls to makeStyles will appear later in the <head> element and thus have greater specificity.

There are two issues then in your example:

  1. TestButton is defined after the code that uses it and therefore after the makeStyles call that is defining styles intended to override styles in TestButton. Since the makeStyles call for gButton occurs first, the corresponding CSS class will be first in the <head> element. In real-world usage though, TestButton (your custom component) would be defined in a separate file and be imported. Since imports have to be at the top, any makeStyles calls at the top level of the imported file will be executed before any makeStyles calls in the file using the imported component.

  2. The makeStyles call for TestButton is not being done at the top level. Instead it is being done inside the TestButton function which means it will be executed when TestButton is rendered instead of when TestButton is imported. Calls to makeStyles should always be at the top level rather than nested within a component function. One other minor issue is the name of the variable returned from makeStyles (i.e. GrangeButtonStyles in your example). Since makeStyles returns a custom hook, you should always have a name that starts with "use" (e.g. useGrangeButtonStyles). This will ensure that the eslint rules for hooks recognize it as a hook and warn you of any hook misuse.

Related answers and references:

score:-1

<TestButton className={classes.gButton} color={"default"}>

// should be

<TestButton classes={{button:classes.gButton}} color={"default"}>

// and

<TestSelect className={classes.gSelect}

// should be

<TestSelect className={{dropdown:classes.gSelect}}

^ when dealing with material-ui's styling solution, don't pass "className" to components (only put this prop on DOM elements!!!!)

and

function TestButton(props) {
  const classes = GrangeButtonStyles();
  ...

// should be


function TestButton(props) {
  const classes = GrangeButtonStyles(props);
  ...


^ This will cause the prop classes.button (which will look like jss-1233) to be merged with the button class that comes out of GrangeButtonStyles so classes will now look like this:

{
  button: 'jss-7382 jss-1233' <- jss-1233 is the classname that got generated in the previous component
}

and

    <Button
      className={clsx(classes.button, color, props.className)}

// should be

    <Button
      classes={{root:classes.button)}

^ See material-ui docs for button

It's actually unfortunate that material-ui forwards it's refs to the DOM element without checking for className because this allows people to put className on material-ui components and it "kind of" work. They should really add warnings to use classes instead. Thanks for a commentor for correcting my mistake, I still like the verbosity of passing classes instead of className as mixing the two can result in confusion!

EDIT:

I also noticed you're messing up the styles in TestSelect - always keep this in mind - don't pass className as props to material-ui components, only pass classes. If you want to pass styles from a parent component to a child component then you've got to use the same key:

Let's try an example, I know, this stuff is hard to grok but eventually it will "click":

const useParentStyles = makeStyles({
   childStyles: { ... some jss }
   childStyles2: { ... some jss }
});

const Parent = props => {
   const classes = useParentStyles(props);
   return <Child classes={{root:classes.childStyles,someOtherKey:classes.childStyles2}}/> <- object with a keys of "root" and "someOtherKey"
}

const useChildStyles = makeStyles({
  root: { ... some jss } <- root
  someOtherKey: { ... some jss } <- someOtherKey 
});

const Child = props => {
   const classes = useChildStyles(props); <- classes have been merged together
   return <div className={classes.root}>...</div>
}

Related Query

More Query from same tag