How UI Became a Thing at Docker

au-sbd80_400x400

 

written by Chris Biscardi, UI Engineer, Docker

 

There are more people at Docker that write Go than write JavaScript. In early August 2014, I joined Docker as the only person whose primary job description was to ship UI. My long term task was to figure out how to enable the UI Engineers and Designers we were assuredly going to hire. Remember, the task of Docker is to “build tools of mass innovation” not “write maintainable css through the proper application of BEM“.

The state of the world when I was hired resembled something pretty typical in the early days of a project. We mostly had Django rendered pseudo-static html pages which were then enhanced in various ways with client-side javascript. Maintainability was generally poor and a rewrite was called for for various reasons. Docker has seen tremendous growth over the past year. Along with that growth comes more projects and a greater need to build quickly to test ideas.


 

One Year Later

The story of the last year is for another blog post, this blog post is focusing on where we ended up after a year from a technical standpoint. The following is the living standard at this moment for shipping UI at Docker.

JavaScript

We standardized our JavaScript in the following ways.

 

babel: stage 0

Babel is a huge piece in our pipeline, as it is in many pipelines at this point. All of our code is written in stage 0 style Babel and we are ecstatic that babel 6 is moving to a very PostCSS-like method of using plugins.

The features babel provides which have helped us the most are:

 

“Also, autobinding”

(class properties, fat arrows and class syntax)

Autobinding gets a small mention in the React 0.13.0-beta-1 blog post but receives a fuller treatment in the transposing property expressions into the constructor section of the ES7 Class Properties gist linked. Finally, checkout the actual implementation in the babel repl to get a really solid view of what’s happening in the compiled code.

Basically, Fat Arrows keep the same lexical this as their surrounding code and property initializers get “raised” into the constructor when desugared. This means the lexical scope is the same scope as the body of the constructor.

 

javascript
class Thing extends Component {
  myFunc = () => {
    // automatically bound this
    debug('a prop!', this.props.whaaaat);
  }
}

 

Computed Keys

Using an excerpt from our internal library (this is a FontAwesome component), we often use computed keys to simplify logic in reusable elements. This, combined with propTypes, can make it so that illegal states are unrepresentable, reducing errors and speeding development.

 

javascript
const classes = classnames({
  'fa': true,
  'fa-fw': fixedWidth,
  'fa-inverse': invert,
  [icon]: true,
  [`fa-${animate}`]: _.includes(animations, animate),
  [`fa-${size}`]: _.includes(sizes, size) && !stack,
  [`fa-flip-${flip}`]: _.includes(flips, flip),
  [`fa-rotate-${rotate}`]: _.includes(rotations, rotate),
  [`fa-stack-${size}`]: _.includes(sizes, size) && stack
});

 

Destructuring

We now spec out almost all of our functions using destructuring. This has made it infinitely more clear as to what arguments a function accepts. This benefit could also be achieved through meticulous documentation, but we have found that destructuring is a far better solution for our purposes (and it hasn’t replaced documentation).

 

javascript
export function doStuff({ name, namespace }) {
  if(namespace) {
    // someone owns it!
  }
}

 

React

React’s benefits are less about the DOM and more about a coherent story for building abstractions. For example, we can revisit the FontAwesome element from earlier:

 

javascript
import React, { Component, PropTypes } from 'react';
const { oneOf, string, bool } = PropTypes;
import classnames from 'classnames';
import _ from 'lodash';

const sizes = ['lg', '2x', '3x', '4x', '5x'];
const animations = ['spin', 'pulse'];
const flips = ['horizontal', 'vertical'];
const rotations = [90, 180, 270];

export default class FontAwesome extends Component {
  static propTypes = {
    icon: string.isRequired,

    animate: oneOf(animations),
    fixedWidth: bool,
    flip: oneOf(flips),
    invert: bool,
    rotate: oneOf(rotations),
    size: oneOf(sizes),
    stack: bool
  }

  render() {

    const {
      animate,
      fixedWidth,
      flip,
      icon,
      invert,
      rotate,
      size,
      stack
    } = this.props;

    const classes = classnames({
      'fa': true,
      'fa-fw': fixedWidth,
      'fa-inverse': invert,
      [icon]: true,
      [`fa-${animate}`]: _.includes(animations, animate),
      [`fa-${size}`]: _.includes(sizes, size) && !stack,
      [`fa-flip-${flip}`]: _.includes(flips, flip),
      [`fa-rotate-${rotate}`]: _.includes(rotations, rotate),
      [`fa-stack-${size}`]: _.includes(sizes, size) && stack
    });

    return ();
  }
}

 

This element will warn the developer when it’s used inaccurately (barring an error where you can actually input anything you want in the icon field).

 

javascript
import FA from 'common/FontAwesome';

 

React Router

I’m not going to say a whole lot here. We’ve standardized on React Router for routing across client, desktop, universal and (hopefully soon) native environments. It has been a joy to work with and significantly reduced the complicatedness of routing in our applications. 1.0 also looks pretty sweet with the addition of history being broken out.

 

Redux

Redux is probably the newest addition to our standard and possibly one of the most exciting. Redux is an isolated “flux” implementation written in a very functional style. Some of the big wins when using redux include the assortment of devtools and middleware which are popping up more and more.

Redux attempts to make state mutations predictable

Taken from the Motivation for redux.


 

CSS

Christopher Chedeau’s “CSS in JS” talk from NationJS in November, 2014 was something we took a huge amount of inspiration from at Docker. The most impactful slide is this one, which lists the problems outstanding with CSS.

 

7-css-problems

 

We chose css-modules, PostCSS and integrated them with webpack.

 

css-modules

css-modules uses the ICSS standard compile target to make css local by default, which allows us to rethink best practices and write excessively generic class names. Given a css file:

 

css
/* Heading.css */
.heading {
  color: pink;
  font-weight: 800;
}

 

We can use that class, for example in a React Element.

 

javascript
import React, { Component } from 'react';
import styles from './Heading.css';

class Classy extends Component {
  render() {
    return<h1 className={styles.heading}>Heads.</h1>
  }
}

 

When rendered, our h1 looks like:

 

<h1 class='Heading__heading__basz'>Heads.</h1>

 

and the compiled CSS (extracted into a single stylesheet) looks like:

 

css
.Heading__heading_basz {
  color: pink;
  font-weight: 800;
}

 

PostCSS

I can literally not say enough good things about PostCSS. If you like and are happy with SASS, there may be no reason for you to switch, but I heavily encourage you to try it out.

We are using cssnext but have been putting thought into how we might modularize the requirements specified by dux elements. Specifically concerning the fact that ordering of plugins matters.

See the features list for more on cssnext. We also use (or are experimenting with) a number of other plugins such as modular-scale, local-constants and various linting tools. The small core and plugins model is powerful.

 

css
:root {
  --mainColor: #ffbbaaff;
}
@custom-media    --mobile (width <= 640px);
@custom-selector --heading h1, h2, h3, h4, h5, h6;

.post-article :--heading {
  color: color( var(--mainColor) blackness(+20%) );
}
@media (--mobile) {
  .post-article :--heading {
    margin-top: 0;
  }
}

 

dux

Over the next few months you will see us rolling out an internal framework we are calling dux. dux is really a collection of libraries arranged in a standardized way. It includes:

• A slim css base built on normalize and some of the same ideas as bootstrap’s reboot.

• A base set of abstractions with familiar names like Card and Button but also, Markdown, FontAwesome and LiLink. These abstractions are built on React and include CSS. Sample usage might look like:

javascript
  import Card, { Title, Content, Footer } from 'dux-card';
  import Moment from 'dux-moment';
  class ProfileDescription extends Component {
    render() {
      const { username, avatar, bio }  = this.props;
      return (
         <Card img={avatar}>
            <Title>{username}</Title>
            <Content>{bio}</Content>
            <Footer>Joined <Moment utc fromNow>{date_joined}</Moment></Footer>
         </Card>
    )
  }
}

• Project templates (iOS, client-app, universal-app, static-site, etc), which kick-start projects by pre-configuring build toolchains based on a standard template (webpack, PostCSS, css-modules, lost-grid, etc)

• Individually versioned elements with a small core. We are taking the lead from babel, PostCSS and Docker itself with regards to splitting out elements/plugins from the core engine. One issue we are anticipating making easier with this move is “CSS as API” and minimizing major version bumps for such breakages in individual elements.

A main goal of the dux project is to eliminate duplicate work. To that end, we have decided to remove, as much as possible, the requirement to write CSS on the consuming side. Most of our CSS is written author-side instead of project-side. Backdoors are still provided in some cases: In fact, we have a generic API which allows the passing in of style-objects.

side-note: Style-Objects are simply objects whose keys are a “generic” classname (such as heading) and whose values are the actual classname (such as Card__header__bgzj4 if it’s compiled). This allows us to re-use our elements even in environments without css-modules (by passing in an object with normal, global classnames as values).


 

Further Reading

css-modules

• icss

• PostCSS

• PostCSS Loader

• cssnext

• rucksack

• babel

 

We hang around in reactiflux and you should too 😉

 


 

 Learn More about Docker

 

,

How UI Became a Thing at Docker


2 Responses to “How UI Became a Thing at Docker”

  1. Thomas

    Nice writeup of the problems of current CSS approaches.
    But please have a look at the soon-to-be-released VCL:

    http://vcl.github.io/
    https://github.com/vcl/doc
    http://vcl.github.io/presentation/index.html#1

    Reply
  2. 03-09-2015 - Links - Magnus Udbjørg

    […] HOW UI BECAME A THING AT DOCKER […]

    Reply

Leave a Reply to Thomas

Click here to cancel reply.

Get the Latest Docker News by Email

Docker Weekly is a newsletter with the latest content on Docker and the agenda for the upcoming weeks.