Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

material-ui and styled override specificity issue #1253

Closed
pavel06081991 opened this issue Oct 19, 2017 · 26 comments
Closed

material-ui and styled override specificity issue #1253

pavel06081991 opened this issue Oct 19, 2017 · 26 comments

Comments

@pavel06081991
Copy link

pavel06081991 commented Oct 19, 2017

I'm using material-ui(https://material-ui-next.com/) components library. In this library there is for example component Typography for rendering text, it supports className to override styles. I'm using styled function from styled-components to override default styles of Typography component like this:

const StyledTypography = styled(Typography)`
	color: palevioletred;
	font-weight: bold;
        any other styles
`;

but some styles are not overridden because of specificity. Maybe because material-ui library adds style tag to the end of head tag after styled-components does the same. I did not find a way except of using !important to override styles. Maybe there is some way to avoid using !important?

@mxstbr
Copy link
Member

mxstbr commented Oct 19, 2017

You hit the nail on the head with your analysis, this in indeed because material-ui injects their styles after the styled-components ones. As you also said, the way to work around this is to increase the specificity, which you could do with !important but that's a pain the butt.

I'd probably use this hack:

const StyledTypography = styled(Typography)`
  && {
    color: palevioletred;
    font-weight: bold;
  }
`;

Each & gets replaced with the generated class, so the injected CSS then looks like this:

.StyledTypography-asdf123.StyledTypography-asdf123.StyledTypography-asdf123 {
    color: palevioletred;
    font-weight: bold;
}

The repeated class bumps the specificity high enough to override the source order without being very tedious to write! 🎉

@mxstbr mxstbr closed this as completed Oct 19, 2017
@pavel06081991
Copy link
Author

@mxstbr, thanks a lot. Lots of my colleagues faced with this problem and used hacks like this one #860 (comment)

I could not find your option anywhere on internet and in styled-components documentation. So maybe you could add it to docs?

P.S. thanks for react-boilerplate

@mxstbr
Copy link
Member

mxstbr commented Oct 19, 2017

Yep, a PR to add a FAQ entry ("How to override styles?" maybe? Or "How to work around specificity issues?"? Dunno) would be very welcome: https://github.com/styled-components/styled-components-website

@unutoiul
Copy link

unutoiul commented Nov 1, 2017

How can we export the styles from the component.jsx in a separate scss file and then import them with styled?

@unutoiul
Copy link

unutoiul commented Nov 1, 2017

also how can you get the material UI theme into my styled?

@adamchenwei
Copy link

@mxstbr pretty cool and out-of-the-box hack there! love it!

@oliviertassinari
Copy link

We have added a documentation section for using styled-components with Material-UI. The demo shows how to configure JSS to avoid the !important issue.

@kristojorg
Copy link
Contributor

kristojorg commented Dec 28, 2017

If anybody is running into this issue, and can't use the jss comment solution, here is a codemod to add specificity to all styled-components:

styled-specificity-codemod.js

For the record, I ran into this problem because I use Gatsby, which renders the base html document template in react, and therefore I couldn't find any way to add a <-- jss-insertion-point --> html comment. Instead, I codemoded all my files to add specificity to the generated styled-components css. I haven't thoroughly tested the script, but it worked for me so far!

@oliviertassinari
Copy link

@kristojorg I'm happy to hear you found a solution with Gatsby. I wish we had an example along side Next.js in the repository.
Regarding not beeing able to provide the injection point. I was facing the same issue with codesandbox and with Next.js. You have a different solution available. You can add the HTML comment in JavaScript before the application render.

@rpellerin
Copy link

rpellerin commented Jan 4, 2018

Hi! How can this be automated, meaning all styled components get a greater specificity? I've been looking at doing this through a babel plugin but could not find how.
Writing && { ... } for each component is pretty tedious and it reduces code readability.
Thanks!

@adamchenwei
Copy link

@oliviertassinari
Copy link

By the way, you can get a styled components API like with Material-UI. It only takes a few lines of code: https://twitter.com/olivtassinari/status/946517606009274369

@techniq
Copy link

techniq commented Jan 4, 2018

@oliverturner can something like this be used to pass styles to an inner component. For example, how could you pass width to the paper element within Drawer from a variable (which might be dynamic, provided by a higher up component)?

For example, this kind of works (and 240 could have been read from props or a global variable). I didn't know if there is a better approach using JSS / Material-UI styling?

I ask because I'm working on creating a component to help orchestrate the Drawer and AppBar (show/hide when menu icon is pressed, show/hide based on media query, etc). I have this working using glamorous, but didn't want to force people to bring in this dependency unnecessarily.

@oliviertassinari
Copy link

@techniq Material-UI supports a global CSS mode, with explicit deterministic class names. Maybe it can help too.

@techniq
Copy link

techniq commented Jan 4, 2018

I saw that with the warnings

image

It's really painful (unless I'm missing something) just to set the width of a Drawer. It looks like I'll also need to grab all the original styles for paper for when I override the classes.paper classname?

@oliverturner
Copy link

@techniq I think you meant to reference @oliviertassinari rather than me 😉

Nevertheless... would this work?

const StyledDrawer = styled(Drawer)`
  & > .paper {
    width: 240px;
  }
`;

@techniq
Copy link

techniq commented Jan 5, 2018

@oliverturner oops, my bad 😄

Regarding your example, since the paper classname is dynamically generated by JSS (it's not className="paper" but something like MuiDrawer-1234 in dev and jss1234 in prod) this would not work, but if you declare paper to use a static classname, you could. This is an idea I had when using glamorous...

const StyledDrawer = glamorous(Drawer, { withProps: { classes: { paper: 'paper' }}})
({
  '& .paper': {
    width: 240,
    backgroundColor: 'blue',
  }
});

which something similar could be done using styled-components attrs I image, or you can declare your component dynamically, such as this (which was my original approach).

const StyledDrawer = glamorous(({ className, ...props }) => (
  <Drawer className={className} classes={{
    paper: 'paper'
  }} {...props} />
))({
  '& .paper': {
    width: 240,
    backgroundColor: 'green',
  }
});

if interested, you can see some of the discussion regarding this approach on this glamorous issue.

I was hoping to find something similar using Material UI / JSS out of the box for a component I looked to publish to npm and didn't want to bring in a dependency others might not already be using/want (glamor/glamorous/styled-components).

Anyways, this is pretty off-topic on this issue at this point. Sorry for all the noise.

@Danilo-Araujo-Silva
Copy link

Danilo-Araujo-Silva commented Jan 10, 2018

Looks like we have 3 ways (could be easier, but not everything is flowers) to override Material UI styles with Styled Components. Here is my Gist.

@rocketraman
Copy link

I know this is a little off-topic for styled-components, but I'll add one more note that I didn't see anyone point out above...

In some cases, the root of the material-ui component is not the one you want to override, and therefore one cannot use the && trick (see relevant comment from @oliviertassinari here: mui/material-ui#9978 (comment)).

For example, for the material-ui Drawer, paper is not the root element and therefore this does not work:

const StyledDrawer = styled(Drawer)`
  && {
    position: relative;
    height: 100%;
    overflow: hidden;
  }
  width: ${props => props.drawerWidth}px;
`

In these cases one has to use the method 1 from @Danilo-Araujo-Silva 's gist. This works:

const StyledDrawer = styled(({ drawerWidth, ...rest }) => <Drawer {...rest} classes={{paper: 'paper-override'}} />)`
  & .paper-override {
    position: relative;
    height: 100%;
    overflow: hidden;
  }
  width: ${props => props.drawerWidth}px;
`

You can even add a withTheme wrapper to that to get access to the material-ui theme as prop.theme.

@wmertens
Copy link
Contributor

wmertens commented Feb 12, 2018 via email

@rocketraman
Copy link

Can't the Paper component be interpolated?

@wmertens What do you mean? Do you mean:

paper {
  ...
}

If so, no, because the actual class names are generated at runtime by JSS.

@wmertens
Copy link
Contributor

wmertens commented Feb 12, 2018 via email

@johncmunson
Copy link

I see styled-components and/or emotion as pillars of any React based UI. In my mind, the fact that material-ui doesn't jive well with either of these two libraries means that it is completely ruled out of selection for almost any serious project. I've had far more success by using a combination of Rebass, styled-system, and styled-components.

Still want that "material" look? Fine. Material Design is just a design system/philosophy after all, and the stack mentioned above gives you the primitives to implement Material Design on your own.

@oliviertassinari
Copy link

oliviertassinari commented May 16, 2019

@johncmunson This problem should already be solved in Material-UI v4: the examples.

import styled from 'styled-components';
import { TextField } from '@material-ui/core';

const StyledTextField = styled(TextField)`
  label.focused {
    color: green; 💚
  }
  .MuiOutlinedInput-root {
    fieldset {
      border-color: red; 🧡
    }
    &:hover fieldset {
      border-color: yellow; 💛
    }
    &.focused fieldset {
      border-color: green; 💚
    }
  }
`;

We will continue the styled-components effort in v5: mui/material-ui#6115.


Do you have an example of frustration with v3? I want to make sure it's correctly handled in v4.

@igortas
Copy link

igortas commented Jan 31, 2022

@johncmunson This problem should already be solved in Material-UI v4: the examples.

import styled from 'styled-components';
import { TextField } from '@material-ui/core';

const StyledTextField = styled(TextField)`
  label.focused {
    color: green; 💚
  }
  .MuiOutlinedInput-root {
    fieldset {
      border-color: red; 🧡
    }
    &:hover fieldset {
      border-color: yellow; 💛
    }
    &.focused fieldset {
      border-color: green; 💚
    }
  }
`;

We will continue the styled-components effort in v5: mui-org/material-ui#6115.

Do you have an example of frustration with v3? I want to make sure it's correctly handled in v4.

v5 mui has issue with specificity, does not override theme settings, when u create styled components on top of already defined base component with global theme.

const StyledInput = styled(TextField)(({ theme }: { theme: Theme }) => {
  console.log(theme)
  return {
    '& .MuiOutlinedInput-root': {
      '& fieldset': {
        borderColor: 'red',
      },
      '&:hover fieldset': {
        borderColor: 'yellow',
      },
      '&.Mui-focused fieldset': {
        borderColor: 'green',
      },
    },
  };
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests