After adding style.css you need to import it in index.js by adding:
import'./style.css';
This tutorial builds on the previous one. If you haven’t completed it, you can just copy the following contents and add them to src/index.js.
/* eslint-disable max-classes-per-file, @scandipwa/scandipwa-guidelines/only-one-class */import { Component, PureComponent } from'react';import ReactDOM from'react-dom';import PropTypes from'prop-types';// new style importimport'./style.css';classButtonextendsPureComponent {// use either propTypes or defaultPropsstatic propTypes = {// sets the required prop wrapperCount:PropTypes.number };static defaultProps = {// sets the default value wrapperCount:0 };constructor (props){super(props);this.state = { clickCount:0,// set a new default prevWrapperCount:0 }; static getDerivedStateFromProps(props, state) {// no access to current value is present// no access to `this`const { wrapperCount } = props;const { prevWrapperCount } = state;// you need to keep previous value in state// if wrapper count is not equal to previous valueif (wrapperCount !== prevWrapperCount) {return{// update click count to wrapper count clickCount: wrapperCount,// update previous value prevWrapperCount: wrapperCount }; }returnnull; }componentDidMount() {console.log('mount',document.getElementById('abc')); }componentDidUpdate() {// triggered by state & props changeconsole.log('update'); }onButtonClick= () => {const { clickCount } =this.state;this.setState({ clickCount: clickCount +1 }); }render () {const { clickCount } =this.state; return ( <divid="abc"> <span> You clicked me <b>{ clickCount }</b> </span> <buttononClick={ this.onButtonClick }>Click me!</button> </div> ); }}classWrapperextendsComponent {// defines the state state = { clickCount:0 };onButtonClick= () => {const { clickCount } =this.state;this.setState({ clickCount: clickCount +1 }); };render() {const { clickCount } =this.state;return( <div> <ButtonwrapperCount= { clickCount } /> <buttononClick={ this.onButtonClick }>Update wrapper</button> </div> ); }}ReactDOM.render(// renders the wrapper component instead <Wrapper />,document.getElementById('root'));
Using CSS properties without a prefix
It is recommended to stick to styling by classes, as it’ll make your life easier in the long run:
// this is className="heading" in index.js.heading { font-size: 20px; font-family: monospace;}// this is className="button" in index.js.button { appearance: none; border: 1px solid black; padding: .25rem 1rem; background-color: hotpink;}
A REM unit is equal to computed value of font-size for the root element. So, if your font default is 12px, 1 REM unit will be 12px as well and 0,25 REM units will be 3px.
In order to make the styles work, we need to add class names to the render methods found in index.js.
First, let’s edit the class Button:
render () {const { clickCount } =this.state; return ( <divid="abc"> {/* change span to h1 and add classNames */} <h1className="heading"> You clicked me <b>{ clickCount }</b> </h1> <buttonclassName="button"onClick={ this.onButtonClick }>Click me!</button> </div> ); }
For now, ESlint rules won’t let you use className, so for the sake of this tutorial, disable this rule for the whole file.
Now the style should change accordingly. If we use ‘Inspect element’ on a button in the browser, we should see in the Styles tab that a previously unknown property webkit-appearance has been added. This happened due to the fact that the compilator automatically adds vendor prefixes to the properties.
Here you can see browser specific examples:
…-webkit-flex:101; # Chrome and Safari-moz-flex:101; # Mozilla-o-flex:101; # Opera-ms-flex:101; # Internet Explorer…
Using CSS variables
Instead of writing out the color references directly, we should use CSS variables or CSS custom properties. They are declared in the root element and later referenced using var().
Now, the background color of the button in <div className="button-wrapper"> should be green. This happens because a CSS custom property has inheritance - it takes the topmost parent and goes downwards, looking for re-definition of the variable. If the property finds a re-definition, it’ll use the one closest to it.
In order to improve the readability of the style file we can switch to SCSS - rename style.css to style.scss and change the name of import in index.js as well.
import'./style.scss';
Now, instead of repeating .button over and over again, we can, for example, add the :hover pseudo-class by nesting the properties.
Notice that blocks are named using PascalCase - each word in a compound word is capitalized. A block’s declaration usually is preceded by a dot, for example, .MyElement.
An element always follows a block and is defined using a dash and PascalCase, for example, .Heading-Strong where Heading is the block and -Strong is the element.
If, we’re using SCSS, we can use glue or & for BEM elements as well, for example:
.MyElement{&-Key {... }}
Modifiers are the state definitions of an element or a block, these are usually preceded by an underscore and named using camelCase. Note that you can use either one-part or two-part modifiers.
One-part modifier is a boolean modifier that should start with is and non-boolean modifiers should be split in two parts, where the first part defines the type of the modification and the second part defines a value.
The next example uses the gluing method to define .Heading_isLarge, .Heading-Key_isHuge and .Heading_type_icon:
Since it’s not exactly convenient to concatenate strings, in order to have multiple elements or modifiers, we should use the BEM HTML helpers. Let’s also add a modifier: