Links

How it works

Classy-UI is a babel-plugin. That means when you write your code, what you write is not what hits the browser. Babel goes through your code a based on its plugins things are changed around.

In development

When you write this:
import { compose, tokens } from 'classy-ui'
const button = compose(tokens.color.GRAY_80, tokens.backgroundColor.GRAY_20)
The Classy-UI babel plugin will convert it to:
import 'classy-ui/normalize.css'
import { addClasses } from 'classy-ui/runtime'
addClasses([
'color__GRAY_80', '.color__GRAY_80{color:#2d3748}',
'background-color__GRAY_20', '.background-color__GRAY_20{background-color:#edf2f7}'
])
const button = 'color__GRAY_80 background-color__GRAY_20'
The addClasses function from Classy-UIs runtime adds these classnames and their CSS to a style tag on the page. If the classnames had already been added, the code would just be completely removed.

In production

The same example:
import { compose, tokens } from 'classy-ui'
const button = compose(tokens.color.GRAY_80, tokens.backgroundColor.GRAY_20)
The classy-ui babel plugin will convert it to:
const button = 'A_A B_A'
So there are two things happening here:
  1. 1.
    All the names are shortened down. We can do this because we control all the classnames consumed
  2. 2.
    The CSS for each classnames is inserted into a CSS file which will be written to disk. It will group media queries, theme variables etc. The file is imported typically in your entry file from classy-ui/styles.css
Since your css is now part of the build tool, any css optimization you have added to it will also optimize the classy-ui file.

Handling specificity

Where Classy-UI truly shines in its approach is how specificity is handled. To understand where specificity plays a role we have to talk about Classy-UIs ability to compose classnames together. Imagine you wanted a general button styling, but then you wanted to extend this to a button that has a red background, but keeps all the other styling. An example of that could be:
import { compose, tokens } from 'classy-ui'
export const button = compose(
tokens.color.GRAY_80,
tokens.backgroundColor.GRAY_20
)
export const redButton = c(
button,
tokens.backgroundColor.RED_50
)
With what we have learned this far, this code would look something like:
const button = 'color__GRAY_80 background-color__GRAY_20'
export redButton = button + 'background-color__RED_50'
But if we look closer, the redButton actually looks like this:
export redButton = 'color__GRAY_80 background-color__GRAY_20 background-color__RED_50'
If you know your CSS rules this causes a specificity issue. The order of classnames does not matter, it is the order of the classnames in the CSS definition that matters. The problem is... there is absolutely no way to control this safely. But we do not have to fix it in the CSS itself, we can fix it on the classnames. Lets have a look again:
export const redButton = compose(button, tokens.backgroundColor.RED_50)
We know that the classname you want for this composition is backgroundColor-RED_500, and we also know that backgroundColor is a category of classnames. That means we can simply filter out any other backgrounds.
export redButton = button.replace(/\b([\s|$]background-color__.*)\b/, '') + 'background-color__RED_50'
We just remove any classnames of the same category on what you compose into your redButton. That means you will never have a set of classnames with crashing categories.
This is not 100% how the logic works, it is simplified, but the concept is the same
But we still have a problem. CSS allows you do define for example padding different ways (padding, padding-top, padding-bottom etc.). That means a padding classname could still crash with a paddingTop classname, or paddingVertical classname. To solve this we derive padding, margin, borderColor etc. from their individual representations. Meaning that using padding will actually result in paddingTop, paddingRight, paddingBottom and paddingLeft. This ensures specificity and also we ensure that there is no duplicate representation of the values.
But we actually still have a problem. When using the screens we have to make sure that the media queries are inserted in the order they override each other. Meaning that for example:
compose(
mobile(tokens.backgroundColor.RED_50),
tablet(tokens.backgroundColor.BLUE_50)
)
When tablet is the active media query, also mobile is active. That means the tablet media queries has to be inserted later in the stylesheet.