I'm using MUI in react. Let's say I have this component with these styles:
const useStyles = makeStyles(theme => ({ outerDiv: { backgroundColor: theme.palette.grey[200], padding: theme.spacing(4), '&:hover': { cursor: 'pointer', backgroundColor: theme.palette.grey[100] } }, addIcon: (props: { dragActive: boolean }) => ({ height: 50, width: 50, color: theme.palette.grey[400], marginBottom: theme.spacing(2) }) })); function App() { const classes = useStyles(); return ( <Grid container> <Grid item className={classes.outerDiv}> <AddIcon className={classes.addIcon} /> </Grid> </Grid> ); }
I want to change the style of addIcon
when hovering over outerDiv
using the styles above.
Here's my example.
The trick is to give the sibling the same size and position as the parent and to style the sibling instead of the parent. This will look like the parent is styled!
If you want to override a component's styles using custom classes, you can use the className prop, available on each component.
The hover selector is simple to add. There is no space needed because it it selecting the hover pseudo-class applied at the top level of the button component. Notice that there is no comma or semicolon between the &:hover and &:focus selectors. Instead of working with Material-UI's styling API, it overrides it.
Set the onMouseEnter and onMouseLeave props on the element. When the user hovers over or out of the element, update a state variable. Conditionally set inline styles on the element.
Below is an example of the correct syntax for v4 ("& $addIcon"
nested within &:hover
). Further down are some v5 examples.
import * as React from "react"; import { render } from "react-dom"; import { Grid, makeStyles } from "@material-ui/core"; import AddIcon from "@material-ui/icons/Add"; const useStyles = makeStyles(theme => ({ outerDiv: { backgroundColor: theme.palette.grey[200], padding: theme.spacing(4), '&:hover': { cursor: 'pointer', backgroundColor: theme.palette.grey[100], "& $addIcon": { color: "purple" } } }, addIcon: (props: { dragActive: boolean }) => ({ height: 50, width: 50, color: theme.palette.grey[400], marginBottom: theme.spacing(2) }) })); function App() { const classes = useStyles(); return ( <Grid container> <Grid item className={classes.outerDiv}> <AddIcon className={classes.addIcon} /> </Grid> </Grid> ); } const rootElement = document.getElementById("root"); render(<App />, rootElement);
Related documentation and answers:
For those who have started using Material-UI v5, the example below implements the same styles but leveraging the new sx
prop.
import Grid from "@mui/material/Grid"; import { useTheme } from "@mui/material/styles"; import AddIcon from "@mui/icons-material/Add"; export default function App() { const theme = useTheme(); return ( <Grid container> <Grid item sx={{ p: 4, backgroundColor: theme.palette.grey[200], "&:hover": { backgroundColor: theme.palette.grey[100], cursor: "pointer", "& .addIcon": { color: "purple" } } }} > <AddIcon className="addIcon" sx={{ height: "50px", width: "50px", color: theme.palette.grey[400], mb: 2 }} /> </Grid> </Grid> ); }
Here's another v5 example, but using Emotion's styled
function rather than Material-UI's sx
prop:
import Grid from "@mui/material/Grid"; import { createTheme, ThemeProvider } from "@mui/material/styles"; import AddIcon from "@mui/icons-material/Add"; import styled from "@emotion/styled/macro"; const StyledAddIcon = styled(AddIcon)(({ theme }) => ({ height: "50px", width: "50px", color: theme.palette.grey[400], marginBottom: theme.spacing(2) })); const StyledGrid = styled(Grid)(({ theme }) => ({ padding: theme.spacing(4), backgroundColor: theme.palette.grey[200], "&:hover": { backgroundColor: theme.palette.grey[100], cursor: "pointer", [`${StyledAddIcon}`]: { color: "purple" } } })); const theme = createTheme(); export default function App() { return ( <ThemeProvider theme={theme}> <Grid container> <StyledGrid item> <StyledAddIcon /> </StyledGrid> </Grid> </ThemeProvider> ); }
And one more v5 example using Emotion's css prop:
/** @jsxImportSource @emotion/react */ import Grid from "@mui/material/Grid"; import { createTheme, ThemeProvider } from "@mui/material/styles"; import AddIcon from "@mui/icons-material/Add"; const theme = createTheme(); export default function App() { return ( <ThemeProvider theme={theme}> <Grid container> <Grid item css={(theme) => ({ padding: theme.spacing(4), backgroundColor: theme.palette.grey[200], "&:hover": { backgroundColor: theme.palette.grey[100], cursor: "pointer", "& .addIcon": { color: "purple" } } })} > <AddIcon className="addIcon" css={(theme) => ({ height: "50px", width: "50px", color: theme.palette.grey[400], marginBottom: theme.spacing(2) })} /> </Grid> </Grid> </ThemeProvider> ); }
Possibly an obvious point, but just to add to the answer above: if you are referencing a separate className, don't forget that you also need to create it in the makeStyles hook or else it won't work. For instance:
const useStyles = makeStyles({ parent: { color: "red", "&:hover": { "& $child": { color: "blue" // will only apply if the class below is declared (can be declared empty) } } }, // child: {} // THIS must be created / uncommented in order for the code above to work; assigning the className to the component alone won't work. }) const Example = () => { const classes = useStyles() return ( <Box className={classes.parent}> <Box className={classes.child}> I am red unless you create the child class in the hook </Box> </Box> ) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With