Shared Variables Between JavaScript and CSS

Because it assures consistency throughout the project and avoids magic numbers, sharing variables between JavaScript and CSS code may help you to keep your project codebase tidy and easier to reason about.

For this article, when talking about CSS, the word “variable” may mean custom property or custom media query.

Getting Started

First, create a object containing your variables and export it.

My JS file containing the object will be called variables.js.

export default {
  mainColor: '#000',
  secondaryColor: '#fff000',
  fullHeight: '100vh'
}

No magic is needed to use those variables in your JS files – just import them when you need it. But in order to communicate those variables to your CSS files and use them with var(), some magic is needed.

For this, I will be using cssnext, a PostCSS plugin, and injecting that object into our stylesheets as custom properties.

Webpack example:

import cssNext from 'postcss-cssnext'
import myVars from './variables'

module.exports = {
  // Entry, loaders, plugins...

  postcss: () => [
    cssNext({
      features: {
        customProperties: { variables: myVars }
      }
    })
  ],
}

Since I’m just using postcss-cssnext API, it’s also possible to do it with the build tool of your choice. Check docs on passing options to cssnext features.

Getting Real-world with Breakpoints

Sharing breakpoints between JavaScript and your stylesheets is a great real-world example.

It’s time for variables.js to grow then!

export const properties = {
  mainColor: '#000',
  secondaryColor: '#fff000',
  fullHeight: '100vh'
}

export const mediaQueries = {
  secondaryColor: '#fff000',
  desktop: '(min-width: 1024px)',
  tablet: '(min-width: 768px)',
  mobile: '(min-width: 320px)'
}

Because it’s not possible to create a media query containing a custom property, we need to inject mediaQueries as custom media queries.

Let’s update the previous Webpack example in order to let cssnext to know about both custom media queries and properties.

import cssNext from 'postcss-cssnext'
import {
  properties,
  mediaQueries
} from './variables'

module.exports = {
  // Entry, loaders, plugins...

  postcss: () => [
    cssNext({
      features: {
        customProperties: { variables: properties },
        customMedia: { extensions: mediaQueries }
      }
    })
  ],
}

Done! Some usage examples for CSS, JS and even React are provided below.

Usage

CSS Example

Using custom properties and custom media queries according to settings from variables.js file.

.banner {
  background: red;
  height: var(--fullHeight); /* Custom property */

  @media (--desktop) { /* Custom media query */
    width: 50%;
  }
}

JavaScript example

Example below uses enquire.js library.

import enquire from 'enquire.js'
import { customMedia } from './variables'

enquire
  .register(customMedia.tablet, () => {
    console.log('Matched tablet resolution')
  })
  .register(customMedia.mobile, () => {
    console.log('Matched mobile resolution')
  })

React example

Rendering a component only on desktop resolutions.

Example below uses react-responsive library.

import MediaQuery from 'react-responsive'
import { customMedia } from './variables'

function DesktopOnlyComponent() {
  /* (min-width: 1024px), according to variables.js */

  return (
    <MediaQuery query={ customMedia.desktop }>
      <p>My desktop-only component!</p>
    </MediaQuery>
  )
}

Final Words

A non-consistent front-end codebase is just messy.

I really use this technique on everyday work, and it’s a cheap way to significantly improve the quality of the front-end codebase.

So, don’t underestimate it! Besides breakpoints, there are infinite possibilities for this, such as project colors, layout dimensions and other shared stuff that can easily get out of hand while working on a large project.