How to CSS in the Era of Components
During my time at Taito United, appr. 2 years, we have had several “practices” for styling our Angular / React apps: plain old CSS without any naming conventions, SCSS without any naming conventions, SCSS with some naming conventions such as BEM (that produces way too long classnames so nobody actually uses it), CSS Modules (with SCSS) and then some quick experiments with a few CSS-in-JS approaches.
Most of these techniques work pretty well in small scale projects but they seriously start to explode in your hands when the project size gets bigger. It’s not fun to modify or add new style rules when you are not sure how they affect your app.
In addition to CSS practices there has been a major shift in the mindset of how you should organize and structure your app. React has made component-based thinking the norm by co-locating the business logic and presentation into one single component (file or folder). With the help of Webpack or some other bundler we are able to include the necessary styles to the same component as well. However, the thing is that CSS is global by nature and we would like to have the styles encapsulated inside the component without having to come up with some complex naming conventions.
CSS-in-JS
Recently many CSS-in-JS solutions such as Radium, Aphrodite, JSS - just to name a few - have emerged bringing their own encapsulation solution to the table. Categorically these solutions either inline the CSS to the DOM elements or inject the necessary styles with some generated unique classnames. By defining your styles in Javascript you have all the power of it which makes it ease to define variables, mixins, etc. — you name it. The main issues with these CSS-in-JS approaches is that by allowing us to use and merge multiple style definitions in one component (or it’s subpart) it becomes unclear semantically what the thing actually is. Also the logic for applying certain styles based on components state, props or some other factor is located inside the component’s render function which usually makes it quite messy. Take this Radium example:
class Example extends React.Component {
render() {
return (
<div style={[
styles.sectionBlock,
styles.highlight,
{ color: this.props.someOvewriteValue },
getHoverColor(this.state.someOtherValue),
]}>
What am I?
</div>
);
}
}
At first glance there’s nothing wrong with the example component but just imagine that the component is more complex containing many subparts that have similar style definitions… Welcome unreadable spaghetti code.
CSS Modules
CSS Modules is another style encapsulation solution that has been gaining a lot of momentum recently. CSS Modules allows you to define component’s styles using basic CSS, SCSS, LESS etc. while guaranteeing that the styles are encapsulated inside the component by automatically generating unique classnames from the already defined classnames (usually by appending some hash). You can literally have twenty “header”, “body” or “footer” classnames in your app in different components and things will just work without any clashes — magic. Also, a big pro for CSS Modules is that it can easily be used with legacy code (that for example uses SCSS) without having to rewrite your styles into JS objects (or to some other CSS-in-JS format).
You can literally have twenty “header”, “body” or “footer” classnames in your app in different components and things will just work without any clashes — magic.
Check this example that uses react-css-modules helper lib:
import styles from './styles.scss';class CSSModulesExample extends React.Component {
render() {
return (
<div styleName='card'>
<div styleName='header'>
<img src='amazing-image.jpg' />
</div>
<div styleName='body'>
I'm a card with header, body and footer sections!
</div>
<div styleName='footer'>
<button>
Gotta have actionable buttons
<i className='global-icon-classname' />
</button>
<button styleName={
someVariable ? 'meh' : 'whyLogicHere?'
}>
Abort mission
</button>
</div>
</div>
);
}
}export default CSSModules(CSSModulesExample, styles);
The react-css-modules nicely helps us separate local and global styles since it is quite common to have e.g. an icon font set such as font-awesome or ionicons that require the use of global classnames. I would also argue that the readability / semantics of the component and it’s subparts is a bit better with CSS Modules than it is with CSS-in-JS solutions. However, it is still required to have the logic for changing or toggling of classnames, in this case stylenames, inside the render function of the component and if you need to overwrite some styles dynamically based on props (let’s say height) things can get a bit hairy.
Well okay, so is there any other solution that makes styling component based web apps easier?!? Drum roll… YES THERE IS!
I would like to introduce you to styled-components which has redefined how I style my React apps. When I first saw styled-components I’m pretty sure I vomited a bit in my mouth and thought “what god forsaken witchcraft is this?” I kinda looked like this:
At first sight the syntax (tagged template literals) that styled-components uses looks really weird and the fact that all the styled components are co-located inside the actual React component felt a bit awkward. However, after taking a deep breath and swallowing the piece of lunch that came up things started to make more sense. After trying out styled-components in action the Amor / Cupid of CSS shot an arrow to my heart and I was instantly in love.
After trying out styled-components in action the Amor / Cupid of CSS shot an arrow to my heart and I was instantly in love.
Here is a simple styled component:
import styled from 'styled-components';const Input = styled.input`
padding: 8px;
font-size: 1rem;
background-color: #eee;
border-radius: 4px;
border: 1px solid #ccc;
color: #333;&:focus {
outline: none;
box-shadow: 0px 0px 2px tomato;
}
`;
Styled-components provides all the HTML elements that you would normally use in your render method and gives you back a React component with the styles you have defined inside the backticks. The first awesome feature of styled-components is that you can use SCSS shorthands such as &:hover/active/focus in your styles. Secondly, by creating these so-called styled components with semantically meaningful names the render method becomes a piece of art that is pleasant to look at:
class StyledExample extends React.Component {
render() {
return (
<Card>
<Header></Header>
<Body></Body>
<Footer>
<Button>
Oh so pretty!
</Button />
</Footer>
</Card>
);
}
}
The third and maybe the most amazing feature of styled-components is that you can pass props to styled components since they are just React components and use these props inside the style definitions, like this:
function getDepth(props) {
if (props.depth === 1) return '0px 2px 4px rgba(0,0,0,0.1)';
if (props.depth === 2) return '0px 4px 16px rgba(0,0,0,0.2)';
if (props.depth === 3) return '0px 6px 22px rgba(0,0,0,0.3)';
return '0px 1px 4px rgba(0,0,0,0.1)';
}const Card = styled.div`
display: flex;
flex-direction: column;
padding: 32px;
background-color: ${props => props.bg || '#fff'};
border-radius: 4px;
width: 100%;
box-shadow: ${props => getDepth(props)};
`;// Then use Card in render
<Card bg='tomato' depth={2} />
This Card component changes its background-color based on the prop it is given to. Also notice how easy it is to have some helper functions (like getDepth) that return style variations based on the props. This way you can keep the stuff in-between the backticks more readable.
You can also conditionally apply complete styles if you like that kind of things:
const Thing = styled.div`
...more styles...
${props => props.show && 'opacity: 1;'}
${props => !props.show && 'opacity: 0;'}
${props => !props.show && 'pointer-events: none;'}
`;
There are many more useful features such as extending other component’s styles, keyframes and theming that you should check out in the Github repo.
You can even use styled-components with React Native!
That’s it!
To wrap things up I’d like to say that CSS has evolved quite a lot during these two years that I have been building web apps professionally at Taito United. CSS Modules and styled-components have definitely improved greatly how we define our styles in this era of components. In conclusion, I’m pretty sure I have never been this excited (and not afraid / irritated) to write styles for my components!
We are hiring!
Do you want to build exciting and interesting apps with modern web technologies? Check out our open positions and join us at Taito United!
Can’t find positions directly suitable for you? Feeling adventurous? Send your resume and an open application to jobs@taitounited.fi — we are more than happy to hear you out and see what you got cooking 🍳