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

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>

Related

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>

Changing theme type with MUI createTheme, palette type

In createTheme function on MUI, in the palette section there is a "type" option.
I have created two different palettes with type options dark and light as you can see :
import { createTheme } from "#mui/material/styles";
const theme = createTheme({
palette: {
type: "light",
primary: {
main: "#022B3A",
dark: "#030316",
},
secondary: {
main: "#1F7A8C",
},
background: {
default: "#232121",
paper: "#544f4f",
},
text: {
primary: "#ffffff",
},
divider: "rgba(49,44,44,0.12)",
},
palette: {
type: "dark",
primary: {
main: "#022B3A",
dark: "#030316",
},
secondary: {
main: "#1F7A8C",
},
background: {
default: "#232121",
paper: "#544f4f",
},
text: {
primary: "#ffffff",
},
divider: "rgba(49,44,44,0.12)",
typography: {
fontFamily: ["IBM Plex Sans", "sans-serif"].join(","),
},
},
});
With this approach, is it possible to create a button that changes the palette type?
Or should I create different themes and switch between them with a global state?
Here is a live demo showing how you can change the theme from light to dark with a button:
Code:
import * as React from 'react';
import IconButton from '#mui/material/IconButton';
import Box from '#mui/material/Box';
import { useTheme, ThemeProvider, createTheme } from '#mui/material/styles';
import Brightness4Icon from '#mui/icons-material/Brightness4';
import Brightness7Icon from '#mui/icons-material/Brightness7';
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
function MyApp() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<Box
sx={{
display: 'flex',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.default',
color: 'text.primary',
borderRadius: 1,
p: 3,
}}
>
{theme.palette.mode} mode
<IconButton sx={{ ml: 1 }} onClick={colorMode.toggleColorMode} color="inherit">
{theme.palette.mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
</IconButton>
</Box>
);
}
export default function ToggleColorMode() {
const [mode, setMode] = React.useState('light');
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[],
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode],
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<MyApp />
</ThemeProvider>
</ColorModeContext.Provider>
);
}

Overriding MUI Stepper styles in React 2022

I've been struggling with the proper way to override styles for a React MUI Stepper component in 2022. More specifically in the active, and completed states for the label as well as the circle icon.
My Code
import { ThemeProvider } from '#mui/styles';
import { createTheme } from '#mui/system';
const theme = createTheme({
overrides: {
MuiStepIcon: {
root: {
'&$completed': {
color: 'pink',
},
'&$active': {
color: 'red',
},
},
active: {},
completed: {},
},
MuiStepLabel: {
root: {
color: 'red'
}
}
}
})
<ThemeProvider theme={theme}>
<Stepper activeStep={activeStep} className='serviceReferralWizardStepper'>
{steps.map((label) => {
const stepProps = {};
const labelProps = {};
return (
<Step className='stepper-holder' style={{ margin: '0 1.8rem' }} key={label} {...stepProps}>
<StepLabel {...labelProps}><span className='serviceReferralWizardStepper__label'>{label}</span></StepLabel>
</Step>
);
})}
</Stepper>
Currently I see no styles at all applied. Any advice would be apprecieted.
I recommend looking at MUI's documentation example: https://v4.mui.com/components/steppers/#CustomizedSteppers.js
You will need to create custom components to represent the styles you want.
const CustomeConnector = withStyles({
active: {
'& $line': {
borderColor: '#784af4',
},
},
completed: {
'& $line': {
borderColor: '#784af4',
},
},
})(StepConnector);
function CustomeStepIcon(props) {
const classes = // create styles
const { active, completed } = props;
return (
<div
className={clsx(classes.root, {
[classes.active]: active,
})}
>
{completed ? <Check className={classes.completed} /> : <div className={classes.circle} />}
</div>
);
}
<Stepper alternativeLabel activeStep={activeStep} connector={<CustomConnector />}>
{steps.map((label) => (
<Step key={label}>
<StepLabel StepIconComponent={CustomStepIcon}>{label}</StepLabel>
</Step>
))}
</Stepper>
UPDATE
Here is an updated link to the v5 implementation: https://mui.com/components/steppers/#customized-horizontal-stepper

Dark Mode in react using MUI v5

Trying to create a Toggle to switch from dark mode to light mode has been quite difficult for me in v5.
Using the code directly from MUI Sandbox MUI darkmode, I attempted to separate the code to work in the app.js and my Navbarpractice.js.
App.js
import React from "react";
import useMediaQuery from '#mui/material/useMediaQuery';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { Paper } from "#mui/material";
import BasicCard from "./components /Card.js";
import Navbarpractice from "./components /Navbar/Navbarpractice"
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
function App() {
const [mode, setMode] = React.useState('light');
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[],
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode],
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<div className="App">
<Navbarpractice/>
<BasicCard/>
</div>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default App;
Navbarpractice.js
import React from 'react';
import AppBar from '#mui/material/AppBar';
import Box from '#mui/material/Box';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import Button from '#mui/material/Button';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
import { useTheme, ThemeProvider, createTheme } from '#mui/material/styles';
import { teal } from '#mui/material/colors';
import { withStyles } from '#mui/styles';
import { Switch } from '#mui/material';
import Brightness4Icon from '#mui/icons-material/Brightness4';
import Brightness7Icon from '#mui/icons-material/Brightness7';
const label = { inputProps: { 'aria-label': 'Switch' } };
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
const theme = createTheme({
Navbar: {
primary: {
// Purple and green play nicely together.
main: teal[500],
},
secondary: {
// This is green.A700 as hex.
main: '#11cb5f',
},
},
});
const TealTextTypography = withStyles({
root: {
color: "#008080"
}
})(Typography);
function Navbar() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>\
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static"
style={{ background: 'transparent', boxShadow: 'none'}}>
<Toolbar>
<IconButton
size="large"
edge="start"
aria-label="menu"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Mentors
</TealTextTypography>
<TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Mentees
</TealTextTypography>
<Box
sx={{
display: 'flex',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.default',
color: 'text.primary',
borderRadius: 1,
p: 3,
}}
>
<IconButton sx={{ ml: 1 }} onClick={colorMode.toggleColorMode} color="inherit">
{ theme.palette.mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
</IconButton>
</Box>
</Toolbar>
</AppBar>
</Box>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default Navbar;
I am certain I am mixing up my const. and placing them in the wrong places. Although I am new to react and Mui I did manage to get it to work statically, however, the toggle is proving to be difficult.
This seem to work for me
App.js
import React from 'react';
import {
ThemeProvider,
createTheme,
responsiveFontSizes,
} from '#mui/material/styles';
import { deepmerge } from '#mui/utils';
import useMediaQuery from '#mui/material/useMediaQuery';
import { getDesignTokens, getThemedComponents } from 'theme/Theme';
import { ColorModeContext } from 'config/color-context';
export default function App() {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [mode, setMode] = React.useState();
React.useEffect(() => {
setMode(prefersDarkMode ? 'dark' : 'light');
}, [prefersDarkMode]);
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[]
);
let theme = React.useMemo(
() =>
createTheme(deepmerge(getDesignTokens(mode), getThemedComponents(mode))),
[mode]
);
theme = responsiveFontSizes(theme);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
...
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Theme.js
import { amber, deepOrange, grey, blue, common } from '#mui/material/colors';
const palette = {
light: {
primary: {
main: '#34C0AC',
light: '#B1DED3',
dark: '#00765A',
},
},
};
export const getDesignTokens = (mode) => ({
palette: {
mode,
...(mode === 'light'
? {
primary: {
main: palette.light.primary.main,
light: palette.light.primary.light,
dark: palette.light.primary.dark,
},
divider: amber[200],
text: {
primary: grey[900],
secondary: grey[800],
},
}
: {
primary: deepOrange,
divider: deepOrange[700],
background: {
default: deepOrange[900],
paper: deepOrange[900],
},
text: {
primary: '#fff',
secondary: grey[500],
},
}),
},
typography: {
fontFamily: [
'Oswald',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
].join(','),
body1: {
fontFamily: 'Poppins, Arial, sans-serif',
},
},
});
export const getThemedComponents = (mode) => ({
components: {
...(mode === 'light'
? {
MuiAppBar: {
styleOverrides: {
colorPrimary: {
backgroundColor: grey[800],
},
},
},
MuiLink: {
variant: 'h3',
},
MuiButton: {
styleOverrides: {
root: {
borderRadius: 0,
color: common.white,
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
fontSize: 20,
borderWidth: 2,
'&:hover': {
borderWidth: 2,
},
},
},
variants: [
{
props: { variant: 'contained' },
style: {
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
{
props: { variant: 'outlined' },
style: {
color: palette.light.primary.main,
},
},
{
props: { variant: 'primary', color: 'primary' },
style: {
border: '4px dashed blue',
},
},
],
},
MuiList: {
styleOverrides: {
root: {},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
color: common.white,
alignItems: 'stretch',
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
},
MuiAccordion: {
styleOverrides: {
root: {
color: common.white,
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
},
}
: {
MuiAppBar: {
styleOverrides: {
colorPrimary: {
backgroundColor: blue[800],
},
},
},
}),
},
});
color-context.js
import React from 'react';
export const ColorModeContext = React.createContext({
toggleColorMode: () => {
// This is intentional
},
});
ThemeToggler.js
import React from 'react';
import { IconButton, Box } from '#mui/material';
import { useTheme } from '#mui/material/styles';
import Brightness4Icon from '#mui/icons-material/Brightness4';
import Brightness7Icon from '#mui/icons-material/Brightness7';
import { ColorModeContext } from 'config/color-context';
export default function SubHeaderNavigation() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<Box
sx={{
display: 'flex',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.default',
color: 'text.primary',
borderRadius: 1,
p: 3,
}}
>
{theme.palette.mode} mode
<IconButton
sx={{ ml: 1 }}
onClick={colorMode.toggleColorMode}
color="inherit"
>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Box>
);
}
Make sure you are not nesting your Themes or both could be negated.
For example:
App.tsx
<>
<ThemeProvider theme={!isDark ? lightTheme : darkTheme}>
<CssBaseline />
<Dashboard />
</ThemeProvider>
</>
Dashboard.tsx taken from here
function Dashboard() {
...
return (
<ThemeProvider theme={mdTheme}>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
...
</ThemeProvider>
);
}
export default function Dashboard() {
return <DashboardContent />;
}
The effect of this will negate the Themes in the App.tsx config.

Material UI Toggle Button Color

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!

Resources