CSS in JavaScript: The future of component-based styling

Jonathan Z. White
Apr 25, 2017 · 7 min read

By adopting inline styles, we can get all of the programmatic affordances of JavaScript. This gives us the benefits of something like a CSS pre-processor (variables, mixins, and functions). It also solves a lot of the problems that CSS has, such as global namespacing and styling conflicts.

For a deep dive into the problems that CSS in JavaScript solves, check out the famous presentation: . For a case study on the performance improvements you get from Aphrodite, you can read . If you want to learn more about CSS in JavaScript best practices, check out .

In addition we’ll be using inline JavaScript styles to build components to address some of the fundamentals of design I covered in one of my previous articles: .

A motivating example

Let’s start off with a simple example: creating and styling a button.

Normally the component and its associated styles would go in the same file: Button and ButtonStyles. This is because they fall under the same concern: the view. However, for this example, I broke up the code into multiple gists to make it more digestible.

Here’s the button component:

This is nothing unexpected — just a stateless React component. Where Aphrodite comes into play is in the className property. The function css takes in a styles object and converts it into css. The styles object is created with Aphrodite’s StyleSheet.create({ ... }) function. You can see the output of StyleSheet.create({ ... }) with this .

Here is the button stylesheet:

One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low. Properties like border-radius become borderRadius and values become strings. Pseudo-selectors, media queries, and font definitions all work. In addition, vendor prefixes are added automatically.

Here is the result:

With this example in mind, let’s take a look at how we can use Aphrodite to build a basic visual design system, focusing on the following design fundamentals: typography and spacing.


Fundamental №1 —Typography

Let’s start off with typography, a fundamental basis for design. The first step is to define typography constants. And unlike Sass or Less, constants for Aphrodite can go in a JavaScript or JSON file.

Define typography constants

When creating constants, use semantic names for your variables. For example, instead of naming one of your font-sizes h2, use a name like displayLarge that describes its role. Similarly, for font-weights, instead of naming one of your weights 600, give it a name like semibold to describe its effect.

It’s important to get the values right for variables like font-sizes and line-heights. This is because they directly affect the vertical rhythm within a design. Vertical rhythm is a concept that helps you achieve consistent spacing between elements.

For more on vertical rhythm, you can read this article:

There is a science behind choosing the values for your line-heights and font-sizes. We can use mathematic ratios to generate a set of potential sizes candidates. A few weeks ago, I wrote an article detailing the methodology: . For determining font-sizes, you use . For determining line-heights, you can use this .

Define a heading component

After defining our typography constants, the next step is to create a component to consume the values. The goal of the component is to enforce consistency in design and implementation for headings across the codebase.

The Heading component is a stateless function that takes in a tag as a property and returns the tag with its associated style. This is possible because we defined the tag mappings earlier in the constants file.

At the bottom of the component file, we define our styles object. This is where we use the typography constants.

And this is how the Heading component would be used:

With this approach, we reduce unexpected variability in our type system. We avoid the pitfall of a hundred different font sizes by removing the need for global styles and standardizing headings across the codebase. In addition, this approach we took to building the Heading component can be applied to building a Text component for body copy.

Fundamental №2 — Spacing

Spacing controls both vertical and horizontal rhythm in design. That makes spacing pivotal to establishing a visual design system. Just like in the typography section, the first step to address spacing is to define spacing constants.

Define spacing constants

When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a spacingFactor constant, we can generate a set of distances based on a common factor. This approach ensures that we have logical and consistent spacing between elements.

The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example, here are the first six computed distances using the golden ratio with a spacingFactor of eight.

Golden Ratio (1:1.618)8.0 x (1.618 ^ 0) = 8.000
8.0 x (1.618 ^ 1) = 12.94
8.0 x (1.618 ^ 2) = 20.94
8.0 x (1.618 ^ 3) = 33.89
8.0 x (1.618 ^ 4) = 54.82
8.0 x (1.618 ^ 5) = 88.71

This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value.

After defining our spacing constants, we can use them to add margins to elements in our design. One approach we can take is to import the spacing constants and consume them in components.

For example, let’s add a marginBottom to the Button component.

This works in most scenarios. However, what happens if we want to change the marginBottom property of the button based on where the button is place?

One way to achieve variable margins is to override the margin style from the consuming parent component. An alternative approach is to create a Spacing component to control the vertical margins on elements.

Using this approach, we can remove the responsibility of setting margins out of the child component and into the parent component. This way, the child component becomes layout agnostic, not requiring any knowledge of where to place itself in relation to other elements.

This works because components like buttons, inputs, and cards may need variable margins. For example, a button in a form may need larger margins than a button in a navigation bar. The caveat is that if a component always has consistent margins, then it would make more sense to handle margins within the component.

Also you may have noticed that the examples only use marginBottom. This is because defining all your vertical margins in one direction allows you avoid collapsing margins and keep track of the vertical rhythm of your design. You can read more on this in Harry Robert’s article, .

On a final note, you can also use the spacing constants you defined as padding.

By using using the same spacing constants for both margins and padding, you can achieve more visual consistency in your design.

Here’s what the result might look like:


Now that you have a grasp of CSS in JavaScript, go out and experiment. Try incorporating inline JavaScript styles into your next project. I think you’ll appreciate being able to work in a single context to handle all of your styling and view concerns.

On the topic of CSS and JavaScript, what are some new developments that you’re excited about? Personally I’m excited about async/await. Leave me a note or send me a on Twitter.

You can find me on Medium where I publish every week. Or you can follow me on , where I post non-sensical ramblings about design, front-end development, and virtual reality.

P.S. If you enjoyed this article, it would mean a lot if you clicked the 💚 and shared with friends.

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Jonathan Z. White

Written by

Designer & developer | Writes about all things to do with building products | twitter.com/jonathanzwhite

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead