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
orbower_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: