CSS conventions
Concepts
ITCSS
Introduction
ITCSS stand for Inverted Triangle architecture for CSS, it is a way to better manage specificity and to categorise styles.
- A sane, scalable, managed architecture.
- A school-of-thought, not a library.
- A meta framework; a framework for frameworks.
The layers are organised in an order following those metrics :
- From the generic to the explicit.
- From low specificity to high specificity.
- From global reach to localized reach.
The ITCSS architecture layers
Please note that we use a slightly modified ITCSS architecture from the original
- Settings: Global variables, config switches.
- Tools: Global mixins and functions.
- Generic: Ground-zero styles (Normalize.css, resets, box-sizing).
- Base/Elements: Unclassed HTML elements (tag selectors).
- Layouts: Cosmetic-free design patterns for layout purposes (like grid, or spacing).
- Typography: Base style for text and title
- Components: Designed components, chunks of UI.
- Utilities: Helpers and overrides (mostly single properties classes with !important on them).
Follow ITCSS order for importing your files
Note that all settings
and tools
are bundled into a file nammed all-settings
.
This file is the only file required by all other files.
This File will not output any css when compiled appart from the font-faces declarations.
/* mandatory */@import '@mozaic-ds/styles/settings-tools/all-settings';/* Generics */@import '@mozaic-ds/styles/generics/g.reset.scss';@import '@mozaic-ds/styles/generics/g.box-sizing.scss';/* Elements */@import '@mozaic-ds/styles/elements/e.inputs.scss';@import '@mozaic-ds/styles/elements/e.headings.scss';@import '@mozaic-ds/styles/elements/e...scss';/* Layouts */@import '@mozaic-ds/styles/layouts/l.grid.scss';@import '@mozaic-ds/styles/layouts/l.flex.scss';@import '@mozaic-ds/styles/layouts/l....scss';/* Components */@import '@mozaic-ds/styles/components/c.buttons.scss';@import '@mozaic-ds/styles/components/c.input-text.scss';@import '@mozaic-ds/styles/components/c....scss';/* Utilities */@import '@mozaic-ds/styles/elements/u.spaces.scss';@import '@mozaic-ds/styles/elements/u.text-align.scss';@import '@mozaic-ds/styles/elements/u....scss';
BEM
Introduction
BEM was created by Yandex, the "Russian Google" in 2009.
It is a component based architecture and naming convention that stand for Block / Element / Modifier and relies only on classes to create meaningful scalables architectures.
We will explain the basics here, but we recommend developers on learning BEM : read the full documentation.
Note that in the following examples, the prefixes are symbolised by pr
in front of the classes.
Blocks
You can think of them as components, they are reusable pieces of interface.
The block name defines the namespace for its elements and modifiers.
css/scss syntax :
.pr-block-name { // ...}
An interface can contain mulitple instances of the same block
Elements
A constituant of a block, or children
The element name is separated from the block name by a double underscore (__)
.
css/scss syntax :
.pr-block-name__element-name { ...;}
Modifiers
A BEM entity that defines a variation in appearance and behavior of a block or an element.
The modifier name is separated from the block or element name by a double hyphen (--)
.pr-block-name--modifier-name { // ...}.pr-block-name__element-name--modifier-name { // ...}
why BEM ?
Just by looking at a piece of markup :
- you can know witch classes are related to others
- you can also know in witch file they are defined, reducing headache
- you can understand the role of the classes (And therefore better follow the SRP)
Following theses practices will help you:
- make decision about emplacement easily
- get self documented code, and logically reduce the need for CSS documentation
- keep specificity low (because most of your selectors will only be composed of one classe)
- make class colision almost impossible (all classes will be uniques)
- making it safer to have multiple sources of code (any CSS frameworks mixed with our own)
Naming
In general, css classes must be written according to these basic principles :
- Names are written in lowercase Latin letters.
- Words are separated by a hyphen
(-)
. - The block name defines the namespace for its elements and modifiers.
- The element name is separated from the block name by a double underscore
(__)
. - The modifier name is separated from the block or element name by a double hyphen
(--)
Prefixed classes
Following the ITCSS convention, we add a two letters prefixe to all our classes. The first letter of the prefixe is the source ID, meaning a letter that tell you that the classe come from our design system. The second letter is the first letter of the ITCSS layer name.
.ml-[classname]
: Mozaic layout.mt-[classname]
: Mozaic typography.mc-[classname]
: Mozaic component.mu-[classname]
: Mozaic utility
States classes
BEM entities can have states.
States classes starts with an auxilary verb be (or have in fiew cases) like .is-open
or .has-no-results
States classes can only be used in combination with another BEM entity :
.mc-accordion.is-open { ...;}.mc-search__results.has-none { ...;}
A state can be defined by the fact that it may change due to user interactions or server response. Modifiers are a variations of an element. For example, a button can have modifiers to be a loader button, with a spinning icon .mc-button--loader
, but a sate in witch it is waiting for a server response, like .mc-button--loader.is-loading
.
Responsives modifiers
Responsive modifiers are classes that apply theire styles only at a certain breakpoint.
they have a sufix @
follwed by the name of the viewport they apply to from-xl
example: .mu-hidden@from-m
to be able to work, the @ character need to be escaped in the css files .mu-hidden\@from-m
but can be called without the backslash on the dom : <span class="mu-hidden@from-m"> hide me at M screens </span>
Variables and design tokens
Design tokens are variables that defines many properties in a json object that are compiled and exported to multiple formats to be used on web and mobile platforms (or any desired format trully).
For css, the tokens are exported as SCSS variables as well as SCSS maps (to give us the abillity the map though them).
We use two "layers" of tokens :
1 - the base, low level layer
To define all the basic values like colors, sizes etc. As an example, we define all the possible colors in the same place.
Most of the time, you should not have to use thoses variables directly, because a highter level of variable or a mixin should be available to provide authorized values for the type of property/element you are trying to define.
2 - an "elements" level layer
For example, we create a list of authorized values for text-colors or create some tokens to define buttons cosmetic styles. This layer make reference to the base layers values.
In the documentation you may read them expressed as js object keys selectors : color.button.solid.background
when compiled to SCSS the dots are replaced with simple dashes -
the scss variable will be $color-button-solid-background
.
Mixins and functions
All mixins and functions used to build all the part of the Mozaic design system css are imported into the all-settings
sub-bundle.
We take great care to create a lot of mixins to build our css framework so that you can mimic the behavior of a classe by applying his properties into another component without the need to import the related SCSS file into your bundle, but also to give you control and extend the existing classes.
There is three types of mixins :
the set-xxx mixins
include properties / values
Example :
.my-class { @include set-font-face('regular');}
Output :
.my-class { font-familly: LeroyMerlin, arial, sans-serif; font-weight: 400;}
the make-xxx mixins
create complete classes
Example :
@include make-flexy-col(1, 2, (s, m));
Output :
.ml-flexy__col--1of2 { flex: 0 0 50%; max-width: 50%;}@media screen and (min-width: 576px) { .ml-flexy__col--1of2\@from-m { flex: 0 0 50%; max-width: 50%; }}
the mod-xxx mixins
that extend existing class with modifiers
Example :
.mu-hidden { @include mod-from-screens(s, m, l) { display: none !important; }}
Output :
.mu-hidden { display: none !important;}@media screen and (min-width: 576px) { .mu-hidden\@from-m { display: none !important; }}@media screen and (min-width: 1100px) { .mu-hidden\@from-l { display: none !important; }}
Formatting
2 spaces indentation
.mc-button { color: blue; &__icon { margin-left: 1rem; }}
Each opening block should be preceded by a blank line
.mc-button { color: blue; /* blank */ &__icon { margin-left: 1rem; }}
Each properties-value and opening block should be preceded and followed by line-break
(even for one line declarations)
.mc-button { color: blue;}
Declaration Orders
Mixins and function should be declared before properties, and a blank line should be added after
.mc-button { @include set-button-layout(large); @include set-button-theme(standard); color: $color-grey-100; font: $size-font-10;}
Everything that may change the behavior and appearance of an entity should be declared after the standard properties and before the next entity
In that order :
- pseudo-selectors
- state classes
- mediaqueries
- modifiers
.mc-button { padding: 0.25rem 0.5rem; &:hover { /*...*/ } &.is-disabled { /*...*/ &:hover { /*...*/ } } @media screen and (min-width: 200px) { /*...*/ } &--large { /* large modifier styles */ /* then you can repeat the changes here if required */ @media screen and (min-width: 200px) { /*...*/ } } /* then only we can style children elements */ &__icon { /* define icon */ /* then you can repeat the changes here if required */ @media screen and (min-width: 200px) { /*...*/ } &--spining { /* make the thing spin */ } }}
MQpacker / medias queries grouping
All Mozaic blocks/components should be wrapped in /* mqp:start */
and /* mqp:end */
comments to enable MQpacker :
/* mqp:start */.mc-button { /*...*/}/* mqp:end */
Comments
Comments are non-executable statements, which are placed in source code. Comments make source code easier to understand. So you can use as much code comments as you want.
Basics
Two types of comments can be used in SCSS files:
- Multi-line comments (also called native CSS comments): Start with
/*
and end at the next*/
- Single line comments: Start with
//
, and go until the end of the line
.mc-button { /* this is a native CSS comments */ &__text { /*...*/ } // this is a single line comments &__wrapper { /*...*/ }}
Behavior during compilation
Each of these types of comments behaves differently when compiling the SCSS file into a CSS file.
Indeed, Single line comments are not rendered in the final CSS file. This is why single line comments are often called silent comments, because they don't produce any CSS.
On the other hand, Multi-line comments are always rendered in the final CSS file. They're also called loud comment, by contrast with silent comments.
By default, multi-line comments be stripped from the compiled CSS in compressed mode.
If a comment begins with /*!
, though, it will always be included in the CSS output.
.mc-button { // This comment won't be included in the CSS. /* But this comment will, except in compressed mode. */ /*! This comment will be included even in compressed mode. */}
General purpose comments in the top of your file
If you think this is required you can add a general purpose comment in the top of your file, for that, use the SCSS single line comment:
// File containing the styles of the .mc-button component// as well as the different variations of colors or sizes// Don't forget to import the _all-settings.scss file to use variables and mixins.mc-button { /*...*/}
Supports comment
You should avoid support comments, but it is possible when required to add comments on code blocks to give information about what is left to do or more importantly what is here for legacy support purpose only.
Please consider that the TODOS are not a good practice and that using the project management tool for that is way better.
For this type of comment, use native CSS comments.
Add the capitalized flags LEGACY_SUPPORT
and END_LEGACY_SUPPORT
at the start and at the end of the legacy support code block.
Add also the version in witch the support is supposed to be dropped.
.mc-button { /* LEGACY SUPPORT : will be removed in version 2 */ &__text { /*...*/ } /* END LEGACY SUPPORT */ // replace .go-my-block__toto in version 1.9.6 and higher &__wrap { /* TODO: do something (this should be avoided in production) */ /*...*/ }}
Tokens comment
Design tokens are theming/styling constants defined in a JSON file. For more information about tokens, please read the "Design Tokens" chapter in the Getting started section.
It is possible to add a comment to a token property, simply by adding a new key/value
pair to the JSON file.
The key must be named comment
and the value contains the comment you want to write.
{ "color": { "star": { "empty": { "value": "{color.grey.200.value}", "comment": "color for empty star" }, "focus": { "value": "{color.secondary-blue.500.value}", "comment": "color for star on focus" }, "hover": { "value": "{color.secondary-orange.300.value}", "comment": "color for star on hover" } } }}
The comment will also appear in the output files, if they accept the comments:
example of a scss output file:
$color-star-empty: #d3d2d6 !default; // color for empty star$color-star-focus: #25a8d0 !default; // color for star on focus$color-star-hover: #ffc17d !default; // color for star on hover