How to Use CSS in Js for Nested Hover Styles, Material Ui

how to use css in JS for nested hover styles, Material UI

The correct syntax is "&:hover > div:hover": { ... }.

Here is a working example demonstrating the syntax:

import React from "react";
import ReactDOM from "react-dom";

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

const useStyles = makeStyles({
navlink: {
border: "1px solid green",
fontSize: "16pt",
"&:hover": {
backgroundColor: "lightgreen"
},
"&:hover > div:hover": {
backgroundColor: "lightblue"
}
}
});
function App() {
const classes = useStyles();
return (
<div className="App">
<div className={classes.navlink}>
Hello <div>CodeSandbox</div>
</div>
</div>
);
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit nested div hover

It also works to deeply nest with this syntax:

const useStyles = makeStyles({
navlink: {
border: "1px solid green",
fontSize: "16pt",
"&:hover": {
backgroundColor: "lightgreen",
"& > div:hover": {
backgroundColor: "lightblue"
}
}
}
});

Edit nested div hover

Here is the relevant JSS documentation: https://cssinjs.org/jss-plugin-nested/?v=v10.0.0-alpha.24

Related answer:

  • How do you change a style of a child when hovering over a parent using material-ui jss styles

How do you change a style of a child when hovering over a parent using MUI styles?

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);

Edit eager-tesla-xw2cg

Related documentation and answers:

  • https://cssinjs.org/jss-plugin-nested?v=v10.0.0#use-rulename-to-reference-a-local-rule-within-the-same-style-sheet
  • how to use css in JS for nested hover styles, Material UI
  • Material UI: affect children based on class
  • Advanced styling in material-ui

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>
);
}

Edit hover over parent


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>
);
}

Edit hover over parent


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>
);
}

Edit hover over parent

How to display div element on hover another element using css-in-js?

Please check this example:

import React, {useState} from "react";

export default function ShowButtonHover() {
const [style, setStyle] = useState({display: 'none'});

return (
<div className="App">
<h2>Hidden message in the box. Move mouse in the box</h2>
<div style={{border: '1px solid gray', width: 300, height: 300, padding: 10, margin: 100}}
onMouseEnter={e => {
setStyle({display: 'block'});
}}
onMouseLeave={e => {
setStyle({display: 'none'})
}}
>
<div style={style}>This was hidden</div>
</div>
</div>
);
}

How to override style of nested Material UI component from the ancestors?

Let's say that the class name generated for myComponent is myComponent-jss123. The selector you used in your styles (&.MuiIconButton-root) would be equivalent to .myComponent-jss123.MuiIconButton-root which would match any element that has both of these classes applied to it. I believe your intent was to match icon buttons which are descendants of the div on which you are applying the myComponent class. In this case you need to use the descendant combinator represented by a space, so the appropriate styles would look like the following:

const styles = theme => ({
myComponent: {
"& .MuiIconButton-root": {
padding: 0
}
}
});

Here's a full working example:

import React from "react";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles({
myComponent: {
"& .MuiIconButton-root": {
padding: 0
}
}
});
const ThirdPartyComponent = () => {
return (
<div>
I'm a third party component that contains an IconButton:
<IconButton color="primary">
<DeleteIcon />
</IconButton>
</div>
);
};
export default function App() {
const classes = useStyles();
return (
<div className={classes.myComponent}>
<ThirdPartyComponent />
</div>
);
}

Edit target descendant icon button

Related documentation:

  • https://cssinjs.org/jss-plugin-nested/?v=v10.0.4#use--to-reference-selector-of-the-parent-rule
  • https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors
  • https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator

Material ui TreeItems change style conditionally based

After a little while this is what I got. I tried to match the design of the gif as good as possible.

Sample Image

I used this data which I derived from the question. This example has one disabled node and parent node (my-photo.jpg and zip directory). The data array can be expanded endlessly if it matches TreeData type.

export type TreeData = {
id: string;
name: string;
disabledButton: boolean;
children?: TreeData[];
};

const data: TreeData[] = [
{
id: "1",
name: "My Web Site",
disabledButton: false,
children: [
{
id: "2",
name: "images",
disabledButton: false,
children: [
{ id: "3", name: "logo.png", disabledButton: false },
{ id: "4", name: "body-back.png", disabledButton: false },
{ id: "5", name: "my-photo.jpg", disabledButton: true }
]
},
{
id: "6",
name: "resources",
disabledButton: false,
children: [
{
id: "7",
name: "pdf",
disabledButton: false,
children: [
{ id: "8", name: "brochure.pdf", disabledButton: false },
{ id: "9", name: "prices.pdf", disabledButton: false }
]
},
{
id: "10",
name: "zip",
disabledButton: true,
children: [{ id: "11", name: "test.zip", disabledButton: false }]
}
]
}
]
}
];

The CustomTreeView consists of CustomTreeItem that again makes use of CustomContent.
CustomContent is used to handle all events and to display another icon (folder) next to the expandIcon.

In the CustomTreeItem I set the width to fit-content to not select the whole row, but to match the example from the gif:

const CustomTreeItem = (props: TreeItemProps) => (
<TreeItem
ContentComponent={CustomContent}
{...props}
sx={{
width: "fit-content",
".MuiTreeItem-content": {
py: "2px",
width: "fit-content"
}
}}
/>
);

The interesting part is the styling of the CustomTreeView and its usage.
I have packed the classes into objects, which can be overwritten easily. The theming happens in the styles class which is in styles/Styles.ts.

// Styles.ts
import { createTheme } from "@mui/material";

export const getMuiTheme = () =>
createTheme({
palette: {
primary: {
main: "#2160dd"
},
secondary: {
main: "#d3d3d3"
}
}
});

// CustomTreeView.tsx
const classes = {
focused: {
bgcolor: "transparent",
py: "1px",
px: "7px",
border: `1px solid ${getMuiTheme().palette.secondary.main}`
},
selected: {
bgcolor: getMuiTheme().palette.primary.main,
color: "white"
}
};

const CustomTreeView = ({ data }: { data: TreeData[] }) => {
return (
<Box mt={2} ml={2} bgcolor="white" width="300px">
<ThemeProvider theme={getMuiTheme()}>
<TreeView
defaultCollapseIcon={<ArrowDropDownIcon />}
defaultExpandIcon={<ArrowRightIcon />}
defaultEndIcon={<InsertDriveFile />}
sx={{
".MuiTreeItem-root": {
".Mui-focused:not(.Mui-selected)": classes.focused,
".Mui-selected, .Mui-focused.Mui-selected, .Mui-selected:hover":
classes.selected
}
}}
>
{renderTreeData(data)}
</TreeView>
</ThemeProvider>
</Box>
);
};

To render arbitrarily nested data we make use of the recursive method renderTreeData. This way the data array can be expanded endlessly as long as it matches TreeData type.

export const renderTreeData = (data: TreeData[]) => {
return data.map((item) => (
<React.Fragment key={item.id}>
{item.children ? (
<CustomTreeItem
nodeId={item.id}
label={item.name}
disabled={item.disabledButton}
icon={<FolderOutlined />}
>
{renderTreeData(item.children)}
</CustomTreeItem>
) : (
<CustomTreeItem
nodeId={item.id}
label={item.name}
disabled={item.disabledButton}
icon={getFileIcon(item.name)}
/>
)}
</React.Fragment>
));
};

Live Demo

Edit nervous-babycat-jlbqnq



Related Topics



Leave a reply



Submit