On this page:
Let’s start by questioning some design principles that have dominated the way we’ve styled websites for the last few decades.
We are not going to question separation of concerns, we think it’s a great design principle. We are going to question the way we have interpreted it for many years in front-end applications. Hopefully, you’ll agree about the new angle from which we look at it.
The following image illustrates how we previously separated the concerns in the front-end:
One of the most amazing examples of this separation that I’ve seen is CSS Zen Garden. With various designs based on the same HTML and one stylesheet targeting the HTML elements, by applying different CSS to them, we can create completely different websites.
You can visit http://www.csszengarden.com/,check some designs, and compare the HTML of some. You’ll see that the only line that changes is the one that imports the CSS. Impressive from the CSS and HTML separation of concerns point of view.
One of the problems you might have with the previous way of separating concerns is maintainability of the code base. In the following example, you can see how a button is styled differently using rules based on sign-up, btn-facebook, chart-view, search, etc. The example is probably very extreme, but it makes the point.
Separating concerns is good, but we need to choose the right concerns. For many years the norm was to separate things based on some technical aspect, like CSS, JS, HTML, views, controllers, etc. In this “old” approach when you look at the source code of a project you see things like the following:
/src/css/index.css /src/css/button.css /src/css/alert.css /src/css/…css /src/views/alert.html … /src/controllers/alert.js …
CSS, controllers, views, etc, are implementation details. If you think of the user or the business, they don’t use techie words like CSS, JS, etc, they say “checkout”, “message”, “button”, etc. These are the first concerns we should think of. Then, of course, each “message” or “button” will have different technical aspects depending on the implementation.
As the following image illustrates we are still separating concerns, but we have given a different angle to the definition.
Change is the only constant, therefore it's good to revise our concerns from time to time.
If you have used create-react-app you’ve seen that it creates a separate file for App.js and for App.css, and for index.js and index.css. Do you think create-react-app promotes separating the concerns in a way that makes it easier to maintain our code?
I think it does. It follows the co-location principle, it co-locates the JS and the CSS close together. Note, we also apply the co-location principle in React Router and GraphQL for instance.
So, are we done with styling? Well, there is another issue created by global styles. That folder structure separates the code by concern, but nothing stops us from creating an “active” class in Button.css, and an “active” class in Alert.css with different values. What do you think is going to happen if we have an active button and an active alert in the same view?
The solution to the style collision is to use naming conventions in our CSS classes. There are a few different popular conventions. The problem with naming conventions is that humans must implement them. That’s more work, and it’s error-prone.
A better approach is to automate that CSS naming. You can use CSS modules for this:
css-loader is the loader that makes CSS modules work. css-loader is installed and configured by default when using create-react-app.
Styled-components is the library we use at LeanJS (React GraphQL Academy's parent company) to style our React components. Why? It's a single library that elegantly solves all the problems I’ve mentioned.
The documentation of styled-components is great, very clear and with good examples. So there is not much we can add in this post that is not already well explained in the official docs.
However, let me point out one feature that is quite important and I don’t want you to miss if you are new to React. Styled-components, apart from solving all the problems we’ve mentioned, does something that SASS, LESS, BIM, SMACSS, and other approaches can’t do. Styled-components allows for conditional rendering. This means it can decide a style at render time based on some props. That’s very powerful and you should definitely leverage this feature.
The following image shows a button with conditional rendering based on a prop called “primary”. Depending on the value of “primary” it will render a different style.
You can edit the previous code by removing 'primary' from the last button. Try it! The style will react and change the color.
Another amazing feature you get from using styled-components is that it will only generate the CSS required for a given page. The way it works is the following, when React traverses the tree rendering components, styled-components adds to the head of the page the
<style>
tag containing only the CSS of the components that were rendered. This works very well on server-side rendering or static site generators like Gatsbyjs,so it optimizes the first page load by reducing the CSS load.
This website uses styled-components, so you can see an example of the CSS injected by looking at the
<head>
in the source code of this page.
Everything on the UI can be a component (including the metas in the head! when we say everything it means everything :). Therefore you can define all your styles using styled-components. That means no need for SASS, LESS, or other CSS pre-processors.
You can see an example in the React GraphQL Academy website, where EVERYTHING is a component:
That being said, I often see that many developers new to React are used to the "old .css" and find it very difficult to only use component based styles. Some common cases they mention:
As you can see, in all those cases we can use a component, and so you can co-locate the style in that component. I know, it might not seem that obvious at the beginning, especially if you are very used to SASS, LESS, or “classic” CSS.
There are different libraries (AKA component toolkits) that you can use to speed up the development of a UI. What I mean by component library is a set of React components that follow some design system and provide an implementation for common UI patterns such as Text, Button, Dropdown, Tabs,Datapicker, etc.
These are some of the most popular component libraries in React:
The component library that we use at LeanJS when we want to speed up the development of a new React app is Rebass, here is why:
Rebass is based on styled-components. Since we use styled-components to build React apps, Rebass integrates very well with the rest of the style and optimizes bundle size. The other reason we use Rebass is because it's wonderfully designed. It's based on styled-system, an elegant responsive, theme-based style props for building design systems with React. Styled-system is part of our advanced React training and bootcamp curriculum.
Reactstrap current version (v7) uses one global CSS :(
SemanticUI current version (2.4) uses LESS, therefore no access to props in that style to use the powerful conditional rendering that we explained before :(
There is an important consideration when choosing a component library: how easy it's going to be in the future to replace it with something else. One of the libraries that provides a lot of (highly-coupled) functionality is Antd - at least the current version (3.12). For instance, the behavior of the form is highly-coupled to how form field components look. In this case, a good alternative is to use Rebass in combination with final-form to decouple the behavior of the form and how the form fields look.
Good developers plan for successful change. Whatever component library you use you should make it as easy as possible to change it in the future.
A good practice in this regard is to abstract any component library into your own components. This a small amount of work up-front but brings a lot of value in the future. For instance, if you use Rebass, you wouldn't
import { Button } from "Rebass"
everywhere you want to use a button. Instead, you would create your own Button that exports the Rebass Button:
// src/components/Button.js export { default as Button } from 'rebass'
Then you can import your Button everywhere you need a button in your app. This might look very simple, but creating this abstraction is very important. When we created the first version of the React GraphQL Academy website we used Rebass and we created this abstraction on top of the Rebass button in the first commit.
We built the first prototype in React in a few days using Rebass and we were able to test it immediately with real users. The reason we are called LeanJS is evident :D
Then we added custom styles to the Button incrementally. Two weeks after we had completely removed Rebass from the app. Removing Rebass from the package.json was a piece of cake. That's amazing considering that the initial version was entirely built with Rebass. Hopefully, I've convinced you of the advantages of creating these abstractions.
If you want to know more about how LeanJS designed, built, and tested in a month the MVP for this website check out this blog post
Add a global and generic CSS like normalize.css to remove all the custom browser styling (paddings, margins, etc). Then use only styled-components to style your app.
If you really think you need to create a separate SASS or LESS file and you need to define variables there, you can export those variables to JavaScript using CSS Modules so you don’t have to define them twice. For instance variables for colors. You can see an example here.
We won't spam you as per our Privacy Policy.
Share this on:
Comments? Shoot me a tweet @reactgqlacademy !
GraphQL Evening with Round Table 💥 Online
London, UK
Prices & more details