Applying embedded styles to MUI components based on variants

Viewed 493

I am trying to apply custom styling to the component based on its variants. However, a single component can have multiple variants specifying different style-properties. For this question, I will use code snippets from the Button component.

My wrapper of the Material UI Button component (defined here as MaterialButton):

import MaterialButton from '@material-ui/core/Button'
import './button-design-tokens.module.css'

export const Button: React.FC<ButtonProps> = (
  props: ButtonProps
) => {
  return (
      <MaterialButton {...props}>{props.children}</MaterialButton>
  )
}

I am trying to apply the following styles to this component in a Storybook environment using useStyles():

const useStyles = makeStyles({
  contained: {
    backgroundColor: 'var(--mdh-button-background-color)',
    fontFamily: 'var(--mdh-button-font-family)',
    '&.primary': {
      color: 'var(--mdh-button-secondary-background-color)',
    },
    '&.secondary': {
      color: 'var(--mdh-button-secondary-background-color)'
    },
    '&:hover': {
      backgroundColor: 'var(--mdh-button-hover-background-color)',
      color: 'var(--mdh-button-hover-text-color)'
    },
    '&:focus': {
      backgroundColor: 'var(--mdh-button-focus-background-color)',
      color: 'var(--mdh-button-focus-text-color)'
    },
    '&$disabled': {
      backgroundColor: 'var(--mdh-button-disabled-background-color)'
    }
  },
  outlined: {
    borderRightColor: 'var(--mdh-button-background-color)',
    borderLeftColor: 'var(--mdh-button-background-color)',
    borderTopColor: 'var(--mdh-button-background-color)',
    borderBottomColor: 'var(--mdh-button-background-color)',
    '&:hover': {
      borderRightColor: 'var(--mdh-button-hover-background-color)',
      borderLeftColor: 'var(--mdh-button-hover-background-color)',
      borderTopColor: 'var(--mdh-button-hover-background-color)',
      borderBottomColor: 'var(--mdh-button-hover-background-color)'
    },
    '&:focus': {
      borderRightColor: 'var(--mdh-button-focus-background-color)',
      borderLeftColor: 'var(--mdh-button-focus-background-color)',
      borderTopColor: 'var(--mdh-button-focus-background-color)',
      borderBottomColor: 'var(--mdh-button-focus-background-color)'
    }
  },
  outlinedPrimary: {
    color: 'var(--mdh-button-background-color)'
  },
  outlinedSecondary: {
    color: 'var(--mdh-button-secondary-background-color)'
  },
  textPrimary: {
    color: 'var(--mdh-button-background-color)',
    '&:hover': {
      color: 'var(--mdh-button-disabled-background-color)'
    },
    '&:focus': {
      color: 'var(--mdh-button-disabled-background-color)'
    }
  },
  textSecondary: {
    color: 'var(--mdh-button-secondary-background-color)',
    '&:hover': {
      color: 'var(--mdh-button-disabled-background-color)'
    },
    '&:focus': {
      color: 'var(--mdh-button-disabled-background-color)'
    }
  }
})

As you can see, a Material UI button has the variants contained (a normal button), outlined (a button with only a coloured border) and text (a button consisting only of text). These variants can, in turn, have the primary, secondary and default variants which change the colour of the text inside the button.

The CSS variables seen in the JSON are defined in the css file imported in the Button wrapper. This file has the following contents:

:root {
  --mdh-button-background-color: rgb(34, 123, 60);
  --mdh-button-secondary-background-color: #EDBF07;
  --mdh-button-focus-background-color: #9a6f1e;
  --mdh-button-focus-text-color: white;
  --mdh-button-hover-background-color: #9a6f1e;
  --mdh-button-hover-text-color: white;
  --mdh-button-disabled-background-color: #666;
  --mdh-button-font-family: "TheMix", sans-serif;
}

.mdh-button {
  background-color: var(--mdh-button-background-color);
  font-family: var(--mdh-button-font-family);
}

.mdh-button--focus, .mdh-button:focus {
  background-color: var(--mdh-button-focus-background-color);
  color: var(--mdh-button-focus-text-color);
}

.mdh-button--hover, .mdh-button:hover {
  background-color: var(--mdh-button-hover-background-color);
  color: var(--mdh-button-hover-text-color);
}

.mdh-button--disabled {
  background-color: var(--mdh-button-disabled-background-color);
}

As you can see, in order to style, for example, the outlined button in the primary variant, I would have to style this by combining the two variants in a class name (i.e. OutlinedPrimary). This is fine for minor changes, but I am developing a design system that is based on MUI and will have to re-style almost all the MUI components I am using in the system. This would lead to an insanely bloated and incomprehensible stylesheet for each component. Although this works, I am trying for a more compact and (in my humble opinion) logical approach. It is already defined in the JSON above, but I will highlight it below once more:

contained: {
    backgroundColor: 'var(--mdh-button-background-color)',
    fontFamily: 'var(--mdh-button-font-family)',
    '&.primary': {
      color: 'var(--mdh-button-secondary-background-color)',
    },
    '&.secondary': {
      color: 'var(--mdh-button-secondary-background-color)'
    },
    '&:hover': {
      backgroundColor: 'var(--mdh-button-hover-background-color)',
      color: 'var(--mdh-button-hover-text-color)'
    },
    '&:focus': {
      backgroundColor: 'var(--mdh-button-focus-background-color)',
      color: 'var(--mdh-button-focus-text-color)'
    },
    '&$disabled': {
      backgroundColor: 'var(--mdh-button-disabled-background-color)'
    }
  }

The primary variant is treated here as a 'subclass' of the contained variant of the Button. However, for some reason, this is not picked up by either Storybook or Material UI. I am applying the classes to the component like so:

const Template: Story<ButtonProps> = (args: any) => {
  const classes = useStyles()

  args.classes = {
    contained: classes.contained,
    outlined: classes.outlined,
    // containedPrimary: classes.containedPrimary,
    // containedSecondary: classes.containedSecondary,
    textPrimary: classes.textPrimary,
    textSecondary: classes.textSecondary,
    outlinedPrimary: classes.outlinedPrimary,
    outlinedSecondary: classes.outlinedSecondary
  }
  return <Button {...args}>Button</Button>
}

I have tried various and browsed through countless articles and blogs on the topic but have gotten none the wiser. Is there something I am missing? As far as I am aware, the syntax is correct.

Thank you in advance!

0 Answers
Related