CSS in JS in real-life

July 20, 2018 0 Comments

CSS in JS in real-life

 

 

By Artur Siery

Recently I have stumbled across a lot of blog posts describing changes in styling approaches. Clearly, we’re entering a new era of styling in JS, that simply can be called CSS in JS. But what all of those posts miss, in my opinion, is a deep comparison of the features provided by those solutions and how they perform in real-world project. Code examples always look fresh and reusable and sexy, but what happens when we pick such solution and use it in a live application? Are there any drawbacks that turn beautiful ideas into coding hell? Let’s find out.

Side note: Recently I’ve been a React developer and when I did the research I was focusing mainly on how I could utilize the technology in my projects, so therefore I will mostly focus on the ease of use in React. Ability to read JSX will help you to understand what the heck is going on.

Putting CSS Modules here can be a bit of a stretch, depending on how you define CSS in JS, because you write basically in CSS (with some cool additions). What’s changes is the way of loading the styles into components. But they were the first, so treat it like a honorable mention on the list. Chances are that you probably seen CSS Modules somewhere. But let’s take a look at them (or, you know, you can always scroll to the next section). Let’s start with this simple example:

// styles.css
.heading {
color: gray;
font-size: 1.5em;
}
.paragraph {
font-size: 1.1em;
}
// component.js
import React from 'react';
import styles from './styles.css';
const Article = () => {
return (
<div>
<h1 className={styles.heading}>
Heading
</h1>
<p className={styles.paragraph}>
Article's text
</p>
</div>
);
};

Okay, so as you can see, the CSS are still the same. You simply write them like you did for years. What changes is the way of loading them into your application. Now, while bundling, the CSS files are being parsed and turn into pure JS objects, that are easy to use inside code. Styles are imported explicitly and used like any other JS object, which is very cool from the semantic perspective. But that’s not where CSS Modules shine.

Having classnames parsed, CSS Modules is able to change the names of the output classnames into totally unique strings. This gives us local scope inside CSS. That’s huge! The generated output will look like this:

// output
<div>
<h1 class="6b87a4">Heading</h1>
<p class="7h3g1a">Article's text</p>
</div>

The unique classnames are awesome! Are the solution to everything! And are totally un-debuggable. But there’s a small fix for it:

// fix (in webpack.config.js)
(...)&localIdentName=[path][name][local][hash:base64:5]
// output
<div>
<h1 class="src-components
componentheading6b87a4">
Heading
</h1>
<p class="src-componentscomponentparagraph_7h3g1a">
Article's text
</p>
</div>

Ah, much better! Now it’s clearly visible what styles come from what files. You can adjust the naming convention according to what’s suits you the most.

Pros:

  • Pure CSS (no learning curve)
  • Some additional features
  • Local scoping
  • Nice semantics using JavaScript’s import statement
  • Easy debugging (with the right Webpack setting)

Cons:

  • No more global styles, which is generally a good thing, but you need to invest some time to adjust to this approach
  • Additional Webpack (or other bundler) configuration

CSS Modules on their own bring some additional functionalities, like composition or global pseudo class. But if you’re missing some of the cool stuff like mixins or variables you can always add SASS or LESS. Just add the respective loader to your webpack config. Now, you have a very powerful duo, fully pack with features and possibilities.

Pros:

  • All of the CSS Modules features
  • All of the SASS/LESS features (!)

Cons:

For a long time I thought that using SASS with CSS Modules was the ultimate solution. Then, the other solutions came along.

Although one can argue, that CSS Modules where the first CSS in JS, Styled Components were the one that started the whole avalanche of such solutions recently.

Styled Components utilize tagged template strings and allow to put your CSS code inside JavaScript directly. You can still split it into 2 files, to get that feeling of logic and styles being separate. But it’s optional. So, how our example would look like in Styled Components? Let’s see:

// component.js
import React from 'react';
import styled from 'styled-components';
const Heading = styled.h1<br>  color: gray;<br>  font-size: 1.5em;<br>;
const Paragraph = styled.p<br>  font-size: 1.1em;<br>;
const Article = () => {
return (
<div>
<Heading>
Heading
</Heading>
<Paragraph>
Article's text
</Paragraph>
</div>
);
};

It’s pretty straightforward. Just use tags provided by the library to render a component that you would like having styling added to inside the template string literal. As simple as that.

Let’s say, we would like to extend the styling of existing component and add some more. That’s very simple:

const SexyParagraph = styled(Paragraph)<br>  text-decoration: underline;<br>  color: green;<br>;

Things get a little bit complicated, when you would like to add additional handlers to styled components. Let’s take a simple button and add a onClickhandler to it. Sadly, we can’t do it in one step. What we need to do, is basically create 2 components — one with logic, and one that brings styles:

// button.js
import React from 'react';
import styled from 'styled-components';
const Button = ({ children }) => (
<button onClick={() => console.log('click')}>{children}</button>
);
const StyledButton = styled(Button)<br>  border: solid 2px green;<br>;
// usage:
<StyledButton>My fancy button</StyledButton>

So this makes things a bit excessive. Every time you would like to add some logic to a component, you will end up with 2 components. While it is kinda in style of HoC, but it just doesn’t go well with other HoC, so it can’t be used with cool HoC helpers like recompose. I must admit, that I’m a big fan of HoC and not being able to use Styled Components in that fashion just misses the mark for me.

Creating a separate component every time that a styling is needed seems like a good idea, but when I used such approach I suddenly missed the ability to just write a bit more complicated markup. So, in theory this approach is very appealing, but practice shows that sometimes you need to produce much more small components. This can lead to less readable code. Because if I can do something in 20 lines of JSX, I would prefer that over creating 10 small components.

One of cool features of Styled Components, that was just not possible in CSS Modules, is the ability to read components’ props and generate the appropriate styling. Let’s assume that we have 2 props: important and width. They can be accessed inside the style in such manner:

const CustomButton = styled.button<br>  color: ${props =&gt; props.important ? &apos;red&apos; : &apos;black&apos;};<br>  width: ${props =&gt; props.width}px;<br>}</pre><pre id="f4d2" class="graf graf--pre graf-after--pre">&lt;CustomButton important={false} width={320}&gt;My button&lt;/Button&gt;</pre><p id="141a" class="graf graf--p graf-after--pre">Dynamic styles are a pretty cool feature. Although my concern is that it can lead to more logic-heavy styles. Depending on the project, that can be a double edged sword.</p><p id="d441" class="graf graf--p graf-after--h4">Another great feature is theme support. That&#x2019;s right, you can simply define your theme or color palette (as a simple object) and access it&#x2019;s properties whenever you like. But what if I could tell you, that you can change themes on the fly? Yup, it&#x2019;s possible. Let&#x2019;s see it in action:</p><pre id="83e0" class="graf graf--pre graf-after--p">// button.js<br>const ThemableButton = styled.button
color: ${props => props.theme.textColor};
border: ${props => props.theme.borderColor};
;</pre><pre id="566a" class="graf graf--pre graf-after--pre">// themes.js<br>const theme = {<br> textColor: &apos;red&apos;,<br> borderColor: &apos;gray&apos;<br>};</pre><pre id="e096" class="graf graf--pre graf-after--pre">const otherTheme = {<br> ...theme,<br> textColor: &apos;green&apos;<br>};</pre><pre id="ffcb" class="graf graf--pre graf-after--pre">// in app.js<br>&lt;ThemeProvider theme={theme}&gt;<br> &lt;div&gt;<br> &lt;SomeComponent&gt;<br> &lt;ThemableButton&gt;Red button&lt;/ThemableButton&gt;<br> &lt;/SomeComponent&gt;<br> &lt;/div&gt;</pre><pre id="dc9f" class="graf graf--pre graf-after--pre">&lt;ThemeProvider theme={otherTheme}&gt;<br> &lt;ThemableButton&gt;I&apos;m green!&lt;/ThemableButton&gt;<br> &lt;/ThemeProvider&gt;<br>&lt;/ThemeProvider&gt;</pre><p id="1195" class="graf graf--p graf-after--pre">The usage of <code class="markup--code markup--p-code">ThemeProvider</code> allows to add a theme (via property) to all components inside. If you&#x2019;re in need of adding another theme, just place your components inside another <code class="markup--code markup--p-code">ThemeProvider</code> and enter a new theme property. As simple as that. The question arises, how many times you were in need of a theming that can be changed? I didn&#x2019;t see much of such projects recently, but if you need it, it&#x2019;s there.</p><p id="834c" class="graf graf--p graf-after--h4">Now, from debugging perspective, the generated output is fairly good. The <code class="markup--code markup--p-code">StyledButton</code> from the example above will not render a component inside a component, which is good. At the very beginning only the class names are unreadable (such as in case of CSS Modules). But there&#x2019;s a Babel <a href="https://github.com/styled-components/babel-plugin-styled-components" class="markup--anchor markup--p-anchor">plugin for that</a>, and you&#x2019;re good to go.</p><p id="947a" class="graf graf--p graf-after--p">Pros:</p><ul class="postList"><li id="c4e4" class="graf graf--li graf-after--p">Easy to use, easy to understand.</li><li id="b5d2" class="graf graf--li graf-after--li">Local scope</li><li id="c8b6" class="graf graf--li graf-after--li">Dynamic styling based on props</li><li id="aea4" class="graf graf--li graf-after--li">Theming</li><li id="a0c5" class="graf graf--li graf-after--li">Easy debugging (with babel plugin)</li><li id="9439" class="graf graf--li graf-after--li">Works with React Native (although I didn&#x2019;t test that)</li></ul><p id="4c4e" class="graf graf--p graf-after--li">Cons:</p><ul class="postList"><li id="4d59" class="graf graf--li graf-after--p">Excessive creation of components (2 components each time handler or lifecycle method needs to be used)</li><li id="f1fe" class="graf graf--li graf-after--li">Possible too much of too small components</li><li id="6cc5" class="graf graf--li graf-after--li">Syntax highlighting needs an external plugin</li></ul><p id="9f71" class="graf graf--p graf-after--h3"><a href="https://glamorous.rocks/" class="markup--anchor markup--p-anchor">Glamorous</a> takes inspiration from Styled Components and aims to change a bit the way you can use it. Notably, the biggest difference is the departure from tagged template strings in favor of plain objects. You still would use it in a similar way, but replacing string with objects. Just take a look:</p><pre id="39c1" class="graf graf--pre graf-after--p">import glamorous from &apos;glamorous&apos;;</pre><pre id="ed05" class="graf graf--pre graf-after--pre"><code class="markup--code markup--pre-code">const Heading = glamorous.h1({<br> fontSize: &apos;2.4em&apos;,<br> marginTop: 10,<br> color: &apos;#CC3A4B&apos;<br>})</code></pre><pre id="fbca" class="graf graf--pre graf-after--pre">// rendering<br>render() {<br> return (&lt;Heading&gt;My Heading&lt;/Heading&gt;);<br>}</pre><p id="7b2f" class="graf graf--p graf-after--pre">To use media queries, pseudo classes and pseudo elements, simply create nested objects with selectors as their names, like so:</p><pre id="cf3e" class="graf graf--pre graf-after--p">import glamorous from &apos;glamorous&apos;;</pre><pre id="7ba5" class="graf graf--pre graf-after--pre"><code class="markup--code markup--pre-code">const Heading = glamorous.h1({<br> fontSize: &apos;2.4em&apos;,<br> marginTop: 10,<br> color: &apos;#CC3A4B&apos;,<br> <br> &apos;:hover&apos;: {<br> color: &apos;yellow&apos;<br> }</code></pre><pre id="6d18" class="graf graf--pre graf-after--pre"><code class="markup--code markup--pre-code">&apos;</code>@media only screen and (max-width: 500px)&apos;: {<br> marginTop: 5,<br> fontSize: &apos;1.8em&apos;<br> }<br><code class="markup--code markup--pre-code">});</code></pre><pre id="f966" class="graf graf--pre graf-after--pre">// rendering<br>render() {<br> return (&lt;Heading&gt;My Heading&lt;/Heading&gt;);<br>}</pre><p id="f3cd" class="graf graf--p graf-after--h4">Well, this is something that does not come with Styled Components. Glamorous has a build in helper for creating CSS animations. That&#x2019;s something really cool. Nowadays fluid animations and transitions are important parts of good UX and everything that improves creation of those effects is more then welcome.</p><pre id="ae0e" class="graf graf--pre graf-after--p">const AnimatedDiv = glamorous.div((props) =&gt; {<br> const bounce = glamor.css.keyframes({<br> &apos;0%&apos;: { transform:scale(1.01)},<br> &apos;100%&apos;: { transform:scale(0.99)}<br> })</pre><pre id="17a3" class="graf graf--pre graf-after--pre">return {<br> animation:${bounce} 0.2s infinite alternate<br> }<br>});</pre><p id="6915" class="graf graf--p graf-after--pre">Okay, so it&#x2019;s not a game changing functionality. But it allows to create unique keyframes animations in a simple manner.</p><p id="f730" class="graf graf--p graf-after--h4">Just like in Styled Components, Glamorous has the ability to use dynamic styling. In my opinion, it does it in much clearer way. Because we work with objects, not with string literals, conditional operations are much more natural. Simply, instead of object, pass a function that receives <code class="markup--code markup--p-code">props</code> and returns a valid object.</p><pre id="b14a" class="graf graf--pre graf-after--p">const CustomButton = glamorous.button((props) =&gt; ({<br> color: props.important ? &apos;red&apos; : &apos;black&apos;,<br> width: props.width<br>});</pre><pre id="1a89" class="graf graf--pre graf-after--pre">&lt;CustomButton important={false} width={320}&gt;My button&lt;/Button&gt;</pre><p id="8fe8" class="graf graf--p graf-after--pre">Obviously, having the ability to pass a function into the component factory gives you much more flexibility, but allows to create even more logic-heavy styles. Double edged sword once again.</p><p id="dbcc" class="graf graf--p graf-after--h4">Theming for Glamorous looks like it was taken directly from Styled Components, just define a <code class="markup--code markup--p-code">theme</code> object and pass it to your components. They will have access to it via <code class="markup--code markup--p-code">props.theme</code>. Simple as that.</p><p id="3457" class="graf graf--p graf-after--p">Glamorous allows also to reuse and extend existing styles. For that you can use the <code class="markup--code markup--p-code">glamorous</code> object as a function, pass the component and then styles to override the existing ones.</p><pre id="f5f7" class="graf graf--pre graf-after--p">const GreatButton = glamorous(CustomButton)({<br> padding: 20<br>});</pre><p id="f3a0" class="graf graf--p graf-after--pre">Moreover, it allows you to copy existing styles to a different tag via <code class="markup--code markup--p-code">withComponent</code> method:</p><pre id="5c81" class="graf graf--pre graf-after--p">const GreatLink = GreatButton.withComponent(&apos;a&apos;);</pre><p id="6d9e" class="graf graf--p graf-after--h4">When it comes to overall usage, Glamorous suffers from the same drawback of not really being in compliant with HoC approach. So, once again, to build a more advanced component with styling, you will need to create 2 components. Let&#x2019;s revisit the example from Styled Components:</p><pre id="4df4" class="graf graf--pre graf-after--p">// button.js<br>import React from &apos;react&apos;;<br>import glamorous from &apos;glamorous&apos;;</pre><pre id="b85b" class="graf graf--pre graf-after--pre">const Button = ({ children }) =&gt; (<br> &lt;button onClick={() =&gt; console.log(&apos;click&apos;)}&gt;{children}&lt;/button&gt;<br>);</pre><pre id="4736" class="graf graf--pre graf-after--pre">const StyledButton = glamorous(Button)({<br> border: &apos;solid 2px green&apos;<br>});</pre><pre id="20bc" class="graf graf--pre graf-after--pre">// usage:<br>&lt;StyledButton&gt;My fancy button&lt;/StyledButton&gt;</pre><p id="19ac" class="graf graf--p graf-after--pre">So this comes with the same issue: too many small components.</p><p id="fbbb" class="graf graf--p graf-after--h4">When it comes to debugging, Glamorous on it&#x2019;s own is not very readable in the DevTools. But, obviously, there&#x2019;s a <a href="https://github.com/bernard-lin/babel-plugin-glamorous-displayname" class="markup--anchor markup--p-anchor">plugin</a> for that.</p><p id="0a68" class="graf graf--p graf-after--p">Pros:</p><ul class="postList"><li id="aac7" class="graf graf--li graf-after--p"><code class="markup--code markup--li-code">...styledComponents.pros</code></li><li id="4267" class="graf graf--li graf-after--li">Object notation instead template literals</li><li id="0522" class="graf graf--li graf-after--li">Keyframes helper</li></ul><p id="2248" class="graf graf--p graf-after--li">Cons:</p><ul class="postList"><li id="04ed" class="graf graf--li graf-after--p">(Potentially) excessive components creation</li></ul><p id="fe38" class="graf graf--p graf-after--h3">When it comes to <a href="https://github.com/rtsao/styletron" class="markup--anchor markup--p-anchor">Styletron</a>, I&#x2019;m probably quite biased. I have become involved in a project using it and it quickly pushed me to do research on other similar solutions to migrate away to. I felt so limited and distracted by it so I couldn&#x2019;t just bare with styling anymore. I get the impression, that Styletron does a lot of things similar to Glamorous, but fails to deliver really usable in real-life solution. Let&#x2019;s start with basics:</p><pre id="09e7" class="graf graf--pre graf-after--p">import { styled } from &apos;styletron-react&apos;;</pre><pre id="14fa" class="graf graf--pre graf-after--pre"><code class="markup--code markup--pre-code">const Heading = styled(&apos;h1&apos;, {<br> fontSize: &apos;2.4em&apos;,<br> marginTop: 10,<br> color: &apos;#CC3A4B&apos;<br>});</code></pre><pre id="8162" class="graf graf--pre graf-after--pre">// rendering<br>render() {<br> return (&lt;Heading&gt;My Heading&lt;/Heading&gt;);<br>}</pre><p id="23e1" class="graf graf--p graf-after--pre">When it comes to simple usage, it&#x2019;s almost the same as in Glamorous.</p><p id="e418" class="graf graf--p graf-after--h4">Styletron provides ways to extend existing styles, or to build dynamic styles. It also supports theming in a very similar fashion, as we have seen previously. Let&#x2019;s see all of those features in action together:</p><pre id="1c86" class="graf graf--pre graf-after--p">import { styled } from &apos;styletron-react&apos;;<br>import ThemeProvider from &apos;./ThemeProvider&apos;;<br>import Button from &apos;./GenericButton&apos;;</pre><pre id="5ec5" class="graf graf--pre graf-after--pre">const FancyButton = styled(GenericButton, ({ theme, size }) =&gt; {<br> return { <br> color: theme.button.color,<br> background: theme.button.background,<br> height: (size === &apos;big&apos;) ? &apos;30px&apos;, &apos;20px&apos;,<br> width: (size === &apos;big&apos;) ? &apos;100px&apos;, &apos;60px&apos;<br> };<br>});</pre><pre id="fef5" class="graf graf--pre graf-after--pre">const MyTheme = {<br> button: {<br> color: &apos;#ee3ae4&apos;,<br> background: &apos;#323c4d&apos;<br> }<br>}</pre><pre id="8c91" class="graf graf--pre graf-after--pre">// rendering<br>&lt;ThemeProvider theme={MyTheme}&gt;<br> &lt;FancyButton size=&quot;big&quot;&gt;Click me&lt;/FancyButton&gt;<br>&lt;/ThemeProvider&gt;</pre><p id="c330" class="graf graf--p graf-after--pre">Similar to previous solutions, Styletron does not allow to create more lifecycle-heavy components with styles. Instead, you need to create 2 components, one with logic, and one with styles:</p><pre id="8d69" class="graf graf--pre graf-after--p">class MyComponent extends React.Component {<br> componendDidMount() {<br> // some awesome logic here<br> }</pre><pre id="6c7f" class="graf graf--pre graf-after--pre">render() {<br> return (&lt;div&gt;My Component&lt;/div&gt;)<br> }<br>}</pre><pre id="4384" class="graf graf--pre graf-after--pre">const StyledComponent = styled(MyComponent, { /* styles */ });</pre><p id="d963" class="graf graf--p graf-after--pre">So, let&#x2019;s see, what Styletron renders in HTML&#x2026;</p><p id="f933" class="graf graf--p graf-after--h4">The output for the example above will be something like:</p><pre id="4409" class="graf graf--pre graf-after--p">&lt;div class=&quot;_c _d _g x1 x3 xm _a&quot;&gt;<br> &lt;div&gt;<br> My Component<br> &lt;/div&gt;<br>&lt;/div&gt;</pre><p id="36a1" class="graf graf--p graf-after--pre">And two issues can be seen here. Firstly, that the rendered code is totally not debuggable. You can&#x2019;t really deduce from which component any of the styles really come. Or even what component you&#x2019;re looking at. Switching to React DevTools doesn&#x2019;t help either. All you gonna see is that it&#x2019;s <code class="markup--code markup--p-code">&lt;styled(div)&gt;</code>. That&#x2019;s really helpful.</p><p id="baa6" class="graf graf--p graf-after--p">Oh, and each style property gets it&#x2019;s own, unique class name. Imagine having a well styled component with over 30 properties. Yeah&#x2026;</p><pre id="3222" class="graf graf--pre graf-after--p">&lt;div class=&quot;_a _b _c _d _e _f _m1 _m2 _m3 _m4 _m17 _m20 _m22 _m33<br> _m45 _m46 _la _lg _lz _lh _l5 _hm _hn _hi x1 x12 x13<br> x15 x16 x21 x22 x23 x24 x25 x26 x30 x32 x33 x34&quot;&gt; <br> Hi<br>&lt;/div&gt;</pre><p id="463b" class="graf graf--p graf-after--pre">And what&#x2019;s even worse, there is <strong class="markup--strong markup--p-strong">no debugging plugin</strong> for it!</p><p id="5653" class="graf graf--p graf-after--p">Moreover, imagine that the example would have 10 nested components, one inside another. Could you tell the number of generated divs? Yup, 20. Because every time you need to have more advanced component, Styletron will need to add additional wrapper for styling purposes. That&#x2019;s something I like to call a <em class="markup--em markup--p-em">Wrapper-iada</em>. And it&#x2019;s no good.</p><p id="52b4" class="graf graf--p graf-after--h4">Styletron has a bug that&#x2019;s basically prevents you from using dynamic tags (so the tags used depending on the props). Let me explain:</p><pre id="ebcb" class="graf graf--pre graf-after--p">const headers = {<br> h1: &apos;h1&apos;,<br> h2: &apos;h2&apos;,<br> h3: &apos;h3<br>}</pre><pre id="08b2" class="graf graf--pre graf-after--pre">const DynamicHeader = ({ size, children }) =&gt; {<br> const Element = headers[size];</pre><pre id="3c99" class="graf graf--pre graf-after--pre">return (<br> &lt;Element&gt;{children}&lt;/Element&gt;<br> );<br>};</pre><p id="cdd2" class="graf graf--p graf-after--pre">Now, if I would like to add styling to my <code class="markup--code markup--p-code">DynamicHeader</code>, I would go with:</p><pre id="f971" class="graf graf--pre graf-after--p">const StyledHeader = styled(DynamicHeader, { /* new styles */ });</pre><p id="037b" class="graf graf--p graf-after--pre">None of the <code class="markup--code markup--p-code">new styles</code> won&#x2019;t be applied. Why? I don&#x2019;t have an idea, but it&#x2019;s not good.</p><p id="5206" class="graf graf--p graf-after--h4">That&#x2019;s right, Styletron does not support css keyframes! Obviously, you can use animation like the following:</p><pre id="ecb3" class="graf graf--pre graf-after--p">const BouncyDiv = styled(&apos;div&apos;, {<br> animation: &apos;bounce 1s&apos;<br>});</pre><p id="3992" class="graf graf--p graf-after--pre">And it will work. But you can&#x2019;t define the keyframes of <code class="markup--code markup--p-code">bounce</code> using Styletron. If you would really like to somehow define keyframes, you need to use good, ol&#x2019; CSS. That&#x2019;s a terrible solution.</p><p id="3613" class="graf graf--p graf-after--p">Let&#x2019;s sum up:</p><p id="7d33" class="graf graf--p graf-after--p">Pros:</p><ul class="postList"><li id="8d06" class="graf graf--li graf-after--p">Looks similar to Glamorous</li><li id="073b" class="graf graf--li graf-after--li">Local scope</li><li id="f466" class="graf graf--li graf-after--li">Theming, dynamic styles, extending styles</li></ul><p id="1993" class="graf graf--p graf-after--li">Cons:</p><ul class="postList"><li id="0eaa" class="graf graf--li graf-after--p">Undebuggable (!)</li><li id="8e7f" class="graf graf--li graf-after--li">Wrapper-iada</li><li id="7eed" class="graf graf--li graf-after--li">No dynamic elements</li><li id="14df" class="graf graf--li graf-after--li">No keyframes support</li></ul><p id="6b30" class="graf graf--p graf-after--h3">Next CSS-in-JS solution that I researched was <a href="https://github.com/zeit/styled-jsx" class="markup--anchor markup--p-anchor">Styled JSX</a>. At the very beginning it looks different to the solutions above, it kinda looks more inline with JSX approach and I find it a bit more appealing. The basic example looks like this:</p><pre id="a151" class="graf graf--pre graf-after--p">const MyComponent () =&gt; {<br> return (<br> &lt;div&gt;<br> &lt;h1&gt;Heading&lt;/h1&gt;<br> &lt;p&gt;Paragraph 1&lt;/p&gt;<br> &lt;p className=&quot;p2&quot;&gt;Paragraph 2&lt;/p&gt;</pre><pre id="2926" class="graf graf--pre graf-after--pre">&lt;style jsx&gt;{
h1 {
font-size: 1.6em;
}
        p {
line-height: 1.5em;
}
        .p2 {
color: gray;
}
}&lt;/style&gt;<br> &lt;/div&gt;<br> );<br>};</pre><p id="309d" class="graf graf--p graf-after--pre">Styled JSX uses template literals inside of a <code class="markup--code markup--p-code">&lt;style jsx&gt;</code> tag. What&#x2019;s really neat, is that you can use the plain old CSS inside. It&#x2019;s readable, it&#x2019;s safe due to local scope. If you like, you can move the string to a separate file because everything is in a string. If you&#x2019;re running React in versions 16.2 or higher, you can replace the <code class="markup--code markup--p-code">div</code> with React&#x2019;s fragment.</p><p id="5135" class="graf graf--p graf-after--h4">Obviously, defining the styles inside component&#x2019;s render method allows us to access all of the props and hence build dynamic styles. Or you can access the theme provided by ThemeProvider although you need to use additional module for that, as Styled JSX don&#x2019;t have a built in ThemeProvider.</p><pre id="6991" class="graf graf--pre graf-after--p">const MyComponent ({ theme, size }) =&gt; {<br> return (<br> &lt;div&gt;<br> &lt;h1&gt;Heading&lt;/h1&gt;</pre><pre id="579e" class="graf graf--pre graf-after--pre"> &lt;style jsx&gt;{
h1 {
font-size: ${size}px;
color: ${theme.heading.color};
}
`}</style>
</div>
);
};

What I like here, is that I can write styles and apply them to components with lifecycle methods. Another thing to like is that Styled JSX integrates nicely with React’s idea of rendering components. That was not possible with any of the previous libraries. But here I can decide when I should split component into smaller ones. That kind of freedom is something I really appreciate. Artificial code splitting forced by previous solutions does not always end up in a very readable code.

I must say I really like this approach, but the drawbacks just kill it for me. The problems start when you want to extend existing styling. Or when you would like to alter the default styling of a child component. There is no easy way of doing such things. While in Glamorous all you needed was to call glamorous(DefaultButton)(newStyles), in Styled JSX you need to come up with your own approach to reusing existing styling. So as I said before, this kills the Styled JSX for me.

Pros:

  • More inline with JSX approach
  • Doesn’t force artificial code splitting
  • Easy to understand
  • Local scope
  • Dynamic Styles, Themes support

Cons:

  • Reusability of styles is hard if non-existent

The last but not least — JSS. At first glance, JSS looks very similar to Glamorous. You create objects literals with styles, you apply them to components. But it also successfully adds the ability to write couple styles for multiple tags inside one component.

import withStyles from 'react-jss';
const styles = {
heading: {
fontSize: '1.6em'
},
paragraph: {
lineHeight: '1.4em'
}
};
const MyComponent ({ classes }) => {
return (
<div>
<h1 className={classes.heading}>
Heading
</h1>
<p className={classes.paragraph}>
Paragraph 1
</p>
</div>
);
};
export default withStyles(styles)(MyComponent);

First what I have to admit, and what really got me excited about JSS is that finally, it’s a HoC! You pass a object with different class names to it, and the HoC will produce a wrapper that adds a classes property containing generated, unique class names. In a way, it brings the functionality of both Styled JSX and Glamorous neatly together. That’s off to a good start.

Just like abovementioned solutions, JSS supports both themes and dynamic styles. It comes with build in ThemeProvider which is always a neat solution.

// component.js
import withStyles from 'react-jss';
const styles = (theme) => ({
wrapper: {
fontSize: '1.4em',
color: theme.primaryColor,
height: (props) => props.size
}
});
const Component = ({ classes }) => {
return (
<div className={classes.wrapper}>
My fancy component
</div>
);
};
export default withStyles(styles)(Component);
// app.js
import { ThemeProvider } from 'react-jss';
const theme = {
primaryColor: '#51abff'
};
// rendering
<ThemeProvider theme={theme}>
<Component size="32px" />
</ThemeProvider>

The theme object is available when instead of styles object, you create a function that returns the styles object. Component’s props are available in a slightly more annoying way, as a argument for function returning value of each property.

When it comes to extending styles, well, JSS does not provide any simple way of doing so. But, then again, they are all JS object, so we can export styles and extend them on our own using the spread operator.

// component.js
export const styles = {
wrapper: {
// original styles
}
};
const Component = () => {
// Component's logic
};
export default withStyles(styles)(Component);
// fancy-component.js
import { styles as defaultStyles } from './component';
export const styles = {
wrapper: {
...defaultStyles.wrapper,
color: 'red'
}
}

A bit of work, but it’s very explicit. No under-the-hood magic. There is a extend-plugin, however its usage is very similar to the spread operator.

JSS has a neat feature that is not available in other libraries, and that’s ability to override default styles of the component. You can call the withStyles HoC again and again on component, overriding it’s default styles, like so:

// componentA.js
const styles = {
wrapper: {
color: 'red',
fontSize: '1.4em'
}
paragraph: {
fontSize: '1.2em',
color: 'gray'
}
};
const ComponentA = () => {
// ...
};
export default withStyles(styles)(ComponentA);
// componentB.js
const stylesA = {
wrapper: {
color: 'blue'
}
};
const StyledA = withStyles(stylesA)(ComponentA);
const ComponentB = () => {
return (
<div>
<StyledA />
</div>
);
};

Because withStyles performs a shallow merge of the style objects, the StyledA component will contain the original styles for paragraph, but it will have only the newest styles for wrapper. This means that it will have only the color: blue property but it will lose the fontSize: 1.4em property.

You can obviously combine the two methods to override styles with extending the existing ones. However, since it is a HoC, you can write your own HoC that does just that.

JSS comes with variety of plugins. You can use them, for example, to write styles inside template literals, if that’s your thing, or to extend existing classes via class names or objects.

When it comes to debugging, JSS is very user friendly. At the very beginning, it renders human-friendly class names, so you can quickly find your way around the generated HTML markup. It also does not change the generated components’ names, as it only works on the className property, which is quite nice.

Pros:

  • Local Scope
  • Theming, dynamic styles
  • No artificial code splitting
  • Overriding default styles (!)
  • works as a HoC

Cons

  • No native style extending solution (but you can work around this)

It’s time to sum up. Is there a clear winner? In my opinion, yes. JSS takes the top spot. It gives me all of the features of other CSS-in-JS solutions and doesn’t forces me to create super-small components each time I need styling for a div. In addition to that, it works as a HoC, so if I don’t like the default behavior (like when overriding styles) I can always create my own HoC that wraps it and changes the behavior. It’s simple to use and flexible. And at the end of the day, it’s the solution that devs from the Material UI are gonna use. For me, that’s a hell of a recommendation for JSS.

As I already mentioned, I thought that SASS + CSS Modules were the future. But now I can clearly see, that CSS in JS is at least as good, with JSS being my favorite. It’s really usable and works great in the real-life development. My personal pick after doing the research: JSS.


Tag cloud