Styled Components - extending element styling and overriding properties - reactjs

I'm using a pre-existing styled button component and the majority of the original styling, however the 'top' and 'right' properties need updating.
I have tried the following:
const StyledButton = styled(Button)`
right: -5px;
top: 40px;
`;
I thought this would extend the styling to the original component, however it doesn't seem to be feeding through. Any help would be much appreciated.

Often, in this case, it's simply a matter of the underlying component not passing on the className prop. I find that, with components that are going to be shared/re-used, it can be helpful in their root element to have {...props} to ensure people can override whatever they need to on that element.
e.g.
const Button: React.FC = ({ text, ...props }) => {
return <button {...props}>{text}</button>
}

Related

Material UI - Overriding styles using Root & using ClassName

I'm new to material UI. While learning, I came to know out that styles of a material UI can be overridden with the rule name of the classes.
If I have an element - MenuItem where I just need to change the default styling of the text (such as fontFamily, fontWeight, fontSize)
According to the documentation available here https://material-ui.com/api/menu-item/ I used makeStyles hook and have overridden some properties of the root element of Menu-Item
Sample code
const useStyles = makeStyles((theme) => ({
menuItem: {
fontFamily: "Raleway",
}
}));
JSX code: <MenuItem onClick={handleClose} component={Link} to="/services" classes={{root: classes.menuItem}}>Services</MenuItem>
In one more tutorial, I found another way of overriding - with className like
const useStyles = makeStyles((theme) => ({
menuItem: {
fontFamily: "Raleway",
}
}));
JSX code: <MenuItem onClick={handleClose} component={Link} to="/services" className={classes.menuItem}>Services</MenuItem>
My question lies in this part className={classes.menuItem} and classes={{root: classes.menuItem}}
On using root -> I see the css properties getting added to the root element but on using className={classes.menuItem} I see a new class is added to the DOM. But Is there any difference with respect to the behaviour of the app between these 2 methods or is it just another way of doing it?
Thanks
From what I can tell having worked with Material UI, the difference is with what you're trying to do.
className={classes.menuItem} and classes={{root: classes.menuItem}} will both get you the results that you're expecting, as you've already discovered.
The distinctions are important when you start building custom themes, because Material UI allows you to apply classes to the components as a whole, meaning potentially comprised of other components, or pass along styles to the root elements if you need a higher specificity.
For example, consider the Stepper component.
classes={{root: classes.menuItem}} will override the root in the component, applying your styles.
className={classes.menuItem} will add an additional class to the component, leaving the underlying styles intact.

Styling nested components using styled components

I've been using styled components for very little time.
At the moment I'm trying to do a style override on a nested element and I've having trouble understanding what I'm doing wrong.
So my struture is.
---------------------------Form.js---------------------------
import { FieldWrapper } from './FieldWrapper';
const Form = styled.form`
/** my form styles **/
`;
const StyledFieldWrapper = styled(FieldWrapper)`
/** my FieldWrapper styles **/
input {
/** input overrides **/
padding-bottom: 0.8rem;
height: 2rem;
line-height: 2rem;
box-sizing: content-box;
background-color: pink !important; // added just for visibility
}
`;
const MyForm = props => {
return (
<>
<Form>
<StyledFieldWrapper />
</Form>
</>
);
}
export { MyForm }
---------------------------FieldWrapper.js---------------------------
const Div = styled.div`
/** my div styles **/
`;
const Label = styled.label`
/** my label styles **/
`;
const Input = styled.input`
/** my input styles **/
`;
const FieldWrapper = props => {
return (
<Div>
<Label>
<Input />
</Label>
</Div>
);
}
export { FieldWrapper }
Now what I expect to happen was that the styles in FieldWrapper.js would be overriden by the StyledFieldWrapper element in Form.js, this however does not happen and I have no idea why. I have overrides like this in the past and in this project. Also StyledFieldWrapper does not contain only overrides, it also has style of its own and I can't even see those.
Has anyone had a similar issue?
NOTE: I have tried to use the solution in Styling Nested Components in Styled-Components with no success.
EDIT:
Since you want the styles to apply to a custom component, you also need to manually assign the className generated by styled-components to the top-level element of that component. Something like:
const FieldWrapper = props => {
return (
<Div className={props.className}>
<Label>
<Input />
</Label>
</Div>
);
}
The problem is likely related to CSS Specicifity, meaning that the original css styles defined in FieldWrapper has higher "importance" than the ones in Form. If you inspect your element you can probably see that both styles are applied, but the former has precendence over the latter.
A way to solve that is to either use the !important rule to each of the input styles defined in your Form component. Another would be to add a class to <Input /> and define your styles as myClass.input. Basically anything that would increase the specicifity of the rules you want to apply.
See the above link for more info of how to do that. Also check out Cascade and inheritance:
Once you understand the fact that source order matters, at some point you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting, rule is applied. This is because that earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.
As we saw earlier in this lesson, a class selector has more weight than an element selector, so the properties defined on the class will override those applied directly to the element.
Something to note here is that although we are thinking about selectors, and the rules that are applied to the thing they select, it isn't the entire rule which is overwritten, only the properties which are the same.
The amount of specificity a selector has is measured using four different values (or components), which can be thought of as thousands, hundreds, tens and ones — four single digits in four columns:
Thousands: Score one in this column if the declaration is inside a style attribute, aka inline styles. Such declarations don't have selectors, so their specificity is always simply 1000.
Hundreds: Score one in this column for each ID selector contained inside the overall selector.
Tens: Score one in this column for each class selector, attribute selector, or pseudo-class contained inside the overall selector.
Ones: Score one in this column for each element selector or pseudo-element contained inside the overall selector.
Here is an example from MDN:
/* specificity: 0101 */
#outer a {
background-color: red;
}
/* specificity: 0201 */
#outer #inner a {
background-color: blue;
}

Any way to apply global styles for html tags(ul, li, select, a .. ) based on props?

Wanted to know if there's any way to add styles to tags like ul, li based on props. Using styled components i can create wrapped components, but i wanted to of alternate methods
i wanted to know if we can achieve something like this
"li": {
color: (props) => props.color
}
Thanks!
You can do that using classnames

How to hide MUI React ListItem?

I have the following:
<ListItem key={name} hidden={true} aria-hidden={true}>
name
</ListItem>
but the ListItem is still showing up. How can it be hidden?
As far as I know, there is no hidden props on the ListItem component in Material-UI, so you will have to implement you own behavior to hide the ListItem :
You can not render the concerned ListItem at all.
You can render it but hide it using some CSS. See How to display and hide a div with CSS?.
I was looking to programmatically hide a Material-UI FormControl component, and found the same issue (i.e. the lack of a hidden prop).
What worked for me was to add a method that returned the appropriate class string, based on whether I wanted to show the component in question or not.
For example, with styles like:
const styles = createStyles({
...
formcontrol: {
minWidth: 120,
margin: 10
},
invisible: {
visibility: "hidden"
},
});
I added this to my component class:
getStyle() {
let cls: string;
if (this.props.whatever) {
cls = this.props.classes.formcontrol;
} else {
cls = this.props.classes.invisible + " " + this.props.classes.formcontrol;
}
return cls;
}
And then reference that from render() when creating the component I want to sometimes hide:
<FormControl className={this.getStyle()}>
...
</FormControl>
This should work for any styled MUI component.
(Side-note: the display prop appears from the docs to do this, but didn't work for me. Perhaps it works only for Box components, which happen to be what's used in all of the examples in the docs. This is worth further investigation which I have not yet spent the time to do.)

How can I access to change styles of inner items of a component in react and material ui?

How can I access to inner css classes of children of a component and add or change styles to/of them? like I want to change and customize material ui stepper steps circle font size and color and so on.
How can I write css classes like bellow:
.stepper circle {
font-size:18px;
}
or
.stepper .text {
font-size:18px;
}
thanks.
Thanks to #spakmad's answer, but that's not exactly what I meant, maybe my question was not clear enough. I meant how to write above mentioned CSSs in material ui object style classes format(classes injected with withStyle HOC).
I found my solution:
stepper:{
'& circle':{
fontSize: '18px'
}
}
and
stepper: {
'& .text': {
fontSize: '18px'
}
}
The very straightforward way to do it without having to worry about classes is by using the material-ui style prop. Like so,
<Stepper style={{ fontSize: '18px' }} />
This applies a style to the root element, which I assume to be a div or container of sorts, although it probably varies by the component.
More specifically, and what you probably want to do is use material-ui classes prop. That is, since you know exactly what classes you want to override, you can do so as follows,
<Stepper classes={{ text: { fontSize: '18px' }}} />
I use text here as a classname because in the question, .text appears to reference and already existing class. Since you're trying to increase the size of the font in this svg that comes with the Stepper. You'll need to get the class name applied to the svg and override it. In this case, one of its classnames is MuiSvgIcon-root-184 so you would expect to be able to do,
<Stepper classes={{ MuiSvgIcon-root-184: { fontSize: '18px' }}} />
This classname however is generated by material-ui dynamically based on the layout resulting from props and could be different across all renders.
TLDR
So, trusty className component and writing some css and component JSX as follows.
<Stepper className={'customStepper'} />
.customStepper svg: {
font-size: '18px !important',
}
You need to include !important to make sure this style is respected. Material UI by default puts its styles last in the document so they're normally overwriting anything else, but the !important spec should override the override.

Resources