Material UI Toggle Button Color - reactjs

I am using the ToggleButton Group and ToggleButton components, but I cannot get the font color to be different than the background color. Right now I'm using a black body background and want the clicked/active button to have a white background with black text. I'm using themes to have a toggle. My buttons are setup like so:
import { ToggleButtonGroup, ToggleButton } from '#mui/material'
const Header = ({onScenario1Change, onScenario2Change, onScenario3Change }) => {
const [alignment, setAlignment] = useState('left')
const handleAlignment = (e, newAlignment) => {
setAlignment(newAlignment)
}
return (
<ToggleButtonGroup
value={alignment}
exclusive
onChange={handleAlignment}
variant="outlined"
color="primary"
>
<ToggleButton color="primary" value="left" onClick={onScenario1Change}>Scenario 1</ToggleButton>
<ToggleButton color="primary" value="center" onClick={onScenario2Change}>Scenario 2</ToggleButton>
<ToggleButton color="primary" value="right" onClick={onScenario3Change}>Scenario 3</ToggleButton>
</ToggleButtonGroup>
)
}
And my theme is setup like so:
import { createTheme } from "#mui/material/styles";
export const darkTheme = createTheme({
palette: {
primary: {
main: '#FeFeFe',
contrastText: '#000000'
},
background: {
default: '#000000',
},
divider: '#fefefe',
},
components: {
MuiToggleButton: {
"&.Mui-selected": {
color: "#000000",
backgroundColor: '#fefefe'
}
}
}
})
Ready to never use MUI again after this. Any help in figuring out this override would be greatly appreciated!!

Ok so I got it working after a few refactors.
Buttons had to have text color based on theme
Buttons also had to have the color={primary} prop removed
Theme had to include action: selectedOpacity, as well as root specified before Mui-selected.
&:hover also had to be added to make the hover effect match the selected look.
import { ToggleButtonGroup, ToggleButton, styled } from '#mui/material'
const Header = ({onScenario1Change, onScenario2Change, onScenario3Change, theme }) => {
const [alignment, setAlignment] = useState('left')
const handleAlignment = (e, newAlignment) => {
setAlignment(newAlignment)
}
let fill = theme ? '#000000' : "#fefefe"
const CustomToggle = styled(ToggleButton)({
color: fill
})
return (
<ToggleButtonGroup
value={alignment}
exclusive
onChange={handleAlignment}
variant="outlined"
color="primary"
>
<CustomToggle value="left" onClick={onScenario1Change}>Scenario 1</CustomToggle>
<CustomToggle value="center" onClick={onScenario2Change}>Scenario 2</CustomToggle>
<CustomToggle value="right" onClick={onScenario3Change}>Scenario 3</CustomToggle>
</ToggleButtonGroup>
)
}
export const darkTheme = createTheme({
palette: {
primary: {
main: '#FeFeFe',
contrastText: '#000000'
},
background: {
default: '#000000',
},
divider: '#fefefe',
action: {
selectedOpacity: .95
}
},
components: {
MuiToggleButton: {
styleOverrides: {
root: {
"&.Mui-selected": {
color: "#000000",
backgroundColor: '#fefefe'
},
"&:hover": {
color: '#000000',
backgroundColor: '#fefefe'
}
}
}
}
}
})
Hope this helps someone in the future!

Related

How to customize the border for MUI components

How can I change the border properties for an MUI Select component using ThemeProvider? I'm able to change the backgroundColor, but for some reason border properties like border and borderColor do not work.
App.js
import React from "react";
import {
ThemeProvider,
Box,
FormControl,
InputLabel,
MenuItem,
Select,
} from "#mui/material";
import theme from "./theme";
function App() {
return (
<ThemeProvider theme={theme}>
<Box sx={{ minWidth: 200, margin: "5%" }}>
<FormControl>
<InputLabel id="demo-simple-select-label">Select Label</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
label="Select Label"
variant="outlined"
sx={{ minWidth: 200 }}
>
<MenuItem value={"1"}>Item 1</MenuItem>
<MenuItem value={"2"}>Item 2</MenuItem>
<MenuItem value={"3"}>Item 3</MenuItem>
</Select>
</FormControl>
</Box>
</ThemeProvider>
);
}
export default App;
theme.js
import { createTheme } from "#mui/material";
const darkGrey = "#262626";
const mediumGrey = "#595959";
const lightGrey = "#adadad";
const white = "#ffffff";
const limeGreen = "3df730";
const theme = createTheme({
components: {
MuiSelect: {
styleOverrides: {
root: {
backgroundColor: mediumGrey,
border: "4px",
borderColor: "red",
},
},
},
// This doesn't work either
// MuiOutlinedInput: {
// styleOverrides: {
// root: {
// border: "4px",
// borderColor: limeGreen,
// },
// },
// },
MuiInputLabel: {
styleOverrides: {
root: {
color: white,
},
},
},
},
});
export default theme;
There are three main issues here:
First, the styleOverrides of the Select component do not have any root property (see https://mui.com/material-ui/api/select/#css). You can use select instead.
Second, your border doesn't have any specified thickness so it won't be visible.
Third, your limeGreen color doesn't start with a # so it won't be recognized as a hex color.
Here is a fixed version of the styling file:
import { createTheme } from '#mui/material';
const darkGrey = '#262626';
const mediumGrey = '#595959';
const lightGrey = '#adadad';
const white = '#ffffff';
const limeGreen = '#3df730';
const theme = createTheme({
components: {
MuiSelect: {
styleOverrides: {
select: {
backgroundColor: mediumGrey,
border: 'solid 3px',
borderColor: limeGreen,
},
},
},
// This doesn't work either
// MuiOutlinedInput: {
// styleOverrides: {
// root: {
// border: "4px",
// borderColor: limeGreen,
// },
// },
// },
MuiInputLabel: {
styleOverrides: {
root: {
color: white,
},
},
},
},
});
export default theme;

What's the right way to override colors with MUI withStyle and TypeScript and next.js?

I have:
const Button: React.FC<ButtonProps> = ({
label,
size,
variant = 'primary',
disabled = false,
}: ButtonProps) => {
const classes = useStyles();
return (
<MaterialButton
className={`${classes.buttonBase} size-${size}`}
disabled={disabled}
>
{label}
</MaterialButton>
);
};
and
const useStyles = makeStyles(() => ({
buttonBase: {
background:'#000000',
color: '#ffffff',
However, if variant is secondary, I want to basically swap the background and color. How do I go about doing this?
You can set it in your theme.
The example below is for using the variant="contained" with color="secondary"
export const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
containedSecondary: {
backgroundColor: '#808080',
color: '#FFFFFF',
'&:hover': {
backgroundColor: '#565656',
},
},
}
}
}
<Button variant="contained" color="secondary">Test button</Button>
Or if you want to set the style in your component you can set up a variant for the secondary like this. Here is a working codesandbox
const useStyles = makeStyles({
primary: {
backgroundColor: "purple",
color: "#FFF"
},
secondary: {
backgroundColor: "red",
color: "#FFF"
}
});
<Button className={classes.secondary}>Secondary</Button>
<Button className={classes.primary}>Primary</Button>

How to keep the background color of disabled Button in Material-UI?

I want to change the global style of the disabled Button component from Material-UI. But the problem is I am unable to keep the original color scheme of the button.
Consider this button:
<Button color="secondary" disabled={isLoading}>Create Account</Button>
Now by default Mui-disabled would be attached to this button. Whose color and the background-color are taken from theme.palatte.action property. So this disabled button would be having CSS as:
color: rgba(0,0,0,0.26);
box-shadow: none;
background-color: rgba(0,0,0,0.12);
But I want my disabled button to maintain its original color ("primary, secondary", "error" etc.) added with an opacity of 0.7. By default, the cursor events are set to none by MUI.
I tried it using the custom theme but I don't know how to maintain the original color of the button. E.g if the Button is primary keep the primary color, if the Button is secondary keep the secondary colors.
MuiButton: {
styleOverrides: {
root: {
textTransform: "none",
boxShadow: "none",
"&.Mui-disabled": {
// background: "initial",
// color: "initial",
opacity: .7
}
},
}
}
Of course, I don't want to write a custom code for each code. I can do this even by creating a wrapper around the MUI's button and use that wrapper everywhere in my code.
But I want to do it the MUI way by overriding the theme.
How can I implement a global solution?
Is this what you want? just don't set the disabledBackground color in the palette:
import Button, { buttonClasses } from "#mui/material/Button";
import { createTheme, ThemeProvider } from "#mui/material/styles";
const defaultTheme = createTheme();
const theme = createTheme({
palette: {
action: {
disabledBackground: "", // don't set the disable background color
disabled: "white", // set the disable foreground color
}
},
components: {
MuiButtonBase: {
styleOverrides: {
root: {
[`&.${buttonClasses.disabled}`]: {
opacity: 0.5
},
// Fix ButtonGroup disabled styles.
[`&.${toggleButtonClasses.root}.${buttonClasses.disabled}`]: {
color: defaultTheme.palette.action.disabled,
borderColor: defaultTheme.palette.action.disabledBackground
}
}
}
}
}
);
At the current time I'm writing. There is also other components that reuses the disableBackground property, make sure you take a look and are okay with that too. I display them all in the example below.
Live Demo
With the way that the disabled state is handled in the default styles, I think you need to redefine the default colors within your override in order for it to work.
In the source code you can find how the default colors are defined for the text, outlined, and contained variants.
The example below demonstrates overriding the disabled styles for all three variants with opacity: 0.7 by redefining the non-disabled colors within the "&.Mui-disabled" style overrides.
import * as React from "react";
import Stack from "#mui/material/Stack";
import Button from "#mui/material/Button";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import { alpha } from "#mui/system";
const defaultTheme = createTheme();
const colors = [
"inherit",
"primary",
"secondary",
"success",
"error",
"info",
"warning"
];
const containedColorStyles = {};
const textColorStyles = {};
const outlinedColorStyles = {};
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
colors.forEach((color) => {
containedColorStyles[`&.MuiButton-contained${capitalizeFirstLetter(color)}`] =
color === "inherit"
? {
backgroundColor: defaultTheme.palette.grey[300],
color: defaultTheme.palette.getContrastText(
defaultTheme.palette.grey[300]
)
}
: {
backgroundColor: defaultTheme.palette[color].main,
color: defaultTheme.palette[color].contrastText
};
textColorStyles[`&.MuiButton-text${capitalizeFirstLetter(color)}`] =
color === "inherit"
? {
color: "inherit"
}
: {
color: defaultTheme.palette[color].main
};
outlinedColorStyles[`&.MuiButton-outlined${capitalizeFirstLetter(color)}`] =
color === "inherit"
? {
color: "inherit",
borderColor:
defaultTheme.palette.mode === "light"
? "rgba(0, 0, 0, 0.23)"
: "rgba(255, 255, 255, 0.23)"
}
: {
color: defaultTheme.palette[color].main,
borderColor: `${alpha(defaultTheme.palette[color].main, 0.5)}`
};
});
const theme = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: "none",
boxShadow: "none",
"&.Mui-disabled": {
...containedColorStyles,
...textColorStyles,
...outlinedColorStyles,
opacity: 0.7
}
}
}
}
}
});
export default function ColorButtons() {
const variants = ["text", "outlined", "contained"];
return (
<ThemeProvider theme={theme}>
{variants.map((variant) => (
<div key={variant}>
{capitalizeFirstLetter(variant)} Variant
<Stack direction="row" spacing={2}>
{colors.map((color) => (
<Button key={color} variant={variant} color={color}>
{capitalizeFirstLetter(color)}
</Button>
))}
</Stack>
Disabled {capitalizeFirstLetter(variant)} Variant
<Stack direction="row" spacing={2} sx={{ mb: 2 }}>
{colors.map((color) => (
<Button key={color} disabled variant={variant} color={color}>
{capitalizeFirstLetter(color)}
</Button>
))}
</Stack>
</div>
))}
</ThemeProvider>
);
}
I have a nice solution for now.
I use the ownerState props to get the color props. Next, I use it in the theme's palette. The following example illustrates how to do this for a contained button.
let theme = createTheme({
palette: {
// Your custom palette
},
});
theme = createTheme(theme, {
components: {
MuiButton: {
defaultProps: {
disableElevation: true
},
styleOverrides: {
root: ({ ownerState }) => ({
'&.MuiButton-contained.Mui-disabled': {
backgroundColor: theme.palette[ownerState.color].main,
}
}),
}
}
}
});

How do you give material-ui drawer component a switchable theme?

I am making a react project but I have a problem getting my material-ui drawer to match with my dark/light switchable theme. I currently have given the drawer styling using makeStyles, but I can't for the life of me get it sync with ThemeProvider.
I have tried doing it all in ThemeProvider but the drawer required other methods. Then I tried doing it all with makeStyles and not using ThemeProvider at all but that didn't work either. I also tried putting a conditional ternary statement in makeStyles for this, but that didn't work.
Any help or advice that leads me in the right direction would be greatly appreciated!
function App() {
const dark_theme = createMuiTheme({
palette: {
primary: {
main: '#e78f23',
secondary: {
main: '#463d3d',
}
},
background: {
default: "#222222",
},
text: {
primary: "#ffffff"
},
}
})
const light_theme = createMuiTheme({
palete: {
primary: {
main: '#4892BC',
},
secondary: {
main: '#EEF3FF',
},
background: {
default: "#e4f0e2"
},
text: {
primary: "#000000"
},
}
});
const useStyles = makeStyles((theme) => ({
drawer: {
background : '#e78f23',
},
switchTrack: {
backgroundColor: "#000"
},
switchBase: {
color: "#000",
"&.Mui-checked": {
color: "white"
},
"&.Mui-checked + .MuiSwitch-track": {
backgroundColor: "white"
},
},
}));
const classes = useStyles()
const [light, setLight] = useState(true);
return (
<ThemeProvider theme={light ? light_theme : dark_theme}>
<CssBaseline />
<Router>
<main>
<Drawer id="Drawer" PaperProps={{className:classes.drawer}} open={drawerOpen} onClose={CloseDrawer}>
<List id="ListID">
<Button>
<ListItem button onClick={() => setLight(prev => !prev)} color="primary" class="themeButton">
<FormControl>
<FormLabel>Dark/Light</FormLabel>
<FormGroup>
<FormControlLabel
id="formControlLabel"
control = {
<Switch2
color="default"
classes={{
track: classes.switchTrack,
switchBase: classes.switchBase,
}}
size="medium"
position="center"
checked={checked}
onChange={toggleChecked}
labelPlacement="end"
/>
}
/>
</FormGroup>
</FormControl>
</ListItem>
</Button>

How to eliminate hover, active, focus gray color in Material UI Chip component

I have Chips implemented in several colors (green, yellow, blue etc.) and by default MUI Chip comes with grey hover/active/focus CSS style. I need to eliminate this hover/active/focus grey background color in MUI Chip component. So once again I don't want to replace gray color with another color but to completely eliminate following CSS styles:
clickable: {
// Remove grey highlight
WebkitTapHighlightColor: theme.palette.common.transparent,
cursor: 'pointer',
'&:hover, &:focus': {
backgroundColor: emphasize(backgroundColor, 0.08),
},
'&:active': {
boxShadow: theme.shadows[1],
backgroundColor: emphasize(backgroundColor, 0.12),
},
},
deletable: {
'&:focus': {
backgroundColor: emphasize(backgroundColor, 0.08),
},
},
In the end this can be done by overriding Chip component in all the colors needed, but there has to be a better way.
You can create a factory function that returns a component with the color of your choice and overrides the behavior highlighted in your question:
import React from 'react';
import { withStyles } from 'material-ui/styles';
import Chip from 'material-ui/Chip';
import { emphasize, fade } from 'material-ui/styles/colorManipulator';
const ChipFactory = (color = null, deleteIconColor = null) => {
const styles = theme => {
const backgroundColor = emphasize(color || theme.palette.background.default, 0.12);
const deleteIconColor = fade(deleteIconColor || theme.palette.text.primary, 0.26);
return {
root: {
color: theme.palette.getContrastText(backgroundColor),
backgroundColor,
},
clickable: {
cursor: 'pointer',
'&:hover, &:focus': {
backgroundColor: emphasize(backgroundColor, 0.08),
},
'&:active': {
backgroundColor: emphasize(backgroundColor, 0.12),
},
},
deletable: {
'&:focus': {
backgroundColor: emphasize(backgroundColor, 0.08),
},
},
deleteIcon: {
color: deleteIconColor,
'&:hover': {
color: fade(deleteIconColor, 0.4),
},
},
};
};
const CustomChip = ({ classes, ...props }) =>
<Chip classes={classes} {...props} />;
return withStyles(styles)(CustomChip);
};
export default ChipFactory;
Rather than creating individual components for each color, you can generate a new variety on the fly by invoking this function:
// excerpt from Chips demo
render() {
const { classes } = props;
const GreenChip = ChipFactory('#0f0');
const RedChip = ChipFactory('#f00');
const BlueChip = ChipFactory('#00f');
return (
<div className={classes.row}>
<GreenChip label="Basic Chip" className={classes.chip} />
<RedChip
avatar={<Avatar>MB</Avatar>}
label="Clickable Chip"
onClick={handleClick}
className={classes.chip}
/>
<BlueChip
avatar={<Avatar src="/static/images/uxceo-128.jpg" />}
label="Deletable Chip"
onRequestDelete={handleRequestDelete}
className={classes.chip}
/>
</div>
);
}
See this code sandbox for a working version.

Resources