Formatting
Follow the formatting style used by Prettier (with default settings).
- Indent with 2 spaces
- One selector per line
- One line per declaration (
property: value;
) - Always use
;
at the end of a declaration - Prefer double quotes (
"hello"
), and quote attribute values (input[type="text"]
)
Recommended: install Prettier (npm i -g prettier
or install for your editor) and use it to automatically reformat your CSS or Sass code.
body {
font-size: 0.8rem;
}
.selector,
.other[type="text"] {
font-weight: bold;
color: black;
}
@media (min-width: 768px) {
.something {
display: block;
background-image: url("/assets/img/pattern.png");
}
}
Comments
Prefer Sass comments
/* A CSS comment (standard syntax) */
// A Sass comment (always removed from CSS output)
In Sass (SCSS) source files, prefer the Sass comment syntax, except for the top comment that describes each file.
CSS/SCSS file header
Start each CSS file (especially a component file) with a header comment using the following format: /*! Title */
.
When minifying the CSS, these comments will be kept in the final CSS, which doesn’t add much to the file’s weight but makes it easier to break the minified CSS into several lines.
Don’t repeat obvious information
// The teaser’s title
.Teaser-title {
…
}
/*! Header Component */
// More information about this component
.Header {
…
}
// Use simple comments surrounded by blank lines
// to introduce a group of selectors
.Header-top {
…
}
.Header-social {
…
}
// Use a comment just before the selector if you need to
// explain something particular about this selector
.Header-navItem {
display: table-cell;
// Use a comment just before the line(s) you want to
// explain; avoid comments after or on the right.
vertical-align: bottom;
}
Units: pixels, ems, etc.
It’s not always obvious which CSS units are best for what use cases. Here are some points of reference:
- Avoid physical units such as
cm
andpt
- Media queries: prefer
px
(over usingem
) font-size
: userem
,em
orpx
line-height
: use a unitless ratio, e.g.1.2
Viewport-relative units
1vw
is equal to 1% of the viewport width (100vw
)1vh
is equal to 1% of the viewport height (100vh
), but it can be unreliable in mobile browsers
html {
// should be 16 * 1.25 = 20px
font-size: 125%;
}
body {
font-size: .8rem;
line-height: 1.4;
}
h1 {
font-size: 1.6rem;
line-height: 1.2;
}
@media (min-width: 1100px) {
h1 {
font-size: 2.4rem;
line-height: 1.1;
}
}
Avoid !important
Adding !important
to your declarations helps fixing specificity issues, but only in the very short term! Avoid it.
If you do use !important
in your code, always write a comment explaining why.
.MyComponent-inner {
// The JS library we use forces a width in JavaScript,
// but we really need a fluid width here!
width: auto !important;
}
Property order
Ordering CSS properties in a way that makes sense is not easy. :)
We try to order things from “general” to “particular”, following this order:
- Position
- Display & Box Model
- Text
- Colors & Backgrounds
- Effects and Everything Else
It’s okay if you don’t use the exact same order. Try to follow this guideline but don’t worry too much about it.
.orderExample {
/* Positioning */
position: absolute;
z-index: 10;
top: 0;
right: 0;
float: none;
/* Display & Box Model */
content: "";
display: inline-block;
visibility: visible;
box-sizing: border-box;
width: 100px;
height: 100px;
overflow: hidden;
margin: 10px;
border: 10px solid #333;
padding: 10px;
vertical-align: middle;
white-space: nowrap;
/* Text */
font-family: sans-serif;
font-size: 16px;
font-style: italic;
font-weight: bold;
line-height: 1.4;
text-align: right;
text-indent: 2em;
text-overflow: ellipsis;
text-shadow: …;
text-transform: uppercase;
/* Colors & Backgrounds */
color: #fff;
background-color: #000;
background-image: url(…);
/* Effects and Everything Else */
opacity: .8;
transform: translateX(100%);
transition: all .5s;
cursor: pointer;
box-shadow: …;
filter: …;
mask: …;
will-change: transform;
-webkit-backface-visibility: …;
}
Fonts
Declare fallbacks
Always declare a generic family name at the end of a font-family
declaration: serif
, sans-serif
or monospace
.
If possible, also declare system fonts that are close in style to the web font you’re using:
body {
font-family: Fira Sans, Arial, sans-serif;
}
Using web fonts
- Always get licenses for commercial fonts.
- Host fonts locally (see: Google Fonts Helper).
- Use WOFF2 and WOFF, ignore older formats.
- Use a single family name for all the
@font-face
blocks describing a font family.
Web fonts make websites slow!
Typically, font files are loaded after stylesheets, and browsers can render invisible text until fonts are loaded. A few options for speeding things up:
- Use system fonts instead.
- Use only a few weights and styles.
- Tell the browser to render the text even if fonts are not loaded yet, using the font-display property.
- Preload the most important WOFF2 file(s).
@font-face {
font-family: "My Cool Typeface";
font-style: normal;
font-weight: 400;
font-display: auto;
src: url("/fonts/mycooltyp.woff2") format("woff2"),
url("/fonts/mycooltyp.woff") format("woff");
}
@font-face {
font-family: "My Cool Typeface";
font-style: normal;
font-weight: 800;
/* show the fallback font while the font file is loading */
font-display: fallback;
src: url("/fonts/mycooltyp-heavy.woff2") format("woff2"),
url("/fonts/mycooltyp-heavy.woff") format("woff");
}
body {
font-family: My Cool Typeface, Georgia, serif;
}
<head>
<link rel="preload" as="font" type="font/woff2"
src="/fonts/mycooltyp.woff2">
</head>
Selectors
We use classes for selectors. We try to keep selectors short (ideally, just one class).
Do not use the #some-id
selector (because of high specificity). If you really need to target an id, and cannot add a class, use the attribute selector: [id="some-id"]
.
In addition to classes, you can use:
- Element selectors (
h2
,.MainNav-item a
, etc.). But keep in mind that sometimes the element can change, for example a<h2>
can be changed to a<h3>
, a<a>
to a<button>
, etc. - Attribute selectors, especially for dynamic states (
.MainNav a[disabled]
). - Pseudo-classes (
:empty
,:invalid
, …)
// Bad selector: too specific!
#stream .tweet_header .author {…}
// Better selectors
.Tweet {…}
.Tweet-author {…}
.Tweet-author a {…}
.Tweet-retweetBtn {…}
.Tweet-retweetBtn[disabled] {…}
Utility classes
Utilities are simple styles that can be used on any element to trigger some basic function (in a few lines of CSS at most).
- Write utility classes in
camelCase
, without a prefix. - Avoid using many utility classes. They move a lot of the presentation logic into your HTML, and can hide that a component has dependencies (unlike mixins and variables).
- If you have several utility classes that work together, maybe make a component?
// Utility classes
.icon {
display: inline-block;
vertical-align: middle;
width: 1em;
height: 1em;
}
.visuallyHidden {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
}
Components
A component is a group of styles that represent a block of UI:
- It can be a single element (e.g. a
Button
component) - Or a group of elements (e.g. an article teaser with a container, a title, an image, a short excerpt…)
It can be hard to decide if some part of a design should be a big component, or several smaller components. Use your best judgement, and/or ask coworkers what they think.
One file for each component
Your component should have a upper camel case name, e.g. Header
or MobileNav
. And each component should have its own stylesheet (CSS or SCSS).
You can organize your component styles together:
components/
Footer.scss
Header.scss
MyAwesomeComponent.scss
Teaser.scss
Or using a folder for each component (so you can have the styles, templates, scripts, etc. in the same place):
components/
Footer/
Footer.js
Footer.scss
Footer.tpl
Header/
…
Component and elements
We follow a variant of the BEM methodology.
Each component must have a main element which contains the others (if any). Use the component’s name as its class, for example:
<div class="Teaser">
…
</div>
For component elements, use the component name as a namespace, and add a lower camel case element name:
<div class="Teaser">
<h2 class="Teaser-head">…</h2>
<p class="Teaser-intro">…</p>
</div>
Component element classes should not have more than 2 segments. MyComponent-list-item
is wrong; use MyComponent-listItem
or simply MyComponent-item
. If you need to create a lot of element classes with 2 or 3 words, maybe your component is too complex and should be refactored into smaller components?
Targetting HTML elements
Unlike the stricter BEM methodology, we allow writing selectors that target a HTML element inside a component or component element.
<div class="Teaser">
<h2 class="Teaser-head">
<a href="…">Cool title</a>
</h2>
…
</div>
For example we could style the link inside MyComponent-title
here:
.Teaser-head a {
display: block;
color: inherit;
}
.Teaser-head a:hover,
.Teaser-head a:focus {
color: purple;
}
Modifier classes
Sometimes the component’s container or a component element can have slightly different styles in different places. In those situations we can create style variants, using classes like: Component--variantName
(for the container) or Component-element--variantName
(for an element in the component).
{# Alternative style for the first instance of this component in a list #}
<article class="Teaser Teaser--highlight">
<h2 class="Teaser-head">
<a href="…">…</a>
</h2>
<p class="Teaser-desc">…</p>
</article>
<article class="Teaser">
<h2 class="Teaser-head">
<a href="…">…</a>
</h2>
<p class="Teaser-desc">…</p>
</article>
Responsive styles
Keep all styles for a selector together
Do not separate desktop and mobile styles into different files. You want to keep them tightly together:
.spaceBefore1 {
margin-top: 20px;
}
@media (min-width: 750px) {
.spaceBefore1 {
margin-top: 30px;
}
}
Mobile-first
We use a mobile-first approach. Here’s how it works:
- First, try to find the simpler “state” of a component or element style. Most of the time, it will look like the mobile layout.
- Then, add styles for larger viewports, in a media query.
Using Sass, you can use media query nesting to avoid repeating the selector and making the relationship between the styles clearer.
.MyComponent {
border: solid 1px black;
padding: 10px;
@media (min-width: 750px) {
border-width: 5px;
padding: 25px;
}
}
Avoid doing things like this, it’s doing extra work for nothing:
.MyComponent-image {
float: left;
margin-right: 20px;
@media (max-width: 749px) {
float: none;
margin-right: 0;
}
}
Finally, sometimes the mobile styles are rather specific and not useful for larger screens. In that case, try to separate styles in tree groups: common, small screens, and larger screens.
.MyComponent-image {
display: block;
outline: solid 1px rgba(0,0,0,.25);
outline-offset: -1px;
background-color: #eee;
@media (max-width: 749px) {
width: calc(100% - 40px);
max-width: 360px;
margin: 0 auto;
}
@media (min-width: 750px) {
float: right;
margin-right: 20px;
}
}
Share breakpoints between components
Try to use 2–3 main breakpoints and reuse them throughout the site or application. Using Sass, you can store those breakpoints in variables:
$bp-medium: 750px;
$bp-large: 1100px;
$bp-xlarge: 1400px;
// By convention, when using max-width you should
// remove 1px to avoid conflicts if the viewport
// width is exactly $bp-medium
@media (max-width: $bp-medium - 1px) {…}
@media (min-width: $bp-medium) {…}
Use named media queries
Name media queries allow you to define a media query once, and reuse it every time. In CSS proposals, it looks like this:
@media (--my-media-query) {…}
Since this feature is not standardized or supported yet, we can mimick this behavior in Sass to make our Sass code easier to maintain. Use this mq-build function to store full media queries in variables:
@import "node_modules/sass-mq-build/mq-build";
$mq-small: mq-build(null, 750px);
$mq-medium: mq-build(750px, 1100px);
$mq-large: mq-build(1100px, null);
.something {
@media ($mq-small) {
color: green;
}
@media ($mq-medium) {
color: red;
}
}
Sass Selector Nesting
Selector nesting makes your code more difficult to read. Avoid unnecessary nesting.
- Nest once if helpful, and twice at most.
- Keep the resulting CSS selectors as short as possible.
- Do not overuse the
&
symbol.
Avoid composing class names
Do not use the &
symbol to create a class name that extends the parent class:
.Header {
…
&--dark {…}
&-logo {…}
}
In this example, if a developer looks at the HTML code and wants to find the CSS code for the Header--dark
or Header-logo
classes, they won’t be able to find it in the codebase!
// BAD: unnecessary nesting
.Header {
.Header-top {
.Header-logo {…}
}
}
.Nav {
ul {
li {
a {…}
}
}
}
// GOOD: limit nesting
.Header {
…
}
.Header-top {
…
}
.Header-logo {
…
}
.Nav a {
…
}
Sass Variables
We use the Sass preprocessor, with the SCSS syntax.
Sass files should have a .scss
extension.
Use node-sass
to compile .scss
files to CSS. Generally, this is integrated in a build config using gulp or webpack. Look for the documentation in the project (in e.g. a README.md
or doc/FRONTEND.md
).
Variables and mixins
Use lowercase names with hyphens (not underscores), and follow an general-to-particular progression:
- Global variables and mixins:
$type-name
,@mixin some-name
- If specific to a component:
$component-type-name
,@mixin component-some-name
// base/_variables.scss
$color-brand: #e95d1e;
$color-link: #08c;
$breakpoint-small: 500px;
$breakpoint-medium: 860px;
// base/_grid.scss
@mixin grid-container(…) {…}
@mixin grid-item(…) {…}
// component/_header.scss
@import "../base/variables";
@import "../base/grid";
$header-color-bg: #555 !default;
$header-color-link-main: #fff !default;
$header-color-link-active: #faa !default;
.Header {
background-color: $header-color-bg;
}