CSS module composition

I have a question about composition. I hope someone can help me. I am using react-css-modules with Sass, and I would like to know how best to create things for one of our lower level core components.

Here is 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 is the stylesheet:

 @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. Each custom prop we add exponentially increases the number of compiled classes that we need to provide. Currently, we can adjust align and type , and since both can be zero, we have 6 possible combinations, so we will create 5 composed classes in addition to the .button base. If we added only one option, say only logical bold , now we have to add a whole bunch of new folded class names: .button-bold, .button-left-bold, .button-right-bold, .button-primary-bold, .button-primary-left-bold, .button-primary-right-bold .

I know that with react-css-modules we can simply enable the allowMultiple parameter allowMultiple that we can specify several modules to apply to the element, but I understand that this is contrary to best practices. I feel like we have to skip something here. What are we doing wrong?

+6
source share
2 answers

I think that in this example there can be many problems, and why it does not comply with the "one class per element" rule. One reason for applying the rule of one class to an element is that we can easily change the appearance of the state of an element without affecting the element. (In principle, the promise of CSS itself is finally realized.) If you have several classes on an element, it is difficult to control the appearance without changing the element. This is especially true if you use classes (rather than semantic).

A class such as "button-primary-left-bold" has a semantic meaning ("button-primary"), but it also has a layout value ("left") and a text value ("bold"). The Button component probably does not have a business controlling its own layout. Remember that you can also create React components! So you can have something more:

 <Left><Button type="primary">Click Me!</Button></Left> 

The CSS module for the Button component can now worry about button types. And the Button component can be used anywhere, with any layout, and not just with a floating point.

Even better, the float can be added to a more semantic component. Perhaps something like:

 <ButtonBar> <Button>Cancel</Button> <Button type="primary">Save</Button> </ButtonBar> 

ButtonBar can have its own CSS module that executes the layout. And later you can change this bright floating layout for a chic flexbox layout. :-)

Your bold modifier example, of course, does not have a place inside the Button component. It is better to think about why something is bold, and then create a component or semantic property for it. Otherwise, if the design requires changing these bold buttons to italic green buttons, you will need to change the group of elements.

If you do this (sharing visual / layout classes for semantic components and splitting CSS components and modules), you will have less "exponential increase." If you end up in a situation where you really need to combine several semantic properties, there is still some value in having these states outlined. A good example would be "primary-disabled". This is better than "primary disconnected" for several reasons. First, it’s easy to browse in the CSS module and see the specified explicit state. Secondly, there are no ambiguous applications and relationships of these classes. Is "primary disconnected" really used by these classes? This can be found out only if someone uses documents. The disabled class can override something in the primary class, which means there are implicit order dependencies. It's easy for complete editing to break things down in the future, because the relationships between these classes are not obvious. As it often happens, choosing something implicit to save some keystrokes can lead to subtle errors. Taking into account that the keystroke bit blocks things and makes it obvious what will work and what will not.

Another small point that will save you a few keystrokes: there really is no reason for the "button-" prefix. That's 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 file name itself is the prefix "button".

+6
source

I think that in this case I will get rid of the essay and nest your classes. Here is my suggestion (forgive me if my jsx is inactive):

 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 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" may conflict with other class names in your application. So it would be nice to come up with slightly better (narrower) names.

+2
source

All Articles