How we work with CSS at CARTO

Using Jekyll, and Grunt to build CARTO static sites

Recently I've switched teams within CARTO and now I'm working full-time in the Builder team, since the CARTO Builder was launched earlier this year. For some years now I've been in charge of the commercial, blog, and docs sites working with a team of talented designers.

While these sites have been redesigned several times during this period until the look they have now after the new brand was introduced, the development and build process has mostly remained, refined in each iteration.

Some back story

In the beginning, the commercial site was part of the user management service which helps runs the CARTO business (aka the backoffice), along with the documentation. The blog lived its own millenial life in Tumblr under a subdomain. The stylesheets for all these sites were scattered between a submodule for this purpose, and the backoffice repository.

This worked for a ~10 people company, but as CARTO grew over 100 people, with dedicated marketing and design teams, it wasn't the case anymore.

One of the first measures taken was splitting the commercial, blog, and docs sites into its own projects. We build them with Jekyll, and then they are deployed to an Amazon S3 bucket via our CI setup, to both staging, and production environments. We even run tests to check the authors and categories are right.

I have a pretty similar setup for this site, which I usually use as a playground for new ideas, and I have been deeply inspired by Yeoman, using Grunt as task runner, and Bower as package manager. 1 Despite that Jekyll manages stylesheets pretty well, with support for SASS, in my opinion there's still need of a better asset management (or space to improve).

How we work with CSS

This just solved the build step, but we still needed some rules, a framework which helped us build the styles and components in the styleguide created by the designers.

There's a recurring discussion that keeps appearing in every team I've been:

How do we work with our CSS?

I have not been able to answer this yet, nor I don't have a silver bullet, but there are some possible solutions. Of course, there's some degree of compromise in every of them.

Styleguide

Most of the complaints CSS (unfairly) receives are related to how easy is to end up with a mess of files, classes, and !important, however there are some guidelines I like to follow to avoid these situations.

  • Architecture
    Use a SMACSS approach for file structure, categorizing CSS rules in Variables, Mixins, Base, Components, Layout, and Utilities.

  • Preprocessors
    Use SCSS as preprocessor. I feel OK using CSS, but the support for mixins, and imports completely increases the productivity working with stylesheets. I'm not totally sold on SASS syntax, though.

  • Naming
    Use SuitCSS naming conventions, or BEM, creating reusable, composable components.

  • Specificity
    Avoid unnecessary nesting. The use of @extend can cause unintended consequences, bloating your files, and adding specificity to your selectors.

  • Browsers
    Use Reset CSS, along with our own default styles. For vendor prefixes use PostCSS.

. main.scss
├── _base.scss
├── _components.scss
├── _layout.scss
├── _mixins.scss
├── _utilities.scss
├── _variables.scss
├── base
│   ├── _defaults.scss
│   └── _reset.scss
├── components
│   ├── _aside.scss
│   ├── _buttons.scss
│   ├── _dialogs.scss
│   ├── _dropdowns.scss
│   ├── _footer.scss
│   ├── _grid.scss
│   ├── _header.scss
│   ├── _icons.scss
│   ├── _navbar.scss
│   ├── _offcanvas.scss
│   ├── _section.scss
│   ├── _separator.scss
│   ├── _subheader.scss
│   └── _titles.scss
├── layout
│   └── _home.scss
├── main.scss
├── mixins
│   └── _clearfix.scss
└── variables
    ├── _colors.scss
    ├── _sizes.scss
    └── _typography.scss

The main.scss file is the entry point, and is the only file which doesn't start with underscore:

/* ==========================================================================
   #MAIN
   ========================================================================== */

/* Non-rendering scss
   ========================================================================== */

@import "variables";
@import "mixins";

/* Base
   ========================================================================== */

@import "base";

/* Components
   ========================================================================== */

@import "components";

/* Layout
   ========================================================================== */

@import "layout";

/* Utilities
   ========================================================================== */

@import "utilities";

By requireing the non-rendering SCSS at the top of the file we make available the variables and mixins to the rest of the files.

Similar files (such components) can be grouped in folders. I prefer to require every file in these folders explicitly.

/* ==========================================================================
   #COMPONENTS
   ========================================================================== */

@import "components/aside";
@import "components/buttons";
@import "components/dialogs";
@import "components/dropdowns";
@import "components/footer";
@import "components/grid";
@import "components/header";
@import "components/icons";
@import "components/navbar";
@import "components/offcanvas";
@import "components/section";
@import "components/separator";
@import "components/subheader";
@import "components/titles";

Minification is applied to the entry files, and sourcemaps are created. Finally optimization tasks are performed like file revision, and gzipping.

There's already some basic linting in the build tasks, and next steps would include extending the check of selectors usage, and implementing Parker for stats.

Development

So, the process of updating the stylesheets would go as follows:

You check if the component you want to use already exists in the current stylesheets, if you need to extend it you create a modifier, if you find two components are very similar you consolidate them into one and create the corresponding children and modifiers, or you just create a new one.

Of course, you'd need to update the components library in every project it is being used.

Distribution

When it comes the time to include the stylesheets in your code, these are the most common scenarios:

  • Having the stylesheets checked in as part of every project and replicate them where needed. This obviously doesn't scale, and it is very easy to end up having several differenciated user interfaces between projects. Also, you'll find many times having to manually overwrite the styles because they have not been completely updated.
  • Create a repository and include them as a Git submodule. While this has brought us many headaches in the past, I think it is a good idea if you don't want to fiddle with package managers
  • Include the CSS via Sprockets. Of course this only applies in some cases like Rails, but I've had great experiences before. When working at Crisalix, we already required our common stylesheets for use with the assets pipeline. GitHub does this with Primer, too, their internal CSS toolkit and guidelines. Edit: Just realized they distribute them via npm now.
  • Package them with Bower/npm. My go-to option, the good thing is you don't need to publish them, but can install directly from the GitHub URL. They can be bundled in a dist file, and used as a library (as you'll do with Bootstrap), or included separately from the node_modules or bower_components folders, after installing the corresponding package. This is my preferred option to try to keep file size low.

Finally, you can always include them as an external resource after packaging, and deploying it to a CDN. But I would only see this happening for larger projects where there's not direct involvement with the development process.

I'm happy to see UI libraries I built years ago are still being used extensively in production. Some of the examples I currenlty follow:

Do you know any more examples or want to share your experience?

This post has been written using some of my own internal talks resources, and inspired in:

  1. Lately they moved to gulp, but I keep an example generator-webapp using Grunt, which I use as boilerplate for every project. I bet we'll see the same with npm in favour of Bower.

Tweet this

Published in Development. If you want to receive new articles when they come out, enter your email address here or subscribe to the RSS feed.