This doc describes our CSS coding practices, conventions and coding styleguide.
For overview of Architecture, see architecure.md
For approach and philosophy introduction, see index.md.
With a growing and diverse team, we need a way to pass on knowledge about our coding style, conventions and practices and to make sure we are on the same page. Keeping this doc up-to-date will reduce confusion, save time on code reviews and improve code quality.
The code is split into v1/ and v2/ directories. This indicates pattern library versioning.
v1/ is no longer actively developed, however its CSS is still used in some parts of the website that were built prior to 2016.
When developing new code, it should be placed in v2/. Eventually, v1/ will be removed, so new code added to v2/ should be self-contained without relying on v1/.
Our website currently includes Foundation library. With small exceptions, we no longer use Foundation in new templates developed from 2016 and on. This decision was made because Foundation columns were not granular enough to accommodate our design needs. Eventually, when we remove v1/, we will also remove Foundation library.
Examples where we still rely on Foundation in new code, are some utility functions like rem-calc
, viewport size variables and utility classes such as hidden-for-small-only
.
Our file structure is based on 7-1 pattern.
All source code lives in src/
directory. Our prototype generator, Hologram, automatically converts folders to menu headers and files to menu items.
hacks.scss
file is used for permanent one-off hacks that don’t naturally belong to any individual file.
shame.scss
file is used for temporary hacks that we have a refactor plan for.
We follow Semantic Versioning (Semver). Every change to the library needs to include a version bump in repository’s package.json
file. Deploy script automatically creates a git tag and pushes it to github. Among other things, versioning enables us to import multiple versions of assets in our app simultaneously and to control timing of when the changes are deployed to the app.
Styles repo has some images that are used not only in Hologram examples, but also in the app. It is a pattern that we are no longer using, so no new images should be added here.
Names of classes, variables or mixins should be descriptive of their function or semantic meaning, and not of their presentational qualities or specific usage cases in the app. The line is hazy, so they say naming is really hard! But some things can be drawn with clarity.
An example of a bad name:
.mod-yellow-card
Why is it bad? It describes how the card “looks”. So, if designer decided later than she wants to make it pink, you’ll have a name that is confusing and not in sync.
An example of a bad name:
.mod-life-insurance-card
Why is it bad? It describes a specific product usage for that card. If we reuse this card in health product, naming will fall out of sync.
An example of a good name:
.mod-profile-card
Naming: same as html tag/element that it represents. For example, Button element has base class button
. List element has base class list
. Anchor tag has link
base class, in order to avoid a one-character classname.
Naming: top level class name has com-
prefix.
Naming: top level class name has mod-
prefix.
Naming: mixin/placeholder name has layout-
prefix.
Naming: top level class name has tem-
prefix.
We try to follow this order of selectors on the file level:
.parent
%resets
@includes / @extends
.sub-classes..
(follow parent pattern with subclasses)
&:pseudo selectors
&.variants of parent
@medias (small -> large screens)
We love SCSS variables. There are two patterns that we use:
Variables that are reused globally. For example, same color variables are re-used throughout our codebase.
Variables that are used within an individual file.
In every file, these variables are placed just above the top level class and are named with a prefix of that file’s item name. E.g. $breadcrumbs-blocks-inactive-hover-background-color
for Breadcrumbs module. These are un-scoped global variables, but their naming removes the chance of collision with other global variables.
All variables used in an individual file need to be defined at the top of that file. This includes global variables (of pattern 1 above), for example:
$breadcrumbs-blocks-inactive-hover-background-color
: $neutral-1
where $neutral-1
variable, which is reused across the codebase, is captured in $breadcrumbs-blocks-inactive-hover-background-color
for local use.
We work with px, rem and ru units.
There are several aspects when it comes to deciding which units to use:
Designers work with ru()
* and px units. They may provide developers with both formats, depending on the use case.
Compiled CSS should always use rem units.
ru()
helper function already converts to rem. If you are using px in code, make sure px is converted to rem by using rem-calc()
helper function. See Foundation docs for docs on this function.
Standalone rem units should not be used in code (instead, always px with rem-calc()
function).
Ru stands for “rhythm unit” and 1ru == 24px. The concept of using these units is similar to vertical rhythm. We use it not only for typography, but for any type of properties. The main benefit of using ru is that commonly used distances such as margins, paddings and widths are standardized. For that matter, 1 ru unit should only be divisible in quarters. For example, ru(1.25)
is good, but ru(1.44)
is bad. When a more granular number is needed, rem-calc()
should be used.
The argument to ru()
function is an integer number of pixels.
We use Autoprefixer to automatically insert vendor prefixes when we compile Sass.
We use a custom basic CSS reset, defined in src/stylesheets/v2/utilities/_reset.scss
. Additionally, %reset
placeholder is currently used in Modules as a local scope reset, however it is likely to be deprecated when the refactoring is done.
Yes, we support IE11 😞.
No, we don’t need to support earlier versions of IE 🎉.
For more detailed information, see Browser Support doc in Confluence.
When calling a @media query, use one of the existing variables.
Ex. (For the full list: src/stylesheets/v1/base/_foundation-and-overrides.scss
)
@media #{$small-only}
@media #{$medium-up}
@media #{$large-up}
We use scsslint, with configuration for that living in .scss-lint.yml
. Don’t override the linter.
Our architecture does not permit use of html5 element selectors everywhere. For example, we cannot use html5 element selectors in Modules, because Modules may contain other Modules. Same with Components containing Elements. To eliminate these problems, html5 element selectors should not be used anywhere and class-based selectors should be used instead.
Mixins and placeholders, if not self-explanatory, should have comments containing a quick description and a statement of purpose.
General purpose mixins live in utilities/_mixins.scss
. General purpose placeholders live in utilities/_placeholders.scss
Layout mixins and placeholders will live in layouts/
directory.
Grids are implemented manually using flex-box. We don’t rely on external libraries for grid implementation. One small exception is the use of Foundation in older templates (legacy, no longer used for new code).
IE has many bugs with flexbox. Because of this, it may be good to use caution when using properties like flex
shorthand or flex-basis
. For a complete list of IE flex bugs, see the excellent flexbugs repo.
Each CSS file in the pattern library (such as Module or Element) contains slim markup inside of an html comment that is used by Hologram to auto-generate examples. This meta comment information is placed above all code on the top of the page.
It is important that every new addition to the pattern library is represented in Hologram because engineers and designers rely on these visual examples in the development process. This includes any variations of the items.
Having said that, Hologram examples must contain the minimum markup necessary to display the visual features of the item. For example, we put lorem-ipsum text and image blocks instead of real content so that the example appears generic. Also, an example of module “A” should not contain module “B”. Instead, the place where module “B” would appear should be given to a generic placeholder block.
One directory that is exempt from the above Hologram rule of generic examples, is the “Examples” directory (currently named “Pages”). This directory is specifically there to provide a real life look of a block of markup, which includes real images, text, Modules inside of Modules, and even CSS animations, if necessary.
“Examples” directory in the pattern library also acts as visual “feature tests” to make sure that examples work together as expected when combined together.
As for the generic blocks of content, we use holder.js library to generate them.
Percy is a testing platform that we use to test for visual regressions. Each page in the pattern library is screenshot by Percy during CI builds, to provide developers and designers with information about any visual regressions.