What is the use of '& .MuiTextField-root' in { makeStyles } of Material UI? - css

I am making a project in React JS through some resources using Material-UI. I was creating in Form.js file for my app, and there was some styling given to that as shown below ---
import useStyles from './styles';
const classes = useStyles();
<form autoCapitalize='off' noValidate className={`${classes.root} ${classes.form}`} onSubmit={handleSubmit}> ```
In styles.js --
export default makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
},
form: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
}
}));
Now I am not able to understand the use of
'& .MuiTextField-root': {
margin: theme.spacing(1),
}
Can anyone please help me with this like what is the use of '& .MuiTextField-root' ??
When I try to simply use the
margin: theme.spacing(1),
its not giving margin from top and bottom, but its giving very less margin
from left and right. But using the Former one gives equal and more margin
from each side to all the textfields and Buttons in the . Why is it so?? Any help will be appreciated. Thanks!
Attaching the Screenshot ---FORM Image with '& .MuiTextField-root'
FORM Image WithOut '& .MuiTextField-root', just using root: {margin: theme.spacing(1)} only

If you know SCSS then this is the convention to select the child selector to write class for. As you are using it in a form. So the text fields are the child of the form. That's why you have to access/write it like this.
The following code actually mean, any child containing the class name MuiTextField-root will have the margin css rules in it. Doc
'& .MuiTextField-root': {
margin: theme.spacing(1),
}

& .MuiTextField-root would mean any children of current element with that class

You can use the browser dev tools to identify the slot for the component you want to override. It can save you a lot of time. The styles injected into the DOM by MUI rely on class names that follow a simple pattern: [hash]-Mui[Component name]-[name of the slot].
⚠️ These class names can't be used as CSS selectors because they are unstable, however, MUI applies global class names using a consistent convention: Mui[Component name]-[name of the slot].
If you would like to override the styles of the components using classes, you can use the className prop available on each component. For overriding the styles of the different parts inside the component, you can use the global classes available for each slot, as described in the previous section.

Related

Material UI 5 class name styles

I migrated from Mui 4 to 5 and wonder how to use class names. If I want to apply certain styles to just one component there is the SX property. However, I'm struggling with using the same class for multiple components. In v4 my code looked like this:
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(1),
margin: 'auto',
},
})
)
I could import this useStyles hook in any component and use it like this:
const classes = useStyles()
...
<div className={classes.root}>...</div>
This docs say, that I can 'override styles with class names', but they don't tell how to do it:
https://mui.com/customization/how-to-customize/#overriding-styles-with-class-names
Do I have to put these styles in an external CSS file?
.Button {
color: black;
}
I would rather define the styles in my ts file.
I also found this migration guide:
https://next.material-ui.com/guides/migration-v4/#migrate-makestyles-to-emotion
I don't like approach one, because using this Root wrapper, it is inconvenient to apply a class conditionally. (Especially for typescript there is some overhead) Approach two comes with an external dependency and some boilerplate code.
Ideally I would use styles like this, perhaps with one rapper function around the styles object:
export const root = {
padding: theme.spacing(1),
margin: 'auto',
}
<div className={root}>...</div>
Of course, the last approach doesn't work, because className wants a string as input. Does anybody know an alternative with little boilerplate code?
I suggest you take a look at emotion's documentations for details. The sx prop is actually passed to emotion.
You can do something like this:
const sx = {
"& .MuiDrawer-paper": {
width: drawerWidth
}
};
<Drawer sx={sx}/>
Equivalent to MUI v4
const useStyles = makeStyles({
drawerPaper: {
width: drawerWidth,
}
});
const classes = useStyles();
<Drawer
classes={{
paper: classes.drawerPaper,
}}
/>
Answering your exact question, there are use cases (I think yours is not one of them and you should use styled components) however for those like me who stumble upon it and want a "exact answer to this question" and not a "do this instead", this is how you achieve to retrieve the class names.
This is so far undocumented.
For functional components, using emotion, here an use case where you have a 3rd party component that expects, not one, but many class names, or where the className property is not where you are meant to pass the property.
import { css, Theme, useTheme } from "#mui/material/styles";
import { css as emotionCss } from "#emotion/css";
const myStyles = {
basicClass: {
marginLeft: "1rem",
marginRight: "1rem",
paddingLeft: "1rem",
paddingRight: "1rem",
},
optionClass: (theme: Theme) => ({
[theme.breakpoints.down(theme.breakpoints.values.md)]: {
display: "none",
}
})
}
function MyComponent() {
cons theme = useTheme();
// first we need to convert to something emotion can understand
const basicClass = css(myStyles.basicClass);
const optionClass = css(myStyles.optionClass(theme));
// now we can pass to emotion
const basicClassName = emotionCss(basicClass.styles);
const optionClassName = emotionCss(optionClass.styles);
return (
<ThirdPartyComponent basicClassName={basicClassName} optionClassName={optionClassName} />
)
}
When you have a Class Component, you need to use the also undocumented withTheme from #mui/material/styles and wrap your class, if you use the theme.
WHEN IT IS NOT AN USE CASE
When your component uses a single className property just use styled components.
import { styled } from "#mui/material/styles";
const ThrirdPartyStyled = styled(ThirdPartyComponent)(({theme}) => ({
color: theme.palette.success.contrastText
}))
Even if you have dynamic styles
import { styled } from "#mui/material/styles";
interface IThrirdPartyStyledExtraProps {
fullWidth?: boolean;
}
const ThrirdPartyStyled = styled(ThirdPartyComponent, {
shouldForwardProp: (prop) => prop !== "fullWidth"
})<IThrirdPartyStyledExtraProps>(({theme, fullWidth}) => ({
color: theme.palette.success.contrastText,
width: fullWidth ? "100%" : "auto",
}))
Even if each one has some form of custom color, you just would use "sx" on your new ThrirdPartyStyled.
When you are just trying to reuse a style around (your use case)
const myReusableStyle = {
color: "red",
}
// better
const MyStyledDiv = styled("div")(myReusableStyle);
// questionable
const MySpanWithoutStyles = styled("span")();
// better
const MyDrawerStyled = styled(Drawer)(myReusableStyle);
function MyComponent() {
return (
<MyStyledDiv>
questionable usage because it is less clean:
<MySpanWithoutStyles sx={myReusableStyle}>hello</MySpanWithoutStyles>
<MySpanWithoutStyles sx={myReusableStyle}>world</MySpanWithoutStyles>
these two are equivalent:
<MyDrawerStyled />
<Drawer sx={myReusableStyle} />
</MyStyledDiv>
)
}
Now what is "presumably" cool about this is that your style, is just an object now, and you can just import it and use it everywhere without makeStyles or withStyles, supposedly an advantage, even when to be honest, I have never used that of exporting/importing around; the code seems a bit cleaner nevertheless.
You seem to want to use it so all you do is.
export const myStyles {
// your styles here
}
because this object is equivalent in memory, and it is always the same object, something that is easier to mess up with styles, it should be as effective or even more than your hook, theoretically (if it re-renders often even when setup may be longer), which stores the same function in memory but returns a new object every time.
Now you can use those myStyles everywhere you deem reasonable, either with styled components or by assigning to sx.
You can further optimize, say if it's always a div that you use that is styled the same way, then the styled component MyStyledDiv should be faster, because it is the same and done each time. How much faster is this? According to some sources 55% faster, to me, it is taking 4 weeks of refactor and the JSS compatibility with emotion is bad, all mixed with SSR is making everything unusable and slow and broken, so let's see until then when the whole is refactored.
Here is a pattern that I've found useful in MUI 5. It allows you to keep style definitions in the same file but isolated, & avoids repeated function calls for every CSS property where you need to access your theme (e.g. width: ({ spacing }) => spacing(12))). It also feels similar to MUI's native CSS API.
Create a function that takes your theme as an argument & returns an object of named style groups. Then reference those groups directly in your sx props. This also allows for the use of classNames in a way similar to Material-UI 4.
import { useTheme } from '#mui/material';
import clsx from 'clsx';
export const NavItem = (props) => {
// Bring in style groups
const sx = styles(useTheme());
// Define classNames
const classNames = clsx({
isActive: props.isActive
});
return (
{/* Use classNames and style groups */}
<ListItemButton className={classNames} sx={sx.button}>
<ListItemAvatar sx={sx.avatar}>{props.icon}</ListItemAvatar>
<ListItemText>{props.label}</ListItemText>
</ListItemButton>
);
}
// Define style groups
function styles(theme) => {
return {
button: {
paddingX: 6,
'&.isActive': {
backgroundColor: theme.palette.secondary.light
}
},
avatar: {
'.isActive &': {
border: '2px solid green'
}
}
};
}
I'm in the same boat, about six months behind, i.e., starting to make the transition to v5 from v4 now... Just when I thought I had a handle on it all!
Having read this post and trying a few things out, I was able to replicate the ability to re-use a chunk of css. I'm a big fan of what used to be the overrides prop; that feature hasn't gone away, it's just under a different prop (loosely speaking). Regardless, I mention it because it provides access to what I like a lot about css: selectors.
To hit all MUI-Drawers my pref is for whatever the new overrides is. For targeted reuse of css I like the following:
import { reuseThisCss } from 'sharedCss';
export default styled(Drawer)(({ theme, ownerState }) => {
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& .MuiListItemText-root': reuseThisCss,
},
};
export default ThisSpecificDrawerVariant;
Note: The focus is not on using styled (It's not my goto approach).
The css in the return value is the equivalent to the following css: .MuiDrawer-paper .MuiListItemText-root {...}.
This says, "select all .MuiListItemText-root under the .MuiDrawer-paper parent. If I want to optimize the render, while increasing the dependency on a specific hierarchy, I'll specify/expand on the selector that much more with whatever lies between the .MuiDrawer-paper and MuiListItemText-root. For instance, in my case:
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& > a > li > div > .MuiListItemText-root': reuseThisCss,
},
};
Finally, per a question in the comments, generally this will not prevent a nested application of the style. In my experience, marking each level with a className is useful. I only "mark" the element that signals the start of a new level. So, if it were Drawer in the above example, I would start the css selector with .MUI-Drawer.level-3. The rest of css remains the same.
I still have not figured out if whether setting the className dynamically remains a performant and sufficiently flexible goto... TBD.
If you are using makeStyles or withStyles to provide CSS class, you can follow the instruction below.
CSS overrides created by makeStyles

React Material UI DataGrid style MuiDataGrid-window within createMuiTheme or makeStyles / styled

I'm trying really hard to change the css for the .MuiDataGrid-window in MatierialUi DataGrid.
Therefore I was following css rules from https://material-ui.com/api/data-grid/
I tried it within createMuiTheme for root it was working fine, but not for window. I also tried a lot of different cominations like MuiDataGrid-window or only 'MuiDataGrid-window' directly under overrides, but nothing worked..
export const theme = createMuiTheme({
overrides: {
MuiDataGrid: {
root: {
backgroundColor: 'red',
},
window: {
width: '120%',
},
},
}
});
Next try was a styled DataGrid component, which also didn't work out.
Both didn't work. A styled component would be my prefered way!
const StyledDataGrid = styled(DataGrid)({
MuiDataGrid: {
root: {
backgroundColor: 'green',
},
window: {
width: '120%',
}
}
});
Maybe I'm completely on the wrong way.. But how to style the CSS attributes in MUI's API like .MuiDataGrid-mainGridContainer, .MuiDataGrid-overlay, .MuiDataGrid-columnsContainer, .MuiDataGrid-colCellWrapper etc.
Thanks a lot and maybe it is helpful for somebody else :)
If you check the styles applied, window class element has two selectors associated (multiple classes):
.MuiDataGrid-root .MuiDataGrid-window
To apply the styles in children elements, such as, window in grid root, you need to select both of them:
MuiDataGrid: {
root: {
backgroundColor: 'red',
'& .MuiDataGrid-window': {
backgroundColor: 'green'
}
}
}
In documentation the grid component have just one rule name: root

Failed to override styles with classes in material-ui

I was implementing a table with TableRow component from material-ui, which has a property called "selected". Whenever "selected" is true, a pink background-color(from the default theme) is applied for it.
I was trying to change this default pink color, according to the docs, i chose to override the css classes like:
const styles = theme => ({
rowSelected: {
backgroundColor: theme.palette.grey[700]
}
})
class CustomTableRow extends React.Component {
// basic component logic
render() {
const {classes, className, style } = this.props;
<TableRow
key={key}
component="div"
selected={true}
hover={true}
className={className}
classes={{ selected: classes.rowSelected}}
style={style}
>
// some children
</TableRow>
}
export default withStyles(styles, { withTheme: true })(CustomTableRow);
But this didn't work, which was very confusing. Because i had succeeded to do the same thing somewhere else for a Drawer component with the same method above.
I debugged every css properties with Chrome Dev Tools. What i am suspecting most now is, the pink color applied on this component with this way below:
.MuiTableRow-root.Mui-selected, .MuiTableRow-root.Mui-selected:hover {
background-color: rgba(245, 0, 87, 0.16);
And my custom class style had lower precedence than this one, which was greyed out.
UPDATE1:
My project is too big, i don't know how to simplify it for codesandbox.io.
Maybe we can check the material-ui source code directly, TableRow Source Code.
What i was doing is to override this css declaration in root
'&$selected, &$selected:hover': {
backgroundColor: fade(theme.palette.secondary.main, theme.palette.action.selectedOpacity),
},
by passing in another selected declaration below. I realized it's because this &$selected, &$selected:hover is not normally css, even if i copy this into rowSelected, it doesn't work either.
UPDATE2:
I managed to override that backgroundColor, with '!important' keyword:
const styles = theme => ({
rowSelected: {
backgroundColor: theme.palette.grey[700] + " !important",
}
})
I don't know whether this is one ideal solution. This clearly shows the problem is about css classes precedence. So how to override that already defined backgroundColor in class root with class selected.
Some help please, thank you.
To provide specifity for selected class you can apply the $selected and $selected:hover classes to your overrides like below
const styles = theme => ({
rowSelected: {
"&$selected, &$selected:hover": {
backgroundColor: theme.palette.grey[800]
}
}
})
Sample demo

Override React AgGrid styling settings using createStyles and withStyles

I'm aware that it's possible to override Ag Grid properties by editing the CSS itself, however I'm wondering if it's possible to use the functionalities built into react to do this instead. I'm relatively new to the two frameworks, so apologies if there's something I'm not understanding.
Ultimately, what I want to do is something like this:
styles.js
---------
const styles = (theme: Theme) =>
createStyles({
root: {
position: 'relative',
height: 'calc(100vh - 128px)',
},
agHeaderCellLabel: {
agHeaderCellText: {
writingMode: 'vertical-lr',
marginTop: '100px',
},
},
})
export default styles
GridComponent.tsx
-----------------
import styles from './styles'
...
return (
<Paper className={classes.root}>
<div
id="myGrid"
style={{
height: '100%',
width: '100%',
}}
className={`ag-theme-material ${classes.agHeaderCellLabel}`}
>
<AgGridReact
// listening for events
onGridReady={onGridReady}
onRowSelected={onRowSelected}
onCellClicked={onCellClicked}
onModelUpdated={calculateRowCount}
// Data
columnDefs={cDef}
defaultColDef={defaultColumnFormat}
suppressRowClickSelection={true}
groupSelectsChildren={true}
debug={true}
rowSelection="multiple"
// rowGroupPanelShow={this.state.rowGroupPanelShow}
enableRangeSelection={true}
pagination={true}
rowData={rows}
/>
</div>
</Paper>
)
...
export withStyles(styles)(GridComponent)
In this example I'm just trying to get the header text to be displayed vertically.
I've inherited this project, and I've noticed that all of the styling has been done in this method, as there are no custom css files lying around, so I'm trying to stick with that convention of a styles file alongside the component.
Is this possible, and if so,
I ran into this same situation, and came up with the following solution. Although not necessarily ideal, it allows you to continue with the desired convention.
styles.js
---------
const styles = (theme: Theme) =>
createStyles({
root: {
position: 'relative',
height: 'calc(100vh - 128px)',
},
//Apply changes to agGrid material HeaderRoot
myClassAppliedToGrid: {
'& .ag-header[ref="headerRoot"]':{
writingMode: 'vertical-lr',
marginTop: '100px',
}
}
//OR
//Apply Changes to agGrid material header row
myClassAppliedToGrid: {
'& .ag-header-row':{
writingMode: 'vertical-lr',
marginTop: '100px',
}
}
})
export default styles
The key idea is using the & SASS syntax to "reach into" agGrid and make more specific CSS classes so you can override them. (see https://css-tricks.com/the-sass-ampersand/ for more info)
The key pieces of info are:
.parent {
& .child {}
}
turns into
.parent .child {}
and
.some-class {
&.another-class {}
}
turns into
.some-class.another-class { }
Using this sytanx, you should be able to create CSS classes that you can apply to your grid, columns, rows, etc that will properly override the material ag-grid theme.
Here is another example, but this class gets applied to a cell using agGrid cellStyleRules when a row is dragged over it, rather than applying the class to the grid as a whole. This way it only effects cells that have a row drag occuring over them:
rowDraggedOverCellsTopEdge: {
'&.ag-cell': {
borderTopColor: theme.palette.gray[50],
borderTopWidth: 8,
backgroundColor: fade(theme.palette.gray[50], 0.3)
}
},
Finally, one thing I did not do but would reccommend investigating is looking into agGrid's theme overriding, especially if you are on version 23+
https://www.ag-grid.com/javascript-grid-themes-provided/#customising-themes
It might be a good idea to get your base overrides to the theme done this way if you expect a consistent look and feel of your grids throughout the application.
Cheers!

ReactJS Material UI component css classes, clarification

I understand that to style Material UI components I should use their useStyles, like:
const useStyles = makeStyles(themes => ({
root: {
marginTop: '15px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexGrow: '0'
},
menuButton: {
color: grey[800],
},
}));
but I'm not sure about why. For example, what if I want to use the same css on several components? Wouldn't it be easier to have a separate css file?
You can export them and reuse in other components, that way you end up with only one makeStyles function which you can probably place in some unified file of styles.
export const useStyles = makeStyles(themes => ({
root: {
marginTop: '15px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexGrow: '0'
},
menuButton: {
color: grey[800],
},
}));
Why would you do this:
Its a theme based component, say it as you have global theme of using blue buttons but then you are overriding it with green but keeping fonts and other stuff inherited from the theme or possibly discarding entirely.
This method is possibly preference based (and in case your repo is following CSS in JS style and you dont want to mix up CSS) but if you want to enforce styled component in your library then you would use this method on the component which is contained within the component.
You can think of this method as taking in themes and then passing in modifier to the theme that you want to override for the component that uses useStyles class.
With that said, you can use it as CSS class as well in your component.
no you can import it in other files where you need it.
suppose you need root style in another View.jsx example file. then import useStyles to that View.jsx file and then you can use all its classes in that View.jsx file.

Resources