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>
);
}
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
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:
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.
- 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 thecss
prop.
/* eslint-disable react/react-in-jsx-scope -- Unaware of jsxImportSource */
/** @jsxImportSource @emotion/react */
- 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
istrue
, 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
Disable/Turn Off Inherited CSS3 Transitions
How to Use a Stylus Variable in Calc
How to Visually Indicate Current Page in ASP.NET MVC
Scale and Reposition Iframe Like Background-Size: Cover
Text-Overflow:Ellipsis on a Link
Bootstrap Scrollable Tab Panel Content
Add Dropshadow Only to Border of Grid Pane Javafx 2.2
Node + React - Hyphenated CSS Class Names
CSS Transition When Class Removed
Twitter Bootstrap Responsive Carousel with Multiple Items
Bootstrap 3 Apply CSS on Mobile View Only