Thoughts on Linaria CSS-in-JS library

Thoughts on Linaria CSS-in-JS library

A look at Linaria, a CSS-in-JS library with zero runtime.

Linaria is a CSS-in-JS library with zero runtime. At the end of the day it generates static CSS from your code.

The Good

Linaria is a good option for building static sites. Some of the places where it shines are:

Performance

Since Linaria has zero runtime, it means that nothing will run on the main thread and your UI will feel snappier. Also, it won't be included in your JavaScript bundle. Usually less JavaScript equals faster webpage loads and less time required by the browser to process your code (better TTI).

CSS Variables

Don't let the words "static" and "zero runtime" fool you. Linaria comes with a very powerful system to dynamically style components with the help of CSS Variables or CSS Custom Properties. So you could easily create a Button component which expects a color prop which could be dynamically updated at runtime.

let Button = styled.button`
    color: ${props => props.color};
`

<Button color={isDisabled ? "gray" : "blue"} />

The Bad

As the complexity of an app grows, we start seeing few of Linaria's drawbacks that require some working around.

Responsive Design

With Linaria you are limited to vanilla CSS and JS techniques when it comes to responsive design. Coming from other CSS-in-JS libraries you get used to some niceties they provide for responsive styling.

For example stitches provides an object syntax for specifying variants at different breakpoints and so does theme-ui with their sx prop and array syntax. styled-components and emotion support utilities such as facepaint and styled-system that allow you to pass props as an array such as:

// responsive font-size
<Text fontSize={[ 3, 4, 5 ]} />

Bloated HTML Markup

This is a big drawback especially if you server render your app and if you end up using lots of dynamic props based styles as demonstrated in the CSS Variables example.

thoughts-on-linaria-css-in-js-library.png

CSS Variables defined as inline styles bloating up HTML

The Ugly

CSS Variables and Inheritance

CSS pop quiz: What is the color of span? Or shall I say who is the rightful heir of span's color property?

<style>
    :root {
      --color: blue;
    }

    p {
      color: red;
    }

    span {
      --color: inherit;
      color: var(--color);
    }
</style>
<p>
  <span>Lorem ipsum dolor sit amet<span>
</p>

One might think that the keyword inherit would be applied to the color property thus resulting in red. But in reality CSS variables have their own inheritance, thus yielding color blue inherited from the CSS property --color itself.

This translates to a frustrating scenario in Linaria when you are trying to apply the keyword inherit to a dynamic property. Since it internally uses CSS Variables the outcome ends up being not what you expected.

const Text = styled.p`
  color: ${(props) => props.color || "inherit"};
`

<Text color="inherit">Hello</Text>

Play with it yourself below.

Nesting Styled Components

When nesting the same styled components, children end up overriding properties provided by parent selectors due to the reuse of same css variable names.

Consider the following example from an issue here:

const Column = styled.div`
  & > * + * {
    margin-top: ${({ spacing }) => spacing || '0'};
  }
`

<Column spacing='30px'>
  <Column></Column>
  <Column></Column>
</Column>

The second nested Column div should have a margin-top property of 30px. But what happens is it get's overriden by the default 0 because CSS variables follow the cascade (duh!)

Play with it yourself on codesandbox