Image for post
Image for post
https://unsplash.com/photos/3shfnfzdFVc

Writing A CSS-in-JS Library From Scratch

Kye Hohenberger
Sep 29, 2017 · 4 min read

CSS-in-JS libraries have been getting a lot of attention lately. You’ve probably used or heard of Styled Components, Glamor, or (shameless plug) hopefully Emotion. But have you ever wondered how to build a CSS-in-JS library? As I’m preparing for the release of emotion version 8, I’d like to show you how you can create your own from scratch!

The Basics

<style>
.circle-avatar {
border: 1px solid darkorchid;
}
</style>

The browser parses the text content and inserts the rules into the CSSOM. Once complete, any elements that match the inserted rules have their associated styles applied to them.

We are going to do the same thing, but with Javascript.

First, we create a <style> element and append it to the head.

const sheet = document.head.appendChild(
document.createElement('style')
).sheet

The style element has a property, sheet, that is an instance of CSSStyleSheet and contains the apis needed for writing CSS with Javascript. The two important apis we need are sheet.cssRules and sheet.insertRule.

cssRules

insertRule

rule

A selector and declaration

.pretty-text { 
color: aquamarine;
}

Or an at-identifier and rule content

@media (max-width: 420px) { 
.my-class { color: aquamarine; }
}

index

First Implementation

function css(selector, styleString) {
const rule = `${selector} { ${styleString} }`;
const index = sheet.cssRules.length; // insert at the end
sheet.insertRule(rule, index);
}

Lets try it out

Second Attempt

All we need is something unique about each rule to do this. Lucky for us, because we insert our rules at the end of our sheet, we have a constant unique value, `cssRules.length`

function css(styleString) {
const index = sheet.cssRules.length;
const id = index.toString(36);
const className = `css-${id}`;
const rule = `.${className} { ${styleString} }`;

sheet.insertRule(rule, index);
return className;
}

This is great! We have a function that takes styles in and returns a class name that we can use anywhere in the DOM.

Fun With Tagged Templates

const errorCls = css`
color: palevioletred;
font-size: 2.5rem;
margin: 0;
`

Doesn’t that look better?

Ok, if this is what we want we should go read Max Stoiber explain how template literals work in styled-components or Wes Bos’s great article on the subject.

First, we need to update the arguments of css to those of a tagged template literal.

function css(strings, ...interpolations) {
const stringStyles = ???

// rest of css is unchanged
}

Next, we need a string from these arguments so we join, or interleave, the strings and interpolations arrays into a single string. We’ll loop over each string and append an interpolation if it exists at that index.

function interleave (strings, interpolations) {
return strings.reduce(
(final, str, i) =>
final +
str +
(interpolations[i] === undefined
? ""
: interpolations[i]),
""
);
}

Lets put it all together.

Pretty neat huh?

Closing Thoughts

More Reading:

DOM Enlightenment Chapter on CSS

emotion’s css function

glamor’s css function

cxs’s styled function

Thanks to Kyle Shevlin and Peter Piekarczyk for the edits.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store