How to Set Selected and Hover Color of Listitem in Mui

How to set selected and hover color of ListItem in MUI?

Below is the portion of the default ListItem styles that deals with the background color:

export const styles = (theme) => ({
/* Styles applied to the (normally root) `component` element. May be wrapped by a `container`. */
root: {
'&$focusVisible': {
backgroundColor: theme.palette.action.selected,
},
'&$selected, &$selected:hover': {
backgroundColor: theme.palette.action.selected,
},
'&$disabled': {
opacity: 0.5,
},
},
/* Pseudo-class applied to the `component`'s `focusVisibleClassName` prop if `button={true}`. */
focusVisible: {},
/* Styles applied to the inner `component` element if `button={true}`. */
button: {
transition: theme.transitions.create('background-color', {
duration: theme.transitions.duration.shortest,
}),
'&:hover': {
textDecoration: 'none',
backgroundColor: theme.palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
/* Pseudo-class applied to the root element if `selected={true}`. */
selected: {},
});

The important thing to notice is that the selected styling is done via a combination of two classes (root and selected), so if you try to override it using a single class you will not have sufficient specificity.

Below is an example showing one way to override the selected and hover states:

import React from "react";
import { makeStyles, withStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import MuiListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import InboxIcon from "@material-ui/icons/Inbox";
import DraftsIcon from "@material-ui/icons/Drafts";

const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));

const ListItem = withStyles({
root: {
"&$selected": {
backgroundColor: "red",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
},
"&$selected:hover": {
backgroundColor: "purple",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
},
"&:hover": {
backgroundColor: "blue",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
}
},
selected: {}
})(MuiListItem);

export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(1);

const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};

return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem
button
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItem>
<ListItem
button
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItem>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItem
button
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItem>
<ListItem
button
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItem>
</List>
</div>
);
}

Edit ListItem selected and hover

Related answers:

  • How to overried the selected classes in menuItem in material ui REACTjs?
  • How to change the styles of ListItem element with the "onclick" event?

How to change background color of a selected ItemList Material-Ui

In your Higher Order Component add new property selectedItemStyle!

<ComposedComponent selectedItemStyle={selectedItemStyle} value={this.state.selectedIndex} onChange={this.handleRequestChange} containerHeight={this.props.containerHeight} elementHeight={40}>
{this.props.children}
</ComposedComponent>

where selectedItemStyle is

const slectedItemStyle = {
backgroundColor: 'red'
}

Material UI and ReactJS, how to style a selected ListItem using withStyles?

If you are using stateless functional component, you should use makeStyles() instead of withStyles().

  • withStyles() is HOC which is often used with class-based component.
  • makeStyles() on the other hand is a hook creator, and hooks are more suitable in functional components. I'd advice you and your children to use this approach since more and more libraries are adopting hooks as the primary API instead of HOC.

You can see a list of ListItem css classes and class keys here to know which class to apply to.

const useStyles = makeStyles({
root: {
"& .Mui-selected": {
backgroundColor: "pink",
color: "red",
fontWeight: "bold"
},
"& .Mui-selected:hover": {
backgroundColor: "tomato"
}
}
});

function App() {
const [selected, setSelected] = React.useState("home");
const styles = useStyles();

return (
<List className={styles.root}>
...
</List>
);
}

If you want to use class key (selected) instead of css class (Mui-selected), you can write it like this

const useStyles = makeStyles({
root: {
"&$selected": {
backgroundColor: "pink",
color: "red",
"&:hover": {
backgroundColor: "tomato"
}
}
},
selected: {}
});

And apply to the component like this

<List>
<ListItem
classes={{
root: styles.root,
selected: styles.selected
}}
...
>
...
</ListItem>
...
</List>

At this point you should consider refactor ListItem to a separate component to clean up the duplicated code.

Live Example

Edit 63973501/material-ui-and-reactjs-how-to-style-a-selected-listitem-using-withstyles

Material-UI - set selected color of ListItemSecondaryAction

Alright nevermind...

I could just have done this:

ListItemSecondarySelected: {
color: palette.primary.main,
},
<ListItem style={{ ...style }} classes={ListItemClasses} selected={selected} button>
<ListItemIcon className={selected ? classes.ListItemIconSelected : classes.ListItemIcon}>
{icon}
</ListItemIcon>
<ListItemText>{text}</ListItemText>
{canAccessSettings && (
<ListItemSecondaryAction className={selected ? classes.ListItemSecondarySelected : ''}>
<IconButton color="inherit" edge="end">
<Settings />
</IconButton>
</ListItemSecondaryAction>
)}
</ListItem>

Material UI ListItem Selected Styling in React.js

You can use focus instead of active.

listWrap: {
"&:hover": {
border: "1px solid #6c757d",
color: "black"
},
textAlign: "center",
"&:focus": {
background: "#6c757d",
color: "black"
}
}

How to change the styles of ListItem element with the onclick event?

Whenever you are trying to override Material-UI styles and it isn't working like you would expect, the best resource is the source code. Here is the URL for the ListItem source code: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/ListItem/ListItem.js. For the most part, you only need to look at the styles variable near the top of the source file.

Below I've copied all of the portions of the styles variable that deal with backgroundColor and textDecoration:

export const styles = theme => ({
/* Styles applied to the (normally root) `component` element. May be wrapped by a `container`. */
root: {
textDecoration: 'none',
'&$selected, &$selected:hover, &$selected:focus': {
backgroundColor: theme.palette.action.selected,
},
},
/* Styles applied to the inner `component` element if `button={true}`. */
button: {
transition: theme.transitions.create('background-color', {
duration: theme.transitions.duration.shortest,
}),
'&:hover': {
textDecoration: 'none',
backgroundColor: theme.palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
'&:focus': {
backgroundColor: theme.palette.action.hover,
},
},
/* Styles applied to the root element if `selected={true}`. */
selected: {},
});

The main styles causing difficulty are the button hover and focus styles. In order to override these successfully without resorting to "!important", you need to have the appropriate CSS specificity.

The following seems to do the trick:

  listItemDone: {
borderRadius: "1em",
"&,&:focus,&:hover": {
backgroundColor: "#F6F6E8",
textDecoration: "line-through"
}
},

However, the above prevents there from being any hover effect on "done" items, so you may instead want to do something more like:

  listItemDone: {
borderRadius: "1em",
"&,&:focus": {
backgroundColor: "#F6F6E8",
textDecoration: "line-through"
},
"&:hover": {
textDecoration: "line-through"
}
},

This allows the hover background color of done items to still be theme.palette.action.hover. If you want the hover color to be different for done items, you can specify it explicitly along with the textDecoration.

There is one other detail to take care of. If you click a list item to put it in a "done" state and then click it again, it will not be in the "done" state anymore but it will have the button focus styles applied. In order to remove that focus styling, you also need the following:

  listItem: {
borderRadius: "1em",
"&,&:focus": {
backgroundColor: theme.palette.background.paper // or whatever color you want this to be
}
},

Here is my modified version of your sandbox:

Edit 5kv3j6r1xn

How can I override styling for ListItemButton when it's selected?

Working examples on Code Sandbox

I have made a similar example using Code Sandbox, which you can find here https://codesandbox.io/s/amazing-gagarin-gvl66n. I show a working implementation using:

  • The css prop
  • The sx prop

Using the css prop

There's two things you need to do to make your code work using Emotion's css prop.

  1. Add the following lines, which is also in the example, at the top of the file where you are using the css prop. This will tell your app how to handle the css prop.
/* eslint-disable react/react-in-jsx-scope -- Unaware of jsxImportSource */
/** @jsxImportSource @emotion/react */

  1. Target the classes Material UI provides for the List Item Button component. For example, if I want to style the List Item Button when selected is true, I would target the .Mui-selected class.

I am assuming you wanted to style the background color of the List Item Button rather than the color. Styling the color changes the font color. However, if you wanted to change the font color, you can just change each instance of background-color to color.

Putting it altogether:

/* eslint-disable react/react-in-jsx-scope -- Unaware of jsxImportSource */
/** @jsxImportSource @emotion/react */
import * as React from "react";
import Avatar from "@mui/material/Avatar";
import ListItemText from "@mui/material/ListItemText";
import ListItemButton from "@mui/material/ListItemButton";
import { useState } from "react";
import { css } from "@emotion/react";

const ProfileInfo = ({ userCredentials, userPicture }) => {
const [selected, setSelected] = useState(false);

return (
<ListItemButton
selected={selected}
onClick={() => setSelected((prev) => !prev)}
css={css`
&.Mui-selected {
background-color: #2e8b57;
}
&.Mui-focusVisible {
background-color: #2e8b57;
}
:hover {
background-color: #2e8b57;
}
`}
>
<Avatar
alt={userCredentials}
src={userPicture}
sx={{ width: 24, height: 24 }}
/>
<ListItemText primary={userCredentials} sx={{ marginLeft: 3 }} />
</ListItemButton>
);
};

export default ProfileInfo;

Alternative: Using the SX prop

The sx prop can be used to override styles with all Material UI components. You are already using this for the Avatar and ListItemText components in your example.

Using the sx prop, the equivalent code would be:

import * as React from "react";
import Avatar from "@mui/material/Avatar";
import ListItemText from "@mui/material/ListItemText";
import ListItemButton from "@mui/material/ListItemButton";
import { useState } from "react";

const ProfileInfo = ({ userCredentials, userPicture }) => {
const [selected, setSelected] = useState(false);

return (
<ListItemButton
selected={selected}
onClick={() => setSelected((prev) => !prev)}
sx={{
"&.Mui-selected": {
backgroundColor: "#2e8b57"
},
"&.Mui-focusVisible": {
backgroundColor: "#2e8b57"
},
":hover": {
backgroundColor: "#2e8b57"
}
}}
>
<Avatar
alt={userCredentials}
src={userPicture}
sx={{ width: 24, height: 24 }}
/>
<ListItemText primary={userCredentials} sx={{ marginLeft: 3 }} />
</ListItemButton>
);
};

export default ProfileInfo;


Related Topics



Leave a reply



Submit