Preface
I asked a similar question to this several days back and while related in nature I believe the solution will ultimately be different, so I am asking again in a different thread.
CodeSanbox Example (Has Been updated to reflect the accepted answer)
The issue:
I'd like any external styles passed in with the className
prop to have higher specificity than my custom components internal style. That way someone using it can adjust margins and padding. However, my components default internal style is overwriting my external style and I would like it to be the other way around.
The Details:
I am creating a custom component library built on top of material-ui. I'd like to make the custom components api similar to @material-ui
so that our devs will find them easier to use. Each component I am building has it's own internal style overwriting the default material-ui styles in this case it is defined as class button
. Additionally, like @material-ui
I am accepting a color prop <TestButton color={'default'}/>
. Finally, I'd like my custom button to be allowed to be overwritten with external styles if the need ever arises. I am using the clsx
library to build the className strings.
The Code:
import React, { useState } from "react";
import { makeStyles } from "@material-ui/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import { Button } from "@material-ui/core";
import clsx from "clsx";
const useAppStyles = makeStyles({
gButton: { margin: "150px" }
});
export default function App() {
const classes = useAppStyles();
return (
<div className={classes.example}>
<div className={classes.separator}>
<div>Buttons:</div>
<TestButton
className={classes.gButton}
color={"default"}
>
Default
</TestButton>
<TestButton
className={classes.gButton}
color={"primary"}
>
Primary
</TestButton>
</div>
);
}
function TestButton(props) {
const classes = GrangeButtonStyles();
let color = props.color === 'default' ? classes.default : classes.primary
const GrangeButtonStyles = makeStyles({
button: {
height: "45px",
padding: "13px 30px 13px 30px",
borderRadius: "5px",
border: "none",
margin: "15px",
},
default: {
backgroundColor: "black",
border: 'solid #2e7d32 1px',
color: "white",
},
primary: {
backgroundColor: 'white',
color: 'black',
fontFamily: 'Montserrat, sans-serif',
border: 'solid black 1px',
}
});
return (
<Button
className={clsx(classes.button, color, props.className)}
variant="contained"
disabled={props.disabled}
disableElevation
>
{props.children}
</Button>
);
}
NOTE:
I have simplified the code greatly for space in this question and in the code sandbox example. Please don't comment that you think what I'm doing doesn't make sense because of the example.
makeStyles
calls is dependent on the order of the style sheets in the<head>
. This is determined by the order in whichmakeStyles
is called. Later calls will be later in the<head>
and thus have higher specificity. If you put your custom components in files of their own, theirmakeStyles
will generally be called first due to importing them before using them and the specificity should be as desired. – Forecastclasses.container
with a jss generated class name it will "always" work. – PomicultureTestButton
and an example of its use with external styles should be sufficient to show the problem. – Forecastclasses
is how you surgically target stuff in a way that won't break tomorrow. If you're building a component library, understanding this should be high priority - passingclassName
around won't cut it. – PomicultureclassName
it isn't going to be any more fragile than doing the same customizations usingclasses
. – Forecastroot: { '& > p > span:first-child': { ... some jss } }
might work.... fragility? From the OPs own question: I'd like to make the custom components api similar to @material-ui so that our devs will find them easier to use Just use classes the way they were intended rather than trying to avoid understanding them. – PomicultureclassName
since that is what styled-components injects to the styled component. So targeting nested classes would then be done via something like& > .MuiButton-label { ...css }
. – ForecastmakeStyles
from within the button component itself. Seems that it would then call the internalmakeStyles
after the internal componentmakeStyles
. I appreciate your help and you input on the load order ofmakeStyles
. Would submit that advise as an answer? I'd like to give you the credit for it. – OrgelmakeStyles
in the component so you can point that out. – Orgel