Esteban Herrera Family man. Java and JavaScript developer. Swift and VR/AR hobbyist. Like books, movies, and still trying many things. Find me at eherrera.net

5 things you didn’t know you can do in CSS-in-JS

5 min read 1426

5 Things You Can Do with CSS-in-JS

Editor’s note: This post was last updated on 29 July 2021 for accuracy and clarity. For more up-to-date information on CSS-in-JS, you can also check out this article on CSS-in-JS libraries.

What does CSS-in-JS solve?

In addition to traditional CSS, inline styles and CSS-in-JS can also be used to style React applications.

Even though inline styles enable you to pass a JavaScript object to the style attribute (as seen in the code snippet below), it doesn’t support all CSS features:

import React from "react";

function App() {
  const myStyle = {
    fontSize: 24,
    lineHeight: "1.3em",
    fontWeight: "bold"
  };

  return (
    <div>
      <p style={myStyle}>Hello world</p>
    </div>
  );
}
export default App;

On the other hand, CSS-in-JS libraries like Aphrodite, styled-components, JSS, Emotion, Radium, etc. give developers the ability to not only style components with JavaScript but also tackle some CSS limitations, such as the lack of dynamic functionality, scoping, and portability when using them:

// Here's an implementation of the inline style code snippet above using Aphrodite

import React from "react";
import { StyleSheet, css } from "aphrodite";

function App() {
  const styles = StyleSheet.create({
    myStyle: {
      fontSize: 24,
      lineHeight: "1.3em",
      fontWeight: "bold"
    }
  });
  return (
    <div>
      <span className={css(styles.myStyle)}>Hello World!</span>
    </div>
  );
}
export default App;

In this article, I will highlight five things you didn’t know you could do in CSS-in-JS using the CSS-in-JS libraries mentioned above as a case study. 👇

1. CSS-in-JS lets you refer to other styled components

Libraries like styled-components and Emotion allow you to use tagged template literals to create React components from styles:

// Create a component that renders a <p> element with blue text
import React from "react";
import styled from "styled-components";

function App() {
  const BlueText = styled.p`
    color: blue;
  `;

  return (
    <div>
      <BlueText>My blue text</BlueText>
    </div>
  );
}

export default App;

But they also allow you to target other styled components (like if you were using CSS selectors):

import React from "react";
import styled from "styled-components";

function App() {
  const ImportantText = styled.div`
    font-weight: bold;
  `;

  const Text = styled.div`
    color: gray;
    ${ImportantText} {
      font-style: italic;
    }
  `;

  return (
    <div>
      <Text>
        Text in gray
        <ImportantText>Important text in gray, bold and italic</ImportantText>
      </Text>
      <ImportantText>Important text bold</ImportantText>
    </div>
  );
}

export default App;

This is useful when it is combined with pseudo-classes; for example, to change the color of a component on hover:

import React from "react";
import styled from "styled-components";

function App() {
  const ImportantText = styled.div`
    font-weight: bold;
  `;

  const Text = styled.div`
    color: gray;
    &:hover ${ImportantText} {
      color: red;
    }
  `;

  return (
    <div>
      <Text>
        Text in gray
        <ImportantText>Important text in gray, bold and italic</ImportantText>
      </Text>
      <ImportantText>Important text bold</ImportantText>
    </div>
  );
}

export default App;

2. JSS (or other libraries) can extend the features of some libraries

Let’s say you’ve used Aphrodite to style your application and now you need to support themes.

The problem is that Aphrodite doesn’t support theming easily. At least, not as easy as Emotion does.

We made a custom demo for .
No really. Click here to check it out.

However, there are two projects that bridge the core of JSS with Aphrodite and styled-components: aphrodite-jss and styled-jss.

This way, you can keep the good parts of Aphrodite (or styled-components) and use all the features and plugins of JSS, from rule caching to rule isolation, and for themes, the theming package, which provides the following high-order components:

  • ThemeProvider, which passes a theme object down the React tree by context
  • withTheme, which allows you to receive a theme object and it updates as a property

For example:

import React from "react";
import { createUseStyles, ThemeProvider, useTheme } from "react-jss";

function App() {
  const useStyles = createUseStyles({
    wrapper: {
      padding: 40,
      background: ({ theme }) => theme.background,
      textAlign: "left"
    },
    title: {
      font: {
        size: 40,
        weight: 900
      },
      color: ({ theme }) => theme.color
    },
    link: {
      color: ({ theme }) => theme.color,
      "&:hover": {
        opacity: 0.5
      }
    }
  });

  const Comp = () => {
    const theme = useTheme();
    const classes = useStyles({ theme });
    return (
      <div className={classes.wrapper}>
        <h1 className={classes.title}>Hello There!</h1>
      </div>
    );
  };

  const theme = {
    background: "blue",
    color: "white"
  };

  return (
    <div>
      <ThemeProvider theme={theme}>
        <Comp />
      </ThemeProvider>
    </div>
  );
}

export default App;

In the particular case of Aphrodite and themes, as another example, you can also use react-with-styles, which interfaces with Aphrodite and JSS, among others, to access theme information when defining styles.

3. CSS-in-JS can chain multiple animations with keyframes

Unlike inline styles, CSS-in-JS allows you to define animations using keyframes.

For example, this is how it’s done with styled-components:

import React from "react";
import styled, { keyframes } from "styled-components";

function App() {
  const MoveAnimation = keyframes`
  0% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(50px, 0);
  }
`;

  const MyComponent = styled.div`
    display: inline-block;
    margin: 50px;
    width: 200;
    position: relative;
    animation: ${MoveAnimation} 1.5s ease infinite;
  `;

  return (
    <div>
      <MyComponent>Hello There!</MyComponent>
    </div>
  );
}

export default App;

But what not many people know is that you can chain multiple animations by using more than one keyframe object in the animation property.

Here’s the above example modified to combine two animations:

import React from "react";
import styled, { css, keyframes } from "styled-components";

function App() {
  const MoveAnimation = keyframes`
  0% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(50px, 0);
  }
`;

  const ColorAnimation = keyframes`
  from {color: red;}
  to {color: blue;}
`;

  const MyComponent = styled.div`
    display: inline-block;
    margin: 50px;
    width: 200;
    position: relative;
    animation: ${(props) => css`
      ${MoveAnimation} 1.5s ease infinite,
      ${ColorAnimation} 1.5s linear infinite
    `};
  `;

  return (
    <div>
      <MyComponent>Hello There!</MyComponent>
    </div>
  );
}

export default App;

4. You can declare global styles with CSS-in-JS

Everything in CSS is global, and one of the purposes of using CSS-in-JS is to eliminate global style definitions.

However, there may be valid uses of global styles; for example, when you want to apply the same font styles to every element in your page.

Of course, you can always use traditional CSS, importing it via Webpack or declaring it in the index.html file.

But if you’re serious about using JavaScript for all your styles, some libraries actually allow you to define global styles via helper components or extensions/plugins.

In Radium, you can use the Style component to render a styled element with global styles.

For example:

<Style
  rules={{
    body: {
      fontFamily: 'Arial, Helvetica, sans-serif'
    }
  }}
/>

Will return:

<style>
body {
  font-family: 'Arial, Helvetica, sans-serif';
}
</style>

JSS uses a plugin to write global styles:

const styles = {
  '@global': {
    body: {
      fontFamily: 'Arial, Helvetica, sans-serif'
    }
  }
}

Which will return:

body {
  font-family: 'Arial, Helvetica, sans-serif';
}

And in Aphrodite, you can use a third-party extension to create styles. For example:

import {injectGlobalStyles} from "aphrodite-globals";

injectGlobalStyles({
    "body": {
          fontFamily: 'Arial, Helvetica, sans-serif',
    }
});

This will return:

    body {
      font-family: 'Arial, Helvetica, sans-serif';
    }

5. Some CSS-in-JS libraries can test components with styles in unit tests

Some libraries contain utilities for testing components with styles.

Aphrodite provides an undocumented (at least at the time of writing this) object, StyleSheetTestUtils, which is only available for non-production environments (process.env.NODE_ENV !== 'production') and has three methods:

  • suppressStyleInjection, which prevent styles from being injected into the DOM, and it’s useful when you want to test the output of Aphrodite components when you have no DOM
  • clearBufferAndResumeStyleInjection, which does the opposite of suppressStyleInjection and should be paired with it
  • getBufferedStyles, which returns a string of buffered styles that have not been flushed

Here’s an example of how they are used:

import { StyleSheetTestUtils, css } from 'aphrodite';
//...

beforeEach(() => {
  StyleSheetTestUtils.suppressStyleInjection();
});

afterEach(() => {
  StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
});

test('my test', () => {
  const sheet = StyleSheet.create({
    background: {
      backgroundColor: 'blue'
    },
  });
  css(sheet.background);

 const buffer = StyleSheetTestUtils.getBufferedStyles();
});

Radium is another example. It has a TestMode object for controlling internal state and behavior during tests with the methods clearState, enable, and disable.

Here, you can find an example of how TestMode is used.

Conclusion

CSS-in-JS is a technique for styling applications with JavaScript, and you can do interesting things with the libraries that implement it.

In this post, I have shown you five things that probably you didn’t know you can do with some of these libraries. Of course, not all libraries are created equal, and some things only apply to specific libraries.

Check out this playground where you can test and compare many CSS-in-JS libraries.

On the other hand, there are other libraries that are taking the concept of CSS, JavaScript, and types a little bit further.

One of these libraries is stylable, a component-based library with a preprocessor that converts Stylable’s CSS into minimal and cross-browser vanilla CSS.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Esteban Herrera Family man. Java and JavaScript developer. Swift and VR/AR hobbyist. Like books, movies, and still trying many things. Find me at eherrera.net

Leave a Reply