javascriptcssreactjsmaterial-uimakestyles

Change disabled Material UI checkbox color or background color


The disabled unchecked checkbox look a bit too subtle and I would like to have them have a grey background and for the cursor to be of type not-allowed.

Unfortunately I cannot figure out how to apply these styles on the checkbox using the makeStyles. This is what my current code looks like:


const useStyles = makeStyles((theme) => ({
  disabledCheckbox: {
    cursor: 'not-allowed',
    color: 'grey',
    backgroundColor: 'grey',
  },
}));

// ...

const App = () => {
  const classes = useStyles();
  return (
    <div>
      Disabled:
      <Checkbox
        disabled={true}
        name="Disabled checkbox"
        classes={{ disabled: classes.disabledCheckbox }}
      />
    </div>
  );
};

Unfortunately this does nothing, and the disabled checkbox looks the same. Here is a demo app to compare a disabled and enabled one.

What am I doing wrong? How can I change the background color of an unselected MUI disabled checkbox:


Solution

  • Using the disabled class by itself does not provide sufficient specificity to override the default styles in IconButton. Also, you don't want to override the background-color for the entire checkbox or it will fill in the entire area that gets the hover effect for the checkbox; instead you just want to target the icon within the checkbox (and even that is slightly more than ideal -- more about that later).

    Below is a way of defining the styles with enough specificity to target just the icon. Overriding the cursor requires some additional work because by default Material-UI disables pointer events on disabled buttons (Checkbox leverages SwitchBase which uses IconButton which uses ButtonBase) and the cursor CSS has no effect when pointer events are disabled. The CSS below turns pointer events back on, but then it is necessary to turn off the hover effect which was previously prevented via pointerEvents: 'none'.

    const useStyles = makeStyles((theme) => ({
      backgroundColorOnWholeIcon: {
        '&.Mui-disabled': {
          pointerEvents: 'auto',
          '&:hover': {
            backgroundColor: 'transparent',
          },
          cursor: 'not-allowed',
          '& .MuiSvgIcon-root': {
            backgroundColor: '#eee',
          },
        },
      },
    }));
    

    Unfortunately, this still doesn't produce quite what you likely want. The box of the checkbox icon does not extend to the edge of the 24px-by-24px box that the icon resides in, so when you set a background color it bleeds out of the box:

    icon with background color

    In order to fill the inner box of the checkbox without changing the color of the couple pixels outside that box, you need to create a custom icon.

    The code below creates a custom icon that is identical to the default unchecked icon except that it adds a second path duplicating the inner box of the checkbox without any fill so that it can be targeted via CSS.

    import React from 'react';
    import createSvgIcon from '@material-ui/icons/utils/createSvgIcon';
    
    export default createSvgIcon(
      <>
        <path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z" />
        <path fill="none" class="innerBox" d="M19 5v14H5V5h14" />
      </>,
      'CustomUnchecked'
    );
    

    Then you can target this inner box as follows:

    const useStyles = makeStyles((theme) => ({
      backgroundColorOnInnerBoxOfCustomIcon: {
        '&.Mui-disabled': {
          pointerEvents: 'auto',
          '&:hover': {
            backgroundColor: 'transparent',
          },
          cursor: 'not-allowed',
          '& .MuiSvgIcon-root .innerBox': {
            fill: '#eee',
          },
        },
      }
    }));
    

    In order for this to work, you need to specify the custom icon on the checkbox. It is also possible to do all of this via the theme if you want all of your disabled checkboxes to look like this.

    Below is a working example demonstrating doing this via both makeStyles and via the theme.

    import { Checkbox } from '@material-ui/core';
    import {
      makeStyles,
      createMuiTheme,
      ThemeProvider,
    } from '@material-ui/core/styles';
    import CustomUncheckedIcon from './CustomUnchecked';
    import React from 'react';
    
    const useStyles = makeStyles((theme) => ({
      backgroundColorOnInnerBoxOfCustomIcon: {
        '&.Mui-disabled': {
          pointerEvents: 'auto',
          '&:hover': {
            backgroundColor: 'transparent',
          },
          cursor: 'not-allowed',
          '& .MuiSvgIcon-root .innerBox': {
            fill: '#eee',
          },
        },
      },
      backgroundColorOnWholeIcon: {
        '&.Mui-disabled': {
          pointerEvents: 'auto',
          '&:hover': {
            backgroundColor: 'transparent',
          },
          cursor: 'not-allowed',
          '& .MuiSvgIcon-root': {
            backgroundColor: '#eee',
          },
        },
      },
    }));
    const defaultTheme = createMuiTheme();
    const theme = createMuiTheme({
      props: {
        MuiCheckbox: {
          icon: <CustomUncheckedIcon />,
        },
      },
      overrides: {
        MuiCheckbox: {
          root: {
            '&.Mui-disabled': {
              pointerEvents: 'auto',
              '&:hover': {
                backgroundColor: 'transparent',
              },
              cursor: 'not-allowed',
              // This syntax is necessary (instead of just ".MuiSvgIcon-root") because of the nested theme causing the global class names to be suffixed)
              '& [class*=MuiSvgIcon-root] .innerBox': {
                fill: '#eee',
              },
            },
          },
        },
      },
    });
    const App = () => {
      const classes = useStyles();
      return (
        <ThemeProvider theme={defaultTheme}>
          <div style={{ marginBottom: '16px' }}>
            Styled via makeStyles
            <br />
            Disabled without custom icon:
            <Checkbox
              className={classes.backgroundColorOnWholeIcon}
              disabled={true}
              name="Disabled checkbox"
            />
            <br />
            Disabled:
            <Checkbox
              className={classes.backgroundColorOnInnerBoxOfCustomIcon}
              disabled={true}
              icon={<CustomUncheckedIcon />}
              name="Disabled checkbox"
            />
            Enabled:
            <Checkbox
              icon={<CustomUncheckedIcon />}
              className={classes.backgroundColorOnInnerBoxOfCustomIcon}
              name="Enabled checkbox"
            />
          </div>
          <ThemeProvider theme={theme}>
            <div>
              Styled via theme
              <br />
              Disabled:
              <Checkbox disabled={true} name="Disabled checkbox" />
              Enabled:
              <Checkbox name="Enabled checkbox" />
            </div>
          </ThemeProvider>
        </ThemeProvider>
      );
    };
    
    export default App;
    

    Edit disabled checkbox