Skip to content

Latest commit

 

History

History
188 lines (135 loc) · 9.06 KB

styling-and-css.md

File metadata and controls

188 lines (135 loc) · 9.06 KB

React / Styling and CSS

How should we style our React app?

This is a huge and controversial topic so we need to be a bit more chatty. It's not obvious that there is (or should be) a one-for-all solution. First we set needs and propose options. Then we break down the evaluation into 3 major topics usually groupped together as "style":

Needs

Looking for (in order of importance)

  • all the features of modern css and preprocessors (e.g. media queries)
  • none of the issues of regular css (e.g. global namespaces)
  • clear best practices and conventions
  • styles not breaking our functionality
  • the more foolproof the better
  • active community, good documentation
  • simplicity for small projects but scalability for big ones
  • low cost of entry
  • framework agnostic
  • server side and react native support is a plus

Not looking for

  • Tier 1 performance

Don't want

  • mixing component logic with styles
  • having to use conventions we cannot test or enforce
  • big cost of entry

Options

Preprocessed CSS modules

Such as SASS, LESS, SCSS.

Pros

  • feature rich
  • solves issues of regular CSS
  • faster
  • widely used and known
  • framework agnostic, easily portable

Cons

  • technical concerns rest on you
  • conventions, best practices not enforced
  • requires conventions to be viable for bigger projects, there're many of them with no clear winner (e.g. BEM, OOCSS, SMACSS, SUITCSS, Atomic, SASS-Guidelines)
  • conventions are completely separate from other project conventions (e.g. naming, structure) and have similar complexity, high cost of entry

CSS in JS

Library comparisons:

Out of the many libs the best choice currently seems to be styled-components which is also clearly shown in usage trends. Pros and cons address this in particular but most apply accros many.

Pros

  • feature rich (complete SASS features for JS with polished)
  • no issues of regular CSS
  • best practices and conventions come with the lib
  • JS for logic (e.g. :nth-child(even) vs i % 2)
  • critical parts can be unit tested
  • technical concerns are abstracted away (e.g. CSS injection, object creation and structure)
  • very simple for small projects
  • needs structure / conventions for big projects, but those align with project conventions as a whole (great articles on the topic: Structuring our Styled Components | Part I, Structuring our Styled Components | Part II)
  • doesn't exclude other ways of styling
  • good documentation, active community
  • low cost of entry also for those only familiar with CSS
  • server side and react native support

Cons

Decision

Let's address the concerns separately. There's also a great post about this on StackOverflow.

Behaviour

We want our state and behaviour to be defined in one place and to be unit tested. As mentioned before we don't want styles to break our app functionality. CSS in JS has the benefit on keeping state-related code in one place and allowing to test critical parts of the style instead of only testing CSS classes (e.g. disabled button, crossed out text). This talk does a great explanation on it: Michael Chan - Inline Styles, 9:39 - 15:55.

Use CSS in JS.

Component appearence

CSS still presents a big drawback on the convention and entry cost part. CSS in JS handles the conventions of component appearence well. It has multiple applicable cons, but they can be addressed (see Conventions).

Use CSS in JS.

Cross-component appearence

Testing this is not in the scope of unit testing. For layouts best practices are estiablished and there're many libraries abstracting away most of the CSS work. Scoping is not an issue for global styles. They also tend to be more straightforward and wouldn't make use of the conventions of CSS-in-JS. CSS-in-JS still can be used to keep the way of styling unified and get the benefit of other, more advanced features such as server side rendering or critical CSS. However Preprocessed CSS modules and sometimes even regular CSS can also meet basic needs.

Use either CSS in JS or Preprocessed CSS modules or Regular CSS.

Conventions

Despite our efforts there're still some custom conventions we have to establish, however now only a small number of straightforward ones.

Never interpolate user input with styled-components

Store CSS-in-JS styles in separate files

It is very often the case that CSS-in-JS is presented with examples where it is part and parcel of the components. This gives the wrong idea and (understandably) scares off poeple. Separating the concerns of style and business logic is encouraged on many levels. Starting on component level (see: "Presentational and Container Components" by Dan Abramov) reaching down to styled-components specifics (see: "Separate your code with Styled Components" by Sara Vieira, "Get Organized with Styled-Components" by Jeremy Davis).

Our approach is inspired by said articles, aiming to provide a clear and generic separation between styles and components.

Component styles are declared in a separate .styles.js file using the styled helper function. See the example below.

Use SMACSS categories for cross-component styles

SMACSS categories provide a practical separation of concerns. Module and state categories are covered by the component.style.js pattern, however styles that reach accross components (whether because they are global or just affect many components) need a proper place. Place them in src/common_styles and use SMACSS catogires for grouping. Applies both to CSS and CSS-in-JS.

CSS-in-JS example

.
└── src
    ├── common_styles
    │   └── layout.scss
    └── components
        └── Footer
            ├── Footer.js
            └── Footer.style.js

Footer.js

import { TextWrapperDiv, SocialIcon } from "./Footer.style";

export const Footer = (props) => {
  return <div>
    <TextWrapperDiv {...props}>
      // ...
    </ TextWrapperDiv>
    <SocialIcon />
  </div>
};

Footer.style.js

import { styled } from "styled-components";

export const TextWrapperDiv = styled.div`
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;

  ${p => p && p.secondary`
    background: white;
    color: palevioletred;
  `}
`;

export const SocialIcon = styled.img`
  width: 45px;
`

Further reading

Things to keep an eye on: