Material-ui: compiled css precedence

Created on 15 Jul 2019  ·  32Comments  ·  Source: mui-org/material-ui

When a project is compiled, the precedence of applied CSS rules changes.

  • [X] This is not a v0.x issue.
  • [X] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior 🤔

.MuiDialogActions-spacing > * + * {
    margin-left: 8px;
}

... other styles ...

.MuiButtonBase-root {
    color: inherit;
    border: 0;
    margin: 0;
    cursor: pointer;
    display: inline-flex;
    outline: none;
    padding: 0;
    position: relative;
    align-items: center;
    user-select: none;
    border-radius: 0;
    vertical-align: middle;
    justify-content: center;
    -moz-appearance: none;
    text-decoration: none;
    background-color: transparent;
    -webkit-appearance: none;
    -webkit-tap-highlight-color: transparent;
}

Current Behavior 😯

.MuiButtonBase-root {
    color: inherit;
    border: 0;
    margin: 0;
    cursor: pointer;
    display: inline-flex;
    outline: none;
    padding: 0;
    position: relative;
    align-items: center;
    user-select: none;
    border-radius: 0;
    vertical-align: middle;
    justify-content: center;
    -moz-appearance: none;
    text-decoration: none;
    background-color: transparent;
    -webkit-appearance: none;
    -webkit-tap-highlight-color: transparent;
}

... other styles ...

.MuiDialogActions-spacing > * + * {
    margin-left: 8px;
}

Steps to Reproduce 🕹

  1. Run a demo of Dialog on localhost to see the expected behavior
  2. Compile the demo and run to see the marginLeft of MuiDialogActions is overridden by margin of MuiButtonBase

Your Environment 🌎

| Tech | Version |
|--------------|---------|
| Material-UI | v4.1.3 |
| React | v16.8.6 |
| Browser | Chrome |

bug 🐛 important

Most helpful comment

The CSS specificity of .x > * + * is 1 then we rely on

import '../Button'; // So we don't have any override priority issue.

to get the CSS injection order right.

The alternative is to increase the specificity to 2. I like this idea, it would make use component agnostic (work with everybody, not just our Button)

diff --git a/packages/material-ui/src/CardActions/CardActions.js b/packages/material-ui/src/CardActions/CardActions.js
index ee57f0120..91b7c574a 100644
--- a/packages/material-ui/src/CardActions/CardActions.js
+++ b/packages/material-ui/src/CardActions/CardActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -13,7 +12,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },
diff --git a/packages/material-ui/src/DialogActions/DialogActions.js b/packages/material-ui/src/DialogActions/DialogActions.js
index 6fb342f5c..458c61350 100644
--- a/packages/material-ui/src/DialogActions/DialogActions.js
+++ b/packages/material-ui/src/DialogActions/DialogActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -15,7 +14,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },
diff --git a/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js b/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
index 5451c6387..2e3f9f179 100644
--- a/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
+++ b/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -14,7 +13,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },

cc @eps1lon as you were advocating for removing the import (tree-shaking)
cc @NMinhNguyen as you were wondering about it

@Luucky I can't explain the behavior of the deployment. Something is wrong in the bundling pipeline independently from my previous comment about CSS specificity. ~I can reproduce the problem with our CRA and Next.js examples~.

All 32 comments

@Luucky Please provide a full reproduction test case. This would help a lot 👷 .
A live example would be perfect. This codesandbox.io template _may_ be a good starting point. Thank you!

@oliviertassinari Here's a link for the DEV EXAMPLE: https://codesandbox.io/embed/create-react-app-36pid
And here's a production build link: https://csb-36pid.netlify.com/

Notice how space between buttons disappears. The margin of ButtonBase overrides the DialogActions left of margin.

The CSS specificity of .x > * + * is 1 then we rely on

import '../Button'; // So we don't have any override priority issue.

to get the CSS injection order right.

The alternative is to increase the specificity to 2. I like this idea, it would make use component agnostic (work with everybody, not just our Button)

diff --git a/packages/material-ui/src/CardActions/CardActions.js b/packages/material-ui/src/CardActions/CardActions.js
index ee57f0120..91b7c574a 100644
--- a/packages/material-ui/src/CardActions/CardActions.js
+++ b/packages/material-ui/src/CardActions/CardActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -13,7 +12,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },
diff --git a/packages/material-ui/src/DialogActions/DialogActions.js b/packages/material-ui/src/DialogActions/DialogActions.js
index 6fb342f5c..458c61350 100644
--- a/packages/material-ui/src/DialogActions/DialogActions.js
+++ b/packages/material-ui/src/DialogActions/DialogActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -15,7 +14,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },
diff --git a/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js b/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
index 5451c6387..2e3f9f179 100644
--- a/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
+++ b/packages/material-ui/src/ExpansionPanelActions/ExpansionPanelActions.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import withStyles from '../styles/withStyles';
-import '../Button'; // So we don't have any override priority issue.

 export const styles = {
   /* Styles applied to the root element. */
@@ -14,7 +13,7 @@ export const styles = {
   },
   /* Styles applied to the root element if `disableSpacing={false}`. */
   spacing: {
-    '& > * + *': {
+    '& > :not(:first-child)': {
       marginLeft: 8,
     },
   },

cc @eps1lon as you were advocating for removing the import (tree-shaking)
cc @NMinhNguyen as you were wondering about it

@Luucky I can't explain the behavior of the deployment. Something is wrong in the bundling pipeline independently from my previous comment about CSS specificity. ~I can reproduce the problem with our CRA and Next.js examples~.

I also had a similar thing happen when I tried the following:

<List>       
       <ListItem button component={Box} border={1}>
               Click this item
       </ListItem>
</List>

ButtonBase would reset my border also after deployment.
I fixed it by doing the following, I don't know which is a better practice:

<List>       
       <Box border={1}>
                <ListItem button>
                        Click this item
                 </ListItem>
       </Box>
</List>

ButtonBase would reset my border also after deployment.

@Luucky This one is expected. Unless you control the import order, the CSS win is unpredictable.

i have the same issue with @Luucky

I'm running into an issue that may be related. If not, I will open another issue.

When building my react app in production mode ($ npm run build) the order of the MUI style elements is getting mixed up with my own jss style elements. However, if I run it in development ($ npm start) everything is as expected. This is project is using 4.2.0. I have a similar project that is using 3.9.1 and it works the same both production/dev (i.e. correctly as I would expect).

it seems the production mode is the issue, but I do not know where to begin looking.

The same issue exists with .MuiCardActions-spacing.

I just ran into this issue setting up server rendering. Server rendering has them in the right order and React.render renders them in the right order but React.hydrate results in .MuiButtonBase-root being added after .MuiCardActions-spacing which makes the buttons drop the left margin after hydrate.

@nickpalmer this symptom might hide another issue.

@oliviertassinari After further investigation we have the exact same problem on our staging environment, we just never noticed. It thus has nothing to do with hydrate and everything to do with how create-react-app is compiling things for production.

Fixed similar behaviour with webpack setting sideEffects to false, which is enabled in production mode by default.

module.exports = {
  //...
  optimization: {
    sideEffects: false
  }
};

https://webpack.js.org/configuration/optimization/#optimizationsideeffects

@dva I can confirm that this resolves the issue.

@nickpalmer final build getting bigger btw.

it works correctly if we disable SSR. in case of SSR few styles are picked from server css file.
i tried removing muisheet.collect(<App {...props} />) from server side and it resolved the issue currently.

While I can no longer reproduce the issue on Codesandbox, CRA and Next.js, I would propose that we move forward with https://github.com/mui-org/material-ui/issues/16609#issuecomment-511764782 so we can:

  1. Be invariant to custom margins people might implement on the button
  2. Maximize tree-shaking in people code that doesn't use a button in the actions
  3. Help people that have buggy tree-shaking env and that don't use our recommended tree-shaking plugin.

Not sure if this is the same issue, let me know if I should open a new one.

I have TextField and I'm adding styling to it using the useStyles hook like so:

const useStyles = makeStyles({
  input: { display: 'block' }
}

const MyComp = () => {
  const classes = useStyles()

  return (<TextField className={ classes.input } /> )
}

This produces locally the class .makeStyles-field-216 which takes precedence over .MuiFormControl-root that has display: 'inline-flex'.

However, when built and run in production, the generated class is .jss253, and .MuiFormControl-root has precedence over it, and breaks the layout.

material-ui version 4.3.3

Thanks!

I'm running into an issue that may be related. If not, I will open another issue.

When building my react app in production mode ($ npm run build) the order of the MUI style elements is getting mixed up with my own jss style elements. However, if I run it in development ($ npm start) everything is as expected. This is project is using 4.2.0. I have a similar project that is using 3.9.1 and it works the same both production/dev (i.e. correctly as I would expect).

it seems the production mode is the issue, but I do not know where to begin looking.

I have this exact same problem.

EDIT: The workaround for me was adding {index: 1} in all of our components' withStyles, to ensure they were created after all the Material UI ones in production.

export default withStyles(styles, {index: 1})(MyComponent);

We also have the same problem using version 4.3.3. The workaround provided by @alexandrecanuto helped.

It seems like jss.createStyleSheet is inserting new styles always at last when all have same index. If then a MUI component is rendered after makeStyles the order gets wrong. For me it looks like an issue with webpack optimization and jss in production mode vs development mode

Question: Why is this not set to 1 by default for makeStyles? Would this not help in future?

Had the same issue with Chip Avatar.

In local development it looks like this:
Screenshot 2019-10-16 at 12 10 04 AM

Screenshot 2019-10-16 at 12 16 33 AM

In our staging environment it looks like this:
Screenshot 2019-10-16 at 12 10 14 AM

Screenshot 2019-10-16 at 12 17 11 AM

Using version 4.5.1.

Edit: Sorry just noticed that https://github.com/mui-org/material-ui/issues/16374 was created for this specifically.

Running into the same issue on 4.5.0 - we're not using SSR. Just a typical CRA. We have a shared export const useGlobalStyles = makeStyles(theme => ({ ... })); file we use, and I'm noticing when these are imported using the const globalClasses = useGlobalStyles(); hook and applied with className, they are being stomped on by MUI styles.

In the image example below, the element is used like this:
<Grid container spacing={2} className={globalClasses.gridContainer}>

I would expect the .makeStyles-gridContainer-241 class styles to be more important than the MuiGrid-spacing-xs-2, but that's not the case.

Annotation 2019-10-20 100221

This was working just a few days ago. We're seeing this even when running on localhost during development.

My workaround in this specific case was to change: margin: theme.spacing(0)
to: margin: '${theme.spacing(0)} !important'

Is there any movement on this? I'm getting more and more areas where I'm having to use !important, which is less than ideal.

@Kizmar We would be happy to accept a pull request for https://github.com/mui-org/material-ui/issues/16609#issuecomment-511764782.

Having the same issue as in https://github.com/mui-org/material-ui/issues/16609#issuecomment-517062719 - i.e. Dev != Prod

A copy-paste:

/* Dev: */
.MuiCardHeader-root {
    display: flex;
    padding: 16px;
    align-items: center;
}
.makeStyles-statusBarOk-276 {
    color: #fff;
    background: #4e698c;
    padding-top: 8px;
    padding-bottom: 8px;
}

/* Prod: */
.jss276 {
    color: #fff;
    background: #4e698c;
    padding-top: 8px;
    padding-bottom: 8px;
}
.MuiCardHeader-root {
    display: flex;
    padding: 16px;
    align-items: center;
}

So the style 276 gets wrong priority. Any ideas how to fix this? Is there a way to provide the priority?

Material UI 4.2.0

I'm still experiencing the issue @OnkelTem posted above
Using Material UI 4.8.3.

on 4.9.5 and started seeing this in the last week @oliviertassinari

@kelly-tock @razvanmandache could you please provide a repro?

https://github.com/mui-org/material-ui/issues/16609#issuecomment-528325054

seems to have fixed it for us.

had this issue in storybookjs as well, and the optimization fix from above fixed it there as well.

@kelly-tock it’s great that you’ve found a workaround but ideally it would be good to get more insight into the issue so that the rest of the community doesn’t have to resort to turning optimisations off.

I will give it a shot. I can very easily try and see if it was also happening before 4.9.5 as we just upgraded to that

Having a similar issue onn version 4.9.14 (currently latest) the Box component with clone attribute only overrides the child's styles on develop mode.

Code:

// import condition matters! 
import { Box, Card } from '@material-ui/core';

const ConnectedApp = () => {
  return (
    <Box clone bgcolor="primary.light">
      <Card>Foo</Card>
    </Box>
  );
};

Dev Output:

<style data-jss="" data-meta="MuiPaper">
.MuiPaper-root {
  color: rgba(0, 0, 0, 0.87);
  transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
  background-color: #fff;
}
<!-- ...rest -->
</style>

<style data-jss="" data-meta="MuiBox">
.MuiBox-root-2 {
    background-color: rgb(121, 134, 203);
}
</style>

<div id="app"><div class="MuiPaper-root MuiCard-root MuiBox-root MuiBox-root-2 MuiPaper-elevation1 MuiPaper-rounded">Foo</div></div>

Prod Output:

<style data-jss="" data-meta="MuiBox">
.jss2 {
    background-color: rgb(121, 134, 203);
}
</style>
<style data-jss="" data-meta="MuiPaper">
.MuiPaper-root {
  color: rgba(0, 0, 0, 0.87);
  transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
  background-color: #fff;
}
<!-- ...rest -->
</style>

<div id="app"><div class="MuiPaper-root MuiCard-root MuiBox-root jss2 MuiPaper-elevation1 MuiPaper-rounded">Foo</div></div>

Here you can see the dev build https://codesandbox.io/s/keen-mclaren-ne8qm

This issue also appears when using Drawer component and parceljs tree-shaking. The ordering of CSS is totally random.

Was this page helpful?
0 / 5 - 0 ratings