I come from a design background, and worked with CSS/Sass for a couple of years before I really got into JavaScript. I was able to solve the famous FizzBuzz challenge using Sass before I was ever able to solve it with JavaScript. I recently made the decision to drop using Sass for my styling, and instead look to JavaScript, so naturally the whole concept of JavaScript styles fascinates me.

React is super popular these days, and there’s plenty of contenders for handling your styles when using React and JavaScript, but none of them quite did what I wanted the way I wanted. With a rough idea of the API I wanted to use, I set out to build my own tool. Several months later I ended up with a finished tool called Polymorph, which I ensured would work nicely with my React components (but I also wanted it to be portable enough to work outside of React projects, too).

To see how it works, let’s create an accordion using React + hooks, and then style it with Polymorph. For now, we will assume that the accordion was previously styled using CSS, using the BEM naming convention (this is important). This leaves us with something like:

This will render HTML to the DOM where presumably some CSS file also exists. Given existing available tools, if you were tasked with replacing the CSS styles with JavaScript styles, you would likely need to refactor the above quite significantly, assigning certain rules to appropriate divs manually. Polymorph was built to work with BEM elements, so as long as the markup resembles BEM, Polymorph can detect which elements to style from just the BEM Element name. This means the styles API for the accordion can exist as a plain JavaScript object, something like:

As Polymorph expects to be working on a BEM interface, it can intelligently determine if a key corresponds to any existing child Element name; if so, it will find those elements and apply the appropriate styles.

You can do some pretty cool stuff with Polymorph, especially when combined with tools like sQuery (a JavaScript library for interacting with DOM elements that follow the BEM naming convention). The above could be re-written to:

…which, if you study the differences, you will see is the same thing just with a slightly different way of doing things.

Adding To React Components

Now that we have our styles object, we just somehow need to provide it to our React component. Polymorph works by passing a DOM element (which should be the parent BEM Block) and a styles object:

polymorph(Element, styles);

So thinking in React terms, we would need to call this function in the component’s componentDidMount lifecycle method (or if using hooks, using the Effect hook), passing a reference to the underlying DOM node generated from the React component. The repaint() method of Polymorph would need to be called within the React component’s componentDidUpdate lifecycle method (or if using hooks, using the Effect hook), so that whenever React updates the DOM, Polymorph can then repaint it accordingly.

Using class components:

Using functional components:

Improving The React API

Pretty much everyone agrees that BEM is ugly, and riddling our React code with BEM class names fills me with despair. The React code can be improved to resemble the BEM philosophy more, as well as have better handling of the styles:

…assuming the that styles is in scope and references the styles object, it’s simply passed to a styles prop. This is achieved by using the Lucid library, which is a library of Higher Order Components for rendering BEM markup.

The reason I like this approach for handling styles with JavaScript in a React context is because it doesn’t involve interfering too much with the React “stuff”; we can totally separate our styles object from the React component without them being too tightly-coupled, which, if using something like Styled Components, the exact inverse would be true. The exact same styles object could be used in another project that didn’t even use React, making it extremely portable.

Hopefully you’ve gained something from reading this article, if not, there’s always next time!