Skip to content

italopessoa/react-style-guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React Code Style Guide

The patterns we have been using in our projects


Introduction

This guide was created to be a quick start for new developers understand the React code style we adopted here at Pagar.me and some practices considered good.

As this guide is an extension of our JavaScript style guide we higly recommend to read it prior to this document.

Installing

The rules described in this repository are also available as a NPM package. To install the package and its dependencies:

$ npm install --save-dev eslint@4.3.0 \
                         eslint-plugin-jsx-a11y@5.1.1 \
                         eslint-plugin-import@2.7.0 \
                         eslint-plugin-react@7.1.0 \
                         eslint-config-pagarme-react

The peer dependencies specified above have hardcoded versions. If you prefer you can use the command npm info eslint-config-pagarme-react@latest peerDependencies to find the exact peer dependencies to install.

To include in the project, create an .eslintrc file with at least the following contents:

{
  "extends": ["pagarme-react"],
  "env": {
    "browser": true
  }
}

Table of contents

Component definition

All components (presentation, containers or pages) should always be defined as a directory, named with pascal casing. The main component file should be index.js and main stylesheet style.css. E.g.:

AwesomeCard/
├── index.js
└── style.css
  • Styles should always be defined in a separate CSS file
  • Avoid prefixing or suffixing component names
    • E.g.: lib/pages/UserPage or lib/container/LoginContainer

⬆️ Back to top

Project organization

Components should be divided in at least three directories:

awesome-react-project/
└── lib/
   ├── components/
   ├── containers/
   └── pages/

Each of these directories have special types of components:

components/

Stateless components. Shouldn't store state. Most components in this directory will be function-based components. Stuff like buttons, inputs, labels and all presentational components goes here. This components can also accept functions as props and dispatch events, but no state should be held inside.

containers/

Container components can store state. Containers are built mostly from the composition of presentational components with some styles to layout them together. Containers can also store internal state and access refs to provide additional logic, but all actions should be accepted as component callbacks.

pages/

Page components can store state, receive route parameters and dispatch Redux actions when applicable. Pages are the highest level of application's components. They represent the application routes and most times are displayed by a router. Pages are also responsible to handle container components callbacks and flow data into children containers.

⬆️ Back to top

CSS are modules!

We use CSS modules everywhere. CSS modules are great because they provide scope to CSS, and allows to create compartmentalized style that doesn't leak to global scope. Here are our good practices of doing CSS modules:

Formatting CSS

80 columns, soft tabs of 2 spaces

Keep at 80 columns. This helps when opening multiple splits. Use soft tabs of 2 spaces to save space! 😛

Camel case instead of dash-case for class names

With CSS modules, camel case makes much more sense:

GOOD
lib/components/Input/index.js lib/components/Input/style.css
import style from './style.css'

const Item = ({ children }) =>
  <li className={style.circleBullet}>
    {children}
  </li>

export default Item
.circleBullet {
  list-style-type: disc;
}

⬆️ Back to top

Never use ID and tag name as root selectors!

Using ID and tag name at the selector's root makes the rule to be applied globally.

GOOD
lib/components/Item/index.js lib/components/Item/style.css
import style from './style.css'

const Item = ({ title, thumbnail }) =>
  <div className={style.container}>
    <img src={thumbnail} alt={title} />
  </div>

export default Item
.container > img {
  background-color: #CCCCCC;
}
BAD
lib/components/Item/index.js lib/components/Item/style.css
import style from './style.css'

const Item = ({ title, thumbnail }) =>
  <div className={style.container}>
    <img src={thumbnail} alt={title} />
  </div>

export default Item
img {
  background-color: #CCCCCC;
}

⬆️ Back to top

When using multiple selectors, give each selector its own line

Organize one selector per line, even when placing at the same line doesn't exceed the limit of 80 columns.

GOOD BAD
.container > img,
.container > div,
.container > section {
  background-color: #CCCCCC;
}
.container > img, .container > div, .container > section {
  background-color: #CCCCCC;
}

⬆️ Back to top

Break lines in CSS function arguments, keep a dangling comma

With 80 columns and CSS variables comes the need to break lines. When breaking, keep one argument per line, and leave a dangling comma at the last argument.

GOOD BAD
.container {
  background-color: linear-gradient(
    0deg,
    var(--color-light-yellow-12),
    var(--color-light-yellow-10),
  );
}
.container {
  background-color: linear-gradient(0deg, --color-light...
}

.container {
  background-color: linear-gradient(
    0deg, var(--color-light-yellow-12), var(--color-lig...
}

⬆️ Back to top

When writing rules, be sure to

  • Put a space before the opening brace { in rule declarations
  • In properties put a space after (but not before) the : character
  • Put closing braces } of rule declarations on a new line
  • Put ONE blank line between rule declarations
GOOD BAD
.container {
  font-size: 12pt;
}

.thumbnail {
  width: 160px;
  height: 90px;
}
.container{
  font-size:12pt;}
.thumbnail{
  width:160px;
  height:90px;}

⬆️ Back to top

CSS Design Patterns

The parent constrains the child

Leaf components shouldn't constrain width or height (unless it makes sense). That said, most components should default to fill the parent:

GOOD
lib/components/Input/index.js lib/components/Input/style.css
import style from './style.css'

const Input = ({ children }) =>
  <input className={style.input}>
    {children}
  </input>

export default Input
.input {
  box-sizing: border-box;
  padding: 10px;
  width: 100%;
}

⬆️ Back to top

The parent doesn't assume child structure

Sometimes we don't want to fill the whole width by default. An example is the button component, which we want to resize itself based on title width.

In this cases, we should allow the parent component to inject styles into the child component's container. The child is responsible for choosing where parent styles are injected.

For merging styles, always use classnames package. The rightmost arguments overrides the leftmost ones.

GOOD
lib/components/Button/index.js lib/components/Button/style.css
import classNames from 'classnames'
import style from './style.css'

const Button = ({ children, className }) =>
  <button className={classNames(style.button, className)}>
    {children}
  </button>

export default Button
.button {
  box-sizing: border-box;
  padding: 10px;
  width: 100%;
}

⬆️ Back to top

Components never leak margin

All components are self-contained and their final size should never suffer margin leakage! This allows the components to be much more reusable!

BAD GOOD
|--|-content size-|--| margin
 ____________________
|   ______________   | | margin
|  |              |  |
|  |              |  |
|  |              |  |
|  |______________|  |
|____________________| | margin

|---container size---|

   |-content size-|
    ______________
   |              |
   |              |
   |              |
   |______________|



⬆️ Back to top

The parent spaces the children

When building lists or grids:

  • Build list/grid items as separate components
  • Use the the list/grid container to space children
  • To space them horizontally, use margin-left
  • To space them vertically, use margin-top
  • Select the first-child to reset margins
GOOD
lib/containers/Reviews/index.js lib/containers/Reviews/style.css
import style from './style.css'

const Reviews = ({ items }) =>
  <div className={style.container}>
    {items.map(item =>
      <img src={item.image} alt={item.title} />
    )}
  </button>

export default Reviews
.container > img {
  margin-left: 10px;
}

.container > img:first-child {
  margin-left: unset;
}

⬆️ Back to top

Nested classes are useless

CSS modules already provides us scope. We don't need to use nested classes.

BAD
lib/components/Button/index.js lib/components/Button/style.css
import style from './style.css'

const Button = ({ children }) =>
  <button className={style.button}>
    <img className={style.icon} />
    {children}
  </button>

export default Button
.button {
  box-sizing: border-box;
  padding: 10px;
  width: 100%;
}

.button .icon {
  width: 22px;
  height: 22px;
}
GOOD
lib/components/Button/index.js lib/components/Button/style.css
import style from './style.css'

const Button = ({ children }) =>
  <button className={style.button}>
    <img className={style.icon} />
    {children}
  </button>

export default Button
.button {
  box-sizing: border-box;
  padding: 10px;
  width: 100%;
}

.icon {
  width: 22px;
  height: 22px;
}

⬆️ Back to top

Variables, lots of variables!

We encourage the "variabilification". Always define variables to increase reuse and make styles more consistent. The CSS specification defines a way to declare native variables that are very interesting. We adopted them as the standard.

To define a variable accessible globally:

GOOD
app/App/variables.css app/components/Button/styles.css
:root {
  --color-green-1: #6CCFAE;
  --color-green-2: #6B66B5;
  --color-green-3: #AAC257;
  --color-green-4: #68B5C1;
}
.container {
  background-color: linear-gradient(
    0deg,
    var(--color-green-1),
    var(--color-green-2),
  );
}

⬆️ Back to top


About

Our React Style Guide

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published