Friday, January 20, 2017

CSS and styling options in ReactJS

Blog describes approaches for styling ReactJS components
Blogger: V. Keerti Kotaru . Author of Angular Material book 

ReactJS provides multiple options for styling components. We might use traditional approach to have a style-sheet in the project; Then apply CSS classes for elements in the component. Or we might pick a contentious approach to code styles within the component.

I played with following approaches so far,

 a) Specify CSS class in VDOM with className attribute.
 b) Use style object.
 c) Styled-Components

Refer to the complete code samples at this link.

Using className attribute

Use className attribute in the component to use a pre-existing CSS class. Consider following code, it applies two CSS classes alert and alert-info on the div. These are bootstraps alert styles. Refer to figure-1 to see the resultant information alert.

<div className="alert alert-info"> 
  <strong>Hello! Namaste. नमस्ते.  నమస్కారం. </strong>
</div>
Figure 1 - rendered output for the code sample 
As we package the code as a React component, we could take advantage of showing the alert as an information or as an error. The component could dynamically apply CSS class based on props set by container component. Consider following code that sets alert-danger style when error prop is set (refer to figure 2). If not the default information styling is applied.

---------- alert.js ---------------------
import React from 'react';

export default (props) => {
  

    // if props.error is true set "alert-danger" else "alert-info".
    return (

        <div className={`alert ${props.error?'alert-danger':'alert-info'}`}>  
           <strong> {props.children} </strong>
        </div>
    )
};

Note: In the expression className={`alert ${props.error?'alert-danger':'alert-info'}`} first set of  braces - {} are React expression and the second set ${} are ES6 string extrapolation syntax. It represents alert-danger or alert-info depending on props.error value (true/false).

Usage:
import Alert from './Alert.js';

          <Alert error>Oops! There is a problem</Alert>
          <Alert>This is an information message</Alert>

Figure 2 - Error alert as error prop is added

Using Style attribute

Similar to className, style attribute could also be used in VDOM. This approach encapsulates style within the component. We are not using a global, pre-existing style.

Provide a JSON object with style values that translate to CSS styles. Consider following code sample and the styleInline variable.

import React from 'react';

export default (props) => {
    let styleInline = {
        fontFamily: 'comic sans ms',
        fontSize:'14px'
    };

    return(
        <div style={styleInline}>
            {props.children}
        </div>
    );
}

Note that styles like font-size need to be written as fontSize in VDOM.

Similar to earlier sample, we can use ES6 string extrapolation to dynamically set a style. Following code changes font-size, font-weight and color based on prop - important.

    let styleInline = {
        fontFamily: 'comic sans ms',
        fontSize:`${props.important?'18px':'14px'}`,
        fontWeight: `${props.important?'bold':'normal'}`,
        color: `${props.important?'red':'gray'}`
    };



Figure 3- result with important prop

Styled Components

Styled components library allows creating and wrapping components with given styles. It makes it easy to implement themes and variation in styles. It eliminates boilerplate code and we can instantly create styled components with quick CSS style text.

Getting started
  • Install styled components using npm
npm install --save styled-components
  • import styled from 'styled-components'
Consider following example. Here a styled component is created with HTML element and styles put-together. 

export let TitleText = styled.h1 `
    margin: 1em;
    font-size: 20pt;
    border: 2px solid cyan;
    background-color: gray;
    color: white;
    padding: 0.5em 1em;  
` ;

// Use the component. See figure 4 for result.
<TitleText>Sample styled component out of H1</TitleText>

Figure 4 - Styled component created out of H1

Like earlier examples, based on props, we can dynamically change certain styles. Following sample changes background and border if a primary prop is set on the component.

export let TitleText = styled.h1 `
    margin: 1em;
    font-size: 20pt;
    border: 2px solid ${props => props.primary?'darkblue':'black'};
    background-color: ${props => props.primary?'darkblue':'gray'};
    color: white;
    padding: 0.5em 1em;  
` ;


// Use the component as below. See figure 5 for result (Without primary attribute, result will be same as figure 4)
<TitleText primary>Sample styled component out of H1</TitleText>
Figure 5 - styled component with prop value primary







The API could also be applied on React components (similar to HTML elements). Consider following simple component.

-----------  Button.js ----------------
import React from 'react';

export default (props) => {
    return (
       <button className={`${props.className} btn`}>{props.text || props.children}</button>        
    )
}

Wrap the component using Styled Component.

import ButtonStatelessComponent from './Button.js';
import styled from 'styled-components';

export let SelfStyledButton = styled(ButtonStatelessComponent) `
                        background: ${props => props.primary?'darkblue':'lightgray'};
                        color: ${props => props.primary?'cyan':'black'};
                        font-size: 18pt;
                        margin: 1em;
                        padding: 0.25em 1em;
                        border: 2px solid lightblue;
                        border-radius: 3px`;

Usage: 
          <SelfStyledButton primary text="Primary Button" />

          <SelfStyledButton> Default button </SelfStyledButton>
Notes: 
  • Primary attribute adds dark blue background and cyan text color.
  • For styling to apply, the component should have className attribute defined as following. This is because, the API creates a CSS class and applies on the component at the given HTML element.
className={`${props.className}

Refer to figure 6 for results.

Figure 6 - Apply primary prop for a primary button. Default button uses component as is.

References and further reading