Skip to content

Instantly share code, notes, and snippets.

@iest
Created November 29, 2016 18:26
Show Gist options
  • Save iest/b2634c327c40cd81617ed2952d11dcb8 to your computer and use it in GitHub Desktop.
Save iest/b2634c327c40cd81617ed2952d11dcb8 to your computer and use it in GitHub Desktop.
How we went from css modules & sass to glamor

We started out with sass files with something like this (obvious brevity is obvious):

// colors.scss
$green: #37ab2e;
$white: #FFFFFF;

// skin.scss
.bg_green { color: $green; }
.fg_white { color: $white; }

Then using webpack, we processed the sass and used css-loader in modules mode to use them in react components like this:

// btn.js
import c from 'classnames'
import s from 'cityons.m.scss'; // our tachyons-style lib

const Btn = () => (
  <button className={c(s.bg_green, s.fg_white)}>
	  Green button with white text
  </button>
);

We're experimenting with SSR, and the architecture we want to use processes files through babel for server-side usage, but crucially the server-side does not use a webpack bundle (although the client does).

So seeing as we're not using webpack loaders for server-side code (and the fact we've been wanting to kill sass for a long time), we decided to take the plunge into glamor.

We chose glamor because:

  1. It works out of the box in the browser and server (and has all the autoprefixer bells & whistles) without needing webpack
  2. It has a comprehensive & clear API
  3. It meant we could rewrite our CSS lib under the hood without actually having to change any usage of that lib (important)

In glamor's readme, the usage is like:

const rule = style({ color: 'green' })
// rule is an object
const Foo = () => <div {...rule}>Green!</div>

However, we rely heavily on classnames for composing styles inside the components, and the following doesn't work:

const green = style({ color: 'green' })
const bgwhite = style({ background: 'white' })
const Nope = () = <div className={classNames(green, bgwhite)}/>

This doesn't work because classNames accepts objects as arguments, so the output is useless to us.

Thankfully however, glamor has a neat trick — you can toString() the rules you create, and it'll output a string class name that you can just use in place of what you were previously using css modules for.

So the example at the very top becomes the following:

// cityons/util.js
import R from 'ramda'
export const stringify = R.map(rule => rule.toString())
// R.map means we can map over an object, transforming the values and leaving the keys as-is

// colors.js
export default {
	green: '#37ab2e',
	white: '#FFFFFF',
}

// skin.js
import { style } from 'glamor'
import {stringify} from 'cityons/util'
import { green, white } from 'cityons/colors'
const rules = {
	bg_green: style({ background: green }),
	fg_white: style({ color: white }),
}
export default stringify(rules)

So the default export is a map of rule key to classname string. Which means the following code will work exactly like it did with sass & css-modules:

// btn.js
import c from 'classnames'
import s from 'cityons'

const Btn = () => (
  <button className={c(s.bg_green, s.fg_white)}>
	  Green button with white text
  </button>
);

Which meant I could swap out the whole underlying lib without any API change to users of the lib.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment