MUI v5 - Add component name to `styled` class names? - reactjs

I did some search in github and here, but maybe I'm doing it the wrong way.
Using styled from #mui/material/styles generates random class names like this:
const TitleWrapper = styled('div')`
display: flex;
justify-content: space-between;
margin-bottom: 2rem;
`
Sometimes it becomes hard to debug the app when we don't know where the rendered component came from.
There's an option parameter that can be passed to styled with a label prop that adds a suffix to the class:
const TitleWrapper = styled('div', { label: 'TitleWrapper' })`
display: flex;
justify-content: space-between;
margin-bottom: 2rem;
`
Is there an automated way of adding that suffix to identify the component? It's really painful to do this in every single component.

The solution I found was to update my babel.config.js file with the following changes
plugins: [
[
'#emotion',
{
autoLabel: 'always',
importMap: {
'#mui/material': {
styled: {
canonicalImport: ['#emotion/styled', 'default'],
styledBaseImport: ['#mui/material', 'styled'],
},
},
},
},
],
'#babel/plugin-transform-runtime',
],
I hope this is useful to someone who faces the same issue.

In order to get a custom class name i would like to suggest you to use scss (your own custom classes and styles), then you can put every style you need there , actually that's what i am doing right now, except when i need some styles based on some dynamic data then i can use both: my custom scss classes and styled-component class

Related

Styled-component warning suggesting I use `attrs` method even though I am?

I had a styled component that was rendering 3 times, each having a parallax effect.
const ParallaxContainer = styled.section`
position: absolute;
left: 0;
right: 0;
height: 100%;
opacity: 0.3;
bottom: ${props => props.bottomValue}px;
`;
The parallax is achieved by updating the bottom value every scrollEvent through a pretty expensive calculation. Meaning, this component is getting re-rendered very often.
Unsurprisingly, I was getting the warning Over 200 classes were generated for component styled.section. Consider using the attrs method, together with a style object for frequently changed styles. So I tried to follow the advice, and refactored the component to this:
const ParallaxContainer = styled.section.attrs(
({ bottomValue }) => ({
style: {
bottom: bottomValue + "px"
}
})
)`
position: absolute;
left: 0;
right: 0;
height: 100%;
opacity: 0.3;
`;
But I am still getting the same error. What am I doing wrong?
Sandbox demonstrating my issue: https://codesandbox.io/embed/styled-components-is-yelling-at-me-attrs-zhnof
The problem is the lines you left commented out in your style. Remember, your style is just a template string. The CSS-style commented lines are only ignored after the string has been interpolated and passed to Styled Components.
The Over 200 classes error happened in the first place because the style string needed to be re-computed on every scroll event, which results in a completely new styled component instance. Moving it to attrs is like defining the style in JS in the old-fashioned React way, so these styles don't go through Styled Components.

Creating specificity in Material UI Next and React

I'm trying to create a class specificity in Material UI Next same way as they allow you to use pseudo classes, but for nested elements. For example, in Material UI Next (MUI-Next) I can create a class with styles in it:
const styles = {
appbar: {
background: '#6d6146',
'&:hover': {
background: '#9e8e6a',
},
},
};
and use it this way
<Toolbar className={classes.appbar}>
... blah blah blah
</Toolbar>
That paints my Toolbar element in color #6d6146 and hovers to #9e8e6a.
Now, if imagine I have some elements inside the Toolbar and I don't want to create a class for every single element in it. (especially if they are not MUI-Next elements, but some custom HTML) For the sake of an example, a hyperlink. Like this:
<Toolbar className={classes.appbar}>
<a href="www.google.com">
<span>Title</span>
</a>
</Toolbar>
Yes, there are ways to do this particular example correctly using MUI control properties, remember this is an example. Real world code is very complex and lots of code.
I would like to access that hyperlink by way of specificity using the main parent class as a hook class. The desired rendered css would look like this:
.appbar {
background: #6d6146;
}
.appbar:hover {
background: #9e8e6a;
}
.appbar a{
color: #d63302;
}
My attempt to create specificity is not working. This is what I tried:
const styles = {
appbar: {
background: '#6d6146',
'&:hover': {
background: '#9e8e6a',
},
'a': {
color: '#d63302',
},
},
};
According to how MUI-Next handles pseudo-classes to create specificity, this element specificity should work, but doesn't. Can this be done and I am not using the right syntax, or is this not supported?
Remember this is Material UI Next found here. Completely different than the older Material UI.
Here is a playground for ya. Thanks in advance.
SAMPLE CODE
Try this :
appbar: {
background: "#6d6146",
"&:hover": {
background: "#9e8e6a"
},
"& a": {
color: "black",
"&:hover": {
color: "red"
}
}
}
Working link

css modules & cssnext custom properties in react + webpack

I am just wondering what would be the best approach to using cssnext custom properties like these, alongside css modules in react.
Is there a way to share these across modules ?
:root{
--primary: pink;
--fontSize: 1rem;
--fullWidth: 100%;
--color: red;
--gutter: 1.618rem;
}
#custom-media --small-viewport (max-width: 30em);
#custom-media --large-viewport (min-width: 75em);
#custom-media --only-medium-screen (width >= 500px) and (width <= 1200px);
EDIT: *** ok i tried this, thought it worked but hasn't
:global(:root) {
--primary: pink;
--fontSize: 1rem;
--fullWidth: 100%;
--color: pink;
--gutter: 1.618rem;
}
CSS Modules should only handle selectors that are classnames (that start with a dot). So it should not be an issue and you should be able to use those custom definition as soon as they are in the file. You can use postcss-import to inline your file that contains global definitions.
Another solution is to define this global values using postcss plugin options:
https://github.com/postcss/postcss-custom-properties#variables
https://github.com/postcss/postcss-custom-media#extensions
Because the postcss-loader only transforms a single file at a time you must import your custom properties, e.g.
#import './root.css';
.foo {
color: var(--primary);
}

CSS Module composition

I've got a question about composition I'm hoping someone can help me with.
I'm using react-css-modules with Sass, and I'd like to know the best way to compose things for one of our basic bottom-level components.
Here's our component:
import React, {PropTypes} from 'react'
import cssModules from 'react-css-modules'
import styles from './style.sass'
const Button = ({children = 'Submit', ...props}) => {
const align = props.align ? `-${props.align}` : ''
const type = props.type ? `-${props.type}` : ''
const styleName = `button${type}${align}`
return (
<button onClick={props.onClick} {...props} styleName={styleName}>
{children}
</button>
)
}
Button.propTypes = {
align: PropTypes.string,
onClick: PropTypes.func.isRequired,
type: PropTypes.string,
}
export default cssModules(Button, styles)
And here's the stylesheet so far:
#import "~components/styles/variables"
.button
color: $button-default
background-color: transparent
font-family: $font-family
font-size: $default-font-size
font-weight: $font-regular
line-height: $default-button-height
margin: 0 $pad 0 0
outline: none
padding: 0 $pad*2
.left
float: left
.right
float: right
.primary
color: $background-interaction
background-color: $button-default
.button-left
composes: button, left
.button-right
composes: button, right
.button-primary
composes: button, primary
.button-primary-left
composes: button, primary, left
.button-primary-right
composes: button, primary, right
Right now, it's pretty painful. Every configurable prop we add exponentially increases the number of composed classes we have to provide. We can currently configure align and type, and since both can be null we have 6 possible combinations, so 5 composed classes to create in addition to the base .button.
If we added just one more prop, say just a boolean bold, we now have to add a whole bunch of new composed class names: .button-bold, .button-left-bold, .button-right-bold, .button-primary-bold, .button-primary-left-bold, .button-primary-right-bold.
I know with react-css-modules we can just enable the allowMultiple setting to allow us to specify multiple modules to apply to an element, but my understanding is that is against best practices. I feel like we have to be missing something here. What are we doing wrong?
I think maybe there's a mix of concerns in the example, and that's why it doesn't fit the "one class per element" rule. One of the reasons to enforce the one class per element rule is so that we can easily change the appearance of an element's state without actually touching the element. (Basically the promise of CSS itself, finally realized.) If you have multiple classes on an element, it's difficult to control the appearance without changing the element. This is especially true if you use appearance (rather than semantic) classes.
A class like "button-primary-left-bold" has some semantic meaning ("button-primary"), but it also has some layout meaning ("left") and some text appearance meaning ("bold"). A Button component probably has no business controlling its own layout. Remember, you can compose React components too! So you can have something more like:
<Left><Button type="primary">Click Me!</Button></Left>
Now the CSS module for the Button component can worry just about the types of buttons. And the Button component can be used anywhere, with any layout, not just a float-based layout.
Even better, the float could be pushed into a more semantic component as well. Perhaps something like:
<ButtonBar>
<Button>Cancel</Button>
<Button type="primary">Save</Button>
</ButtonBar>
The ButtonBar can have its own CSS module that does the layout. And later you can swap out that janky float layout for a swanky flexbox layout. :-)
Your "bold" modifier example certainly has no place inside the Button component. Better to think about why something is bold, and then make a component or semantic property for that. Otherwise, if the design calls for changing those "bold" buttons to "italic green" buttons, you have to go around changing a bunch of elements.
If you do this (exchange visual/layout classes for semantic components and break up components and CSS modules), you'll have less "exponential increases". If you do end up in a situation where you really do have a need for multiple semantic properties being combined, there is still some value in having those states be spelled out. A good example might be "primary-disabled". This is better than "primary disabled" for a couple of reasons. First, it's easy to look in the CSS module and see the explicit state listed. Second, there aren't ambiguous uses and relationships of those classes. Is "primary disabled" actually a valid use of those classes? That can only be known if somebody documents that use. The "disabled" class may override something in the "primary" class, meaning there are implicit ordering dependencies. It's easy for a well-meaning edit in the future to break things, because the relationship between those classes isn't obvious. As is often the case, choosing something implicit to save some keystrokes can lead to subtle errors. Paying that bit of keystroke tax locks things in and makes it obvious what will work and what won't.
Another small point that will save you a few keystrokes: there's really no reason for the "button-" prefix. That's exactly what CSS modules are for. Do this instead:
.normal
color: $button-default
background-color: transparent
font-family: $font-family
font-size: $default-font-size
font-weight: $font-regular
line-height: $default-button-height
margin: 0 $pad 0 0
outline: none
padding: 0 $pad*2
.primary
composes: normal
color: $background-interaction
background-color: $button-default
The filename itself essentially is the "button" prefix.
I think I'd move away from compose in this case and nest your classes. Here's my suggestion (pardon me if my jsx is a tad off):
import React, {PropTypes} from 'react'
import cssModules from 'react-css-modules'
import styles from './style.sass'
const Button = ({children = 'Submit', ...props}) => {
const align = props.align ? `${props.align}` : ''
const type = props.type ? `${props.type}` : ''
const styleName = `button ${type} ${align}`
return (
<button onClick={props.onClick} {...props} styleName={styleName}>
{children}
</button>
)
}
Button.propTypes = {
align: PropTypes.string,
onClick: PropTypes.func.isRequired,
type: PropTypes.string,
}
export default cssModules(Button, styles)
And the SASS:
#import "~components/styles/variables"
.button
color: $button-default
background-color: transparent
font-family: $font-family
font-size: $default-font-size
font-weight: $font-regular
line-height: $default-button-height
margin: 0 $pad 0 0
outline: none
padding: 0 $pad*2
&.left
float: left
&.right
float: right
&.primary
color: $background-interaction
background-color: $button-default
Fully acknowledging that "left" and "primary" could conflict with other class names in your app. So it might not be a bad idea to come up with some slightly better (more scoped) names.

Using Font Awesome in ui.bootstrap.rating

How can I use Font Awesome in ui.bootstrap.rating?
I found out, that when I add state-on="'fa-star'" state-off="'fa-star-o'" to and changed class="glyphicon" to class="fa" in ui-bootstrap-tpls it works.
But I guess there is a more custom way to change the class of the icons.
Yeah as you are doing with setting state-off and state-on is their recommended manner. If you are going to have lots of the ratings on a page, I would just create a custom template and over-ride the stock template. Here is a post custom templates
I had Font Awesome and so didnt want to include Glyphicons.
uib.bootstrap Version: 1.3.3 - 2016-05-22 uses limited Glyphicons, so this is what i added to my css
.glyphicon {
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.glyphicon-star:before {
content: "\f005";
}
/**
copied from
.fa-star:before {
content: "\f005";
}
*/
.glyphicon-star-empty::before {
content: "\f006";
}
.glyphicon-chevron-right:before {
content: "\f054";
}
.glyphicon-chevron-left:before {
content: "\f053";
}
.glyphicon-chevron-up:before {
content: "\f077";
}
.glyphicon-chevron-down:before {
content: "\f078";
}
i.e. added css from Font Awesome 4.6.3 to appropriate glyphicon names
Now i dont know if this code will break on version of Font Awesome

Resources