Material-UI Theme Override Issue - reactjs

I am overriding the <ExpansionPanelSummary/> component in Material UI to have less margin. I am using a theme with overrides to do this.
const theme = createMuiTheme({
overrides: {
MuiExpansionPanelSummary: {
expanded: {
marginTop: 5,
marginBottom: 5,
},
content: {
marginTop: 5,
marginBottom: 5,
},
}
},
The problem I run into however is that in the Material-UI built in css, there are two classes being applied at the same time: content and expanded.
.MuiExpansionPanelSummary-content-567.MuiExpansionPanelSummary-expanded-564 {
margin: 20px 0;
}
How can I override multiple applied classes? Is it possible to create a theme rule for this?

Got this working today. The margin you want is on the expanded class on content, so the rules need to look like this to get higher CSS specificity. Look for '&.expanded'.
const useStyles = makeStyles(theme => ({
expansionPanelSummaryContent: {
backgroundColor: 'red',
'&.expanded': {
margin: 0,
backgroundColor: 'yellow',
},
},
}));
export default function MyComponent(props) {
const classes = useStyles();
return (
<ExpansionPanel expanded={props.expanded}>
<ExpansionPanelSummary
classes={{
content: classes.expansionPanelSummaryContent,
expanded: 'expanded'
}}
>
...
</ExpansionPanelSummary>
</ExpansionPanel>
);
}

Related

Chakra UI __css on sub-component isn't being applied when styling multipart components

I am using Typescript, React(create-react-app) and Chakra.
As in the title, css on <DropdownItem> component isn't being applied.
Css on <Dropdown> works normally.
I have this code basically copied from the styling multipart components and when comparing it to the List component from the Chakra source code, it looks okay to me. But it doesn't work for some reason.
There is no errors and when console logging the CSSDict from useStyles() and useMultiStyleConfig() it logs the styles normally. I think it must be breaking at __css={styles.item}.
Thank you in advance.
Select.tsx
<Dropdown onClick={() => setOpen(!open)}>
<DropdownItem>{currItem}</DropdownItem>
</Dropdown>
Dropdown.tsx
const [StylesProvider, useStyles] = createStylesContext("Dropdown");
export function Dropdown(props: any): any {
const { size, variant, children, ...rest } = props;
const styles = useMultiStyleConfig("Dropdown", { size, variant });
return (
<List __css={styles.dropdown} {...rest}>
<StylesProvider value={styles}>{children}</StylesProvider>
</List>
);
}
export function DropdownItem(props: any): any {
const styles = useStyles();
return <ListItem __css={styles.item} {...props} />;
}
theme.ts
export const theme = extendTheme({
components: {
Button,
Dropdown,
},
colors: {
main: {
independence: "#3D405B",
purplenavy: "#51557A",
darkbluegray: "#656A99",
terracotta: "#E07A5F",
eggshell: "#F4F1DE",
},
},
styles: {
global: () => ({
body: {
bg: "main.independence",
height: "100%",
},
}),
},
shadows: {
terra: "2px -2px #E07A5F",
eggshell: "2px -2px #F4F1DE",
},
});
database.style.ts
export const Dropdown: ComponentStyleConfig = {
parts: ["dropdown", "item"],
// The base styles for each part
baseStyle: {
dropdown: {
position: "relative",
display: "inline-block",
width: "100px",
cursor: "pointer",
fontSize: "calc(35px, 0.87vw)",
h: "100%",
zIndex: "10",
},
item: {
h: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
_hover: { bg: "main.violetLt", borderRadius: "15px 0 0 15px" },
fontWeight: "600",
bg: "orange",
},
},
// The size styles for each part
sizes: {},
// The variant styles for each part
variants: {},
// The default `size` or `variant` values
defaultProps: {},
};
Edit 1
Looking at it from React Devtools, <ListElement> is the last component that gets the correct __css prop, which then disappears in <li> child.
Edit 2
export function DropdownItem(props: any): any {
const styles = useStyles();
return <chakra.li __css={styles.item} {...props} />;
}
Using <chakra.li> instead of <ListElement> somehow solves the problem and the css is being passed and applied but I still don't understand why.

Issue trying to override mui theme in Reactjs

I have a navbar with some list items and I want to override the style of MuiListItemText when MuiListItemButton is selected. I can override the hightlight but can't do it with the text or the icon. I can override the ListItemText but all of them, I just want to override if 'selected' (Mui-selected).
I'm using Mui 5.2.0.
This is my approach.
const Theme = createTheme({
components: {
MuiListItemButton: {
styleOverrides: {
root: {
borderRadius: '4px',
padding: '12px',
'&.Mui-selected': {
backgroundColor: '#F2F2EA',
MuiListItemText: {
styleOverrides: {
secondary: {
color: 'red',
...
You should define the overriding theme structure in the same depth.
const Theme = createTheme({
components: {
MuiListItemButton: {
styleOverrides: {
root: {...}
}
},
// it should be another component, not a nested structure
MuiListItemText: {
styleOverrides: {
secondary: {
...
And you can customize an individual component by using sx prop, styled-components, or makeStyles API.
https://mui.com/customization/how-to-customize/
Here's an example using styled-components.
const CustomListItemButton = styled(ListItemButton)(() => ({
borderRadius: '4px',
padding: '12px',
'&.Mui-selected': {
backgroundColor: '#F2F2EA',
...
}));
Try this:
import { createMuiTheme, ThemeProvider } from '#material-ui/core/styles';
I don't think it's the best approach but I was able to do it with something like this:
<ListItemText
secondary={
<Typography
variant="body2"
style={selected === '/' ? { color: 'red' } : { color 'green' }>
Item Text</Typography> } />

How do I extend Material-UI Button and create my own button component

I am a React newbie. Using Material-UI / React, how can I create my own Button component? Ideally, I would like the component to encapsulate certain properties.
Here is my code.
export const Button = withSytles((theme) => ({
root: {
backgroundColor: theme.palette.primary.main,
minWidth: '100px',
}
}))(MaterialButton);
This code is working however, is there a way to have the component employ all of the properties when I use color="primary" to the Material-UI Button. Do I need to include ALL of the properties or is there an easier way.
Also, I am sure using 100px is not the correct way for this control to be responsive. Is 100em correct or should I use something different?
This is possible in Material-UI v5, you can create a custom variant for your Button. A variant is just a specific styles of a component, each variant is identified by a set of component props. See the example below:
const defaultTheme = createMuiTheme();
const theme = createMuiTheme({
components: {
MuiButton: {
variants: [
{
props: { color: "primary", variant: "contained" },
style: {
backgroundColor: defaultTheme.palette.primary.main,
fontSize: 20,
minWidth: "200px"
}
},
{
props: { color: "secondary", variant: "contained" },
style: {
backgroundColor: defaultTheme.palette.secondary.main
}
}
]
}
}
});
When you render this component:
<Button color="primary" variant="contained">
Primary
</Button>
The first variant style specified above is used because they have the same props:
{ color: "primary", variant: "contained" }
Live Demo
You can create custom variant as mentioned by #NearHuscarl other way you can create component modifying style.
Something like.
import { makeStyles } from '#material-ui/core/styles'
import Button from '#material-ui/core/Button'
import PropTypes from 'prop-types'
const useStyles = makeStyles((theme) => ({
root: {
height: '48px',
width: '100%',
backgroundColor: '#232323',
color: '#fff',
fontSize: '16px',
borderRadius: '4',
minHeight: theme.spacing(6),
'&:hover, &:active, &:focus': {
backgroundColor: `rgba(0, 0, 0, 0.8)`,
},
'&.Mui-disabled': {
opacity: 0.75,
},
},
}))
const propTypes = {
loading: PropTypes.bool,
}
function PrimaryButton({ children, loading = false, ...props }) {
const classes = useStyles()
return (
<Button
variant="contained"
className={classes.root}
disabled={loading}
{...props}
>
{children}
</Button>
)
}
PrimaryButton.propTypes = propTypes
export default PrimaryButton
Created code sandbox
https://codesandbox.io/embed/boring-mayer-cjl0f?fontsize=14&hidenavigation=1&theme=dark

Material UI Overriding styles with increased specificity

How can I override a rule of a class which has high specificity?
For example, the .MuiAccordionSummary-content.Mui-expanded class in the AccordionSummary
const useStyles = makeStyles(() => ({
expanded: {
marginBottom: 0,
},
}));
in combination with:
<AccordionSummary classes={{
expanded: classes.expanded,
}}/>
is applied but overridden.
Note: marginBottom: '0 !important' works, but doesn't feel like an optimal solution.
You could use global overrides to change the default margin of the AccordionSummary. However, this will affect all AccordionSummary components in your application.
The better approach (the one you are already using) is to wrap the component and alter its classes. If you look into the source of the AccordionSummary, you will find that the expanded property is an empty block. The margin gets set by the referencing selector in the content property:
content: {
display: 'flex',
flexGrow: 1,
transition: theme.transitions.create(['margin'], transition),
margin: '12px 0',
'&$expanded': {
margin: '20px 0',
},
},
If you add the same reference in your custom styles, the priority becomes higher and you won't need !important. You will have to add the expanded className to your custom styles though:
import React from 'react';
import makeStyles from '#material-ui/core/styles/makeStyles'
import Accordion from '#material-ui/core/Accordion';
import AccordionDetails from '#material-ui/core/AccordionDetails';
import AccordionSummary from '#material-ui/core/AccordionSummary';
const useStyles = makeStyles(() => ({
expanded: {},
content: {
'&$expanded': {
marginBottom: 0,
},
},
}));
const MyAccordion = ({ summary, details }) => {
const classes = useStyles();
return (
<Accordion>
<AccordionSummary classes={{ content: classes.content, expanded: classes.expanded }}>
{summary}
</AccordionSummary>
<AccordionDetails>
{details}
</AccordionDetails>
</Accordion>
)
};
export default MyAccordion;

Media queries in MUI components

I am using MUI components in ReactJs project, for some reason I need customization in some components to make it responsive according to screen width.
I have added media query and pass it as style attribute in the components but not working, any idea?
I am using code like this:
const drawerWidth = {
width: '50%',
'#media(minWidth: 780px)' : {
width: '80%'
}
}
<Drawer
.....
containerStyle = {drawerStyle}
>
</Drawer>
Code is working for web only, on mobile device no effect. Even CSS code is not applying I've checked in developer console. I am using MUI version 0.18.7.
Any help would be appreciated.
PS: As per requirement I need to make some changes according to screen size using CSS.
By using the breakpoints attribute of the theme, you can utilize the same breakpoints used for the Grid and Hidden components directly in your component.
API
theme.breakpoints.up(key) => media query
Arguments
key (String | Number): A breakpoint key (xs, sm, etc.) or a screen width number in pixels.
Returns
media query: A media query string ready to be used with JSS.
Examples
const styles = theme => ({
root: {
backgroundColor: 'blue',
[theme.breakpoints.up('md')]: {
backgroundColor: 'red',
},
},
});
for more information check this out
You were almost right, but you need to use min-width instead of minWidth:
const styles = {
drawerWidth: {
width: '50%',
'#media (min-width: 780px)': {
width: '80%'
}
}
}
You have a typo in the media query. You should use the following syntax and it will work as expected:
const drawerWidth = {
width: '50%',
'#media (min-width: 780px)' : {
width: '80%'
}
}
instead of
const drawerWidth = {
width: '50%',
'#media(minWidth: 780px)' : {
width: '80%'
}
}
In MUI v5, breakpoints can be declared in sx props by specifying an object where the keys are the breakpoint names and the values are the CSS values.
You can see MUI default breakpoints here. The breakpoint names and values can be overrided using createTheme():
const theme = createTheme({
breakpoints: {
values: {
xxs: 0, // small phone
xs: 300, // phone
sm: 600, // tablets
md: 900, // small laptop
lg: 1200, // desktop
xl: 1536 // large screens
}
}
});
return (
<ThemeProvider theme={theme}>
<Box
sx={{
// specify one value that is applied in all breakpoints
color: 'white',
// specify multiple values applied in specific breakpoints
backgroundColor: {
xxs: "red",
xs: "orange",
sm: "yellow",
md: "green",
lg: "blue",
xl: "purple"
}
}}
>
Box 1
</Box>
</ThemeProvider>
);
In the example above, xs: "orange" means set the Box color to orange if the screen width is inside xs range [300, 600).
You can also set the breakpoints using an array consists of the values from the smallest to largest breakpoint:
return (
<ThemeProvider theme={theme}>
<Box
sx={{
backgroundColor: [
"red",
"orange",
// unset, screen width inside this breakpoint uses the last non-null value
null,
"green",
"blue",
"purple"
]
}}
>
Box 2
</Box>
</ThemeProvider>
);
Similiar answer to #Lipunov's, based on #nbkhope's comment
const styles = {
drawerWidth: {
width: '50%',
[theme.breakpoints.up(780)]: {
width: '80%'
}
}
}
I've solved this problem by doing something like this:
const dropzoneStyles =
window.screen.availWidth < 780 ?
{ 'width': '150px', 'height': '150px', 'border': 'none', 'borderRadius': '50%' }
: { 'width': '200px', 'height': '200px', 'border': 'none', 'borderRadius': '50%' };
and then appending it as an attribute in the Material UI element:
<Dropzone style={dropzoneStyles} onDrop={this.handleDrop.bind(this)}>
So the key is to find out the window screen using window.screen.availWidth. And you would be doing this in the render() function. Hope that helps!
In the style property on React you can only define properties that you can define in a normal DOM element (You can't include media queries for example)
The way you can include media queries for that component would be passing a class name to the Drawer Component
<Drawer containerClassName="someClass" />
And then in a CSS file you do something like this
#media(min-width: 780px){
.someClass {
width: 50%!important;
}
}
In my case I just needed the breakpoint on one component and I found the createTheme approach a little bit too much. I ended using useMediaQuery and useTheme.
I see that with useMEdiaQuery you can be quite granular
import { useTheme } from '#mui/material/styles';
import useMediaQuery from '#mui/material/useMediaQuery';
const Component = () => {
const theme = useTheme();
const matchesSM = useMediaQuery(theme.breakpoints.down('sm'));
const matchesMD = useMediaQuery(theme.breakpoints.only('md'));
const dynamicStyles = {
...matchesSM && {margin: '10px 0'},
...matchesMD && {margin: '20px 0'}
}
return (
<Grid item xs={12} md={4} sx={{...dynamicStyles}}>
<div>Children</div>
</Grid>
)
}
CSS media queries are the idiomatic approach to make your UI responsive. The theme provides five styles helpers to do so:
theme.breakpoints.up(key)
theme.breakpoints.down(key)
theme.breakpoints.only(key)
theme.breakpoints.not(key)
theme.breakpoints.between(start, end)
In the following stress test, you can update the theme color and the background-color property live:
const styles = (theme) => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('md')]: {
backgroundColor: theme.palette.secondary.main,
},
[theme.breakpoints.up('md')]: {
backgroundColor: theme.palette.primary.main,
},
[theme.breakpoints.up('lg')]: {
backgroundColor: green[500],
},
},
});
<Root>
<Typography>down(md): red</Typography>
<Typography>up(md): blue</Typography>
<Typography>up(lg): green</Typography>
</Root>
Know more
Create a variable and then use that variable anywhere in the function
import React from 'react';
import { createMuiTheme, ThemeProvider, useTheme } from '#materialui/core/styles';
import useMediaQuery from '#material-ui/core/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up('sm')); // Variable for media query
return <span hidden={matches}>Hidden on screen size greater then sm </span>;
}
const theme = createMuiTheme();
export default function ThemeHelper() {
return (
<ThemeProvider theme={theme}>
<MyComponent />
</ThemeProvider>
);
}

Resources