Skip to content

Instantly share code, notes, and snippets.

@iest
Last active August 29, 2015 14:27
Show Gist options
  • Save iest/a1ccf10d8a8de113650f to your computer and use it in GitHub Desktop.
Save iest/a1ccf10d8a8de113650f to your computer and use it in GitHub Desktop.
A draft of a post about topnotes' CSS

TODO:

  • Tools used
    • Webpack
    • CSS modules
    • postcss + plugins used
  • Why it's magic
    • No namespace issues
    • Composability
    • Component-specific styles
      • There's only one place to change the CSS for a particular classname
      • To override you have to combine CSS classes instead of overwriting
    • Explicit globals
    • Output CSS only includes CSS files that are explicitely imported
  • Coding style
    • Indentation
    • Proper (standard) variable syntax
    • Media queries and how they're dealt with

CSS on beta.pactcoffee.com

Our new (still in progress) beta site, is built with a whole load of awesome technologies and techniques. One of the best things about our toolchain is how we write CSS, and how it's used as part of the project.

In this post I'll go into a bit of detail about how we write CSS for the site, including coding style, tools used, and why it's a little bit magic.

This is the first in a series of posts that detail the various parts of our new app.

Components all the way down

The only page we currently have live on our beta site is the careers page. This page is made up of a bunch of react components.

For example, the hero at the top of the page (the bit with a background image & "Join the Pact Team") is a single component that looks like this:

  import React from 'react';

  import Wrapper from 'loggins/components/Wrapper/Wrapper';
  import * as m from 'loggins/globals/modifiers.css';
  import Btn from 'loggins/components/Btn/Btn';

  import styles from './Hero.css';

  export default class Hero {
    render() {
      return (
        <div className={styles.hero}>
          <Wrapper className={styles.inner}>

            <h1 className={styles.heading}>Join the Pact team</h1>
            <h2 className={styles.tagline}>Help us deliver happiness to coffee lovers across the UK.</h2>

            <div className={[m.alignc, m.mtl].join(' ')}>
              <Btn href="#jobs" className={styles.cta} type="primary">Open roles at Pact</Btn>
            </div>

          </Wrapper>
        </div>
      );
    }
  }

If you've not seen or read JSX or ES2015 before, don't worry. The important bits are:

  import styles from './Hero.css';
  // Equivalent to `var styles = require('./Hero.css');` in ES5

and lets take that first <div> for example:

  <div className={styles.hero}>
  ...
  </div>

Importing CSS directly into JS, then using it like an object doesn't really make sense. So what's going on here?

Webpack, yo!

We're using webpack to bundle our app together. Essentially, what it does is interpret all the import/require calls you do in your javascript, optionally running those files through what's called a loader.

For example — the reason we can write ES2015 code is becuase all our javascript is run through a babel loader, transforming our ES2015 syntax into ES5 code.

A similar thing happens with our CSS. We use webpack's css-loader in module mode, which means that when I write CSS inside a file called Btn.css like this...

  .root {
    border: 1px solid black;
  }
  .primary {
    background: green;
  }
  .secondary {
    background: red;
  }

...then import styles from './Btn.css' inside my javscript file, the styles object I get looks like this:

  {
    root: '...',
    primary: '...',
    secondary: '...'
  }

The classnames inside the CSS file are known as local classnames. In the styles object, the string values of those keys are known as global classnames, and are what end up in the output CSS file. These are auto-generated according to how we configure webpack.

For example, when in development we set up css-loader with the following option: localIdentName=[name]-[local]. This means the class names we get in the output CSS file will be .nameOfCSSFile-nameOfLocalClassName. In the case of the Btn example above, we'd have Btn-root, Btn-primary, and Btn-secondary. In this way, we get a BEM-esque naming system.

In production we could set the name like this: localIdentName=[hash:base64:5], which means to use the first 5 characters of a base64 hash of the file. So while we might have a local classname of .root, the actual output would use _2h2JM, for example.

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