DEV Community

Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

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

In addition to traditional CSS, you also have inline styles and CSS-in-JS as options for styling a React application.

With inline styles, you pass a JavaScript object to the style attribute:

const myStyle = {
  fontSize: 24,
  lineHeight: '1.3em',
  fontWeight: 'bold',
};

<span style={myStyle}>Hello World!</p>

However, not all CSS features are supported.

On the other hand, CSS-in-JS is a technique where JavaScript is used to style components. When this JavaScript is parsed, CSS is generated (usually as a <style> element) and attached into the DOM.

This functionality is implemented by third-party libraries. For example, here’s the previous example implemented with Aphrodite:

import { StyleSheet, css } from 'aphrodite';
const styles = StyleSheet.create({
    myStyle: {
        fontSize: 24,
        lineHeight: '1.3em',
        fontWeight: 'bold',
    }
});

<span className={css(styles.myStyle)}>Hello World!</p>

Other libraries I can recommend are:

I’m not completely in favor of CSS-in-JS, but I have to say that some of these libraries add support for features you might find helpful in certain situations.

In this post, I’ll talk about five things you can do in CSS-in-JS with the above libraries that I bet you didn’t know about.

LogRocket Free Trial Banner

1. You can refer to other styled components

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

import styled from 'styled-components';
// Create a component that renders a <p> element with blue text
const BlueText = styled.p`
  color: blue;
`;

<BlueText>My blue text</BlueText>

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

const ImportantText = styled.div`
   font-weight: bold;
`;

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

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

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

const Text = styled.div`
  color: gray;

  &:hover ${ImportantText} {
    color: red;
  }
`;

2. You can extend the features of some libraries with JSS (or other 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 in an easy way. At least not as easy as Emotion does.

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 its updates as a property.

For example:

const blackTheme = {
  color: 'black',
};

const App = () => (
  <ThemeProvider theme={blackTheme}>
    <MyComponent />
  </ThemeProvider>
);

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 other, to access theme information when defining styles.

3. 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:

const heightAnimation = keyframes`
  0% { height: 0;  }
  100% { height: 200; }
`;

const myComponent = styled.div`
  display: inline-block;
  width: 200;
  position: relative;
  animation-name: ${heightAnimation};
  animation-duration: 1.5s;
  animation-timing-function: ease;
`;

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

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

const heightAnimation = keyframes`
  0% { height: 0; }
  100% { height: 200; }
`;

const rotateAnimation = keyframes`
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
`;

const myComponent = styled.div`
  display: inline-block;
  width: 200;
  position: relative;
  animation: ${props => css`
    ${heightAnimation} 1.5s ease infinite,
    ${rotateAnimation} 1.5s linear infinite
  `}
`;

Radium is another library that supports multiple animations by passing an array of keyframes objects as the value of the animationName property:

const heightAnimation = Radium.keyframes(
  {
    0% { height: 0; }
    100% { height: 200; }
  },
  'myHeightAnimation',
);

const rotateAnimation = Radium.keyframes(
  {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  },
  'myRotateAnimation',
);

const styles = {
  myStyle: {
    animationName: [heightAnimation, rotateAnimation],
    animationDuration: '1.5s, 1s',
    animationIterationCount: 'infinite, infinite',
    animationTimingFunction: 'ease, linear',
    display: inline-block;
    width: 200;
    position: relative;
  },
};

4. Declare global styles

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'
    }
  }
}

And in Aphrodite, you can use a third-party extension to do this:

import {injectGlobalStyles} from "aphrodite-globals";

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

Or aphrodite-jss to use the global JSS plugin.

5. Test component with styles in unit tests

Some libraries contain utilities for testing components with styles.

Aphrodite provides 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 which 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);
  // buffer will contain something like [ ".background_k554e1{background-color:blue !important;}" ]
  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 it 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.

On this page, you can find a 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.

Here’s a great presentation about this library and CSS-in-JS in general. Totally recommended.


Plug: LogRocket, a DVR for web apps

 
LogRocket Dashboard Free Trial Banner
 
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
Try it for free.


The post 5 things you can do in CSS-in-JS that you didn't know about appeared first on LogRocket Blog.

Top comments (0)