This guide contains our best practices and suggestions that fit our work environment. Please be welcome to fork and/or make Pull Requests with suggestions, new topics, correcting us on typos, grammar, wrong assumptions, broken links, unclear text, etc. Pull Requests should contain a short description explaining what it is and its importancy.
- Class naming convention
- Coding style
- Colors
- Css for JavaScript
- Designing for different sized plataforms
- Grid
- Stylesheets folders architecutre
- Tools
- Typography
- Units
For most of our overall CSS architecutre, and specially for our class naming convention, we'll be using RSCSS as a reference and inspiration.
In simple terms, we'll have classes for components and for elements inside the components. Components will be named with at least two words, separated by a dash, and elements shall have no dashes, only lowercase letters.
Right
.menu-nav {
> .item {}
}
Wrong
.menu_nav {
> .menu_nav-item {}
}
We strongly recommend reading the full RSCSS documentation to better understand the whole CSS structure system in which we based ours.
You should:
- Use soft-tabs with a two space indent
- Put spaces before
{
in rule declarations (right:.foo {}
; wrong:.foo{}
) - Use
//
for comment blocks (instead of/* */
)
Would be nice if you:
- Avoid using
margin-top
and prefer the use of onlymargin-bottom
. With all your elements having margin only below them it is easier to organize the overall structure.
In order for us to have a standard way of writing CSS we should always try to declare the properties in a specific order. This is not a "frozen" thing, and it is flexible, but the main guideline here is to group some properties together and have these blocks in a specific order.
The declarations groups are the following (and should follow this order):
-
Mixins' includes: these should be the first instructions to be declared. It is not a property, and it should be declared before so it can be overridden by properties declared below it.
-
Box model properties: The CSS box model is essentially a box that wraps around every HTML element. These are
width
,height
,padding
,margin
, andborder
. -
Other layout/positioning properties: properties with the function of positioning elements such as
display
,position
,top
,right
,bottom
, andleft
. -
Typography: properties such as the
font
properties (i.e.:font-size
,font-family
, etc.),text
properties (i.e.:text-transform
,text-decoration
, etc.) andline-height
. -
Aesthetic/cosmetic properties: properties that change the elements visual aspects without changing its size or positioning, such as
background
,color
,box-shadow
,opacity
,cursor
, etc. -
Transition:
transition
properties (i.e.:transition-delay
,transition-duration
,transition-property
, andtransition-timing-function
).
These groups should be separated by a blank line.
Example
.button {
@include btn;
width: 240px;
margin-top: 2rem;
margin-bottom: 4rem;
position: absolute;
top: 8rem;
font-size: $font-size-body;
text-transform: uppercase;
text-decoration: underline;
color: $color-highlight;
background-color: $color-dark;
opacity: 0.6;
box-shadow: 0 2px 4px rgba(#000, 0.2);
transition: opacity 0.2s ease-in;
}
Another relevant pattern to be followed is declaring abscissa properties before ordinate properties if both exists. For example, transformX
should come before transformY
; and width
should come before height
.
Properties should also be spread when possible to avoid conflicts of overriding when not wanted.
Avoid
.bg {
background: fixed $color-main url(../assets/bg.png) center top no-repeat;
}
Prefer
.bg {
background-attachment: fixed;
background-color: $color-main;
background-image: url(../assets/bg.png);
background-position: center top;
background-repeat: no-repeat;
}
Prefer hexadecimal color codes #f7931e
with lowercase letters and abbreviate them to three characters when possible #f36
.
We recommend using variables to set project's color palette with names beginning with color-
. So when using it in any part of project it's implicit that is a color variable and we can take advantage of text auto complete listing only color variables.
When defining the color variables for a project, you can approach it in two different ways.
The first way is to set variables for every hexadecimal that will be used in the project. Some of these colors will be shades of another color, so you can name them with the suffix -light
, -dark
, xlight
, x-dark
, xx-dark
, and so on, like this:
$color-action: #2aaafe;
$color-action-hover: #018fec;
$color-error: #de4163;
$color-coolgray: #c4ced9;
$color-coolgray-light: #f0f2f5;
$color-coolgray-xlight: #f8f9fa;
$color-coolgray-dark: #8d969b;
$color-coolgray-xdark: #51565b;
Here is an example of this applied in a component.
The prefered way to do this, however, is to set only the base colors. And then, when using them, use the function mix
to mix the base color with either black (#000
) or white (#fff
) the amount desired.
$color-action: #2aaafe;
$color-error: #de4163;
$color-coolgray: #c4ced9;
.button {
background-color: $color-action;
&:hover {
background-color: mix(#000, $color-action, 10%);
}
}
Here is an example of this applied in a component.
This way you won't need multiple variables and also will have flexibility to chose shades of colors for your interface as desired when designing the UI. It is recommended to limit the number of shades used to simplify the interface visually, but that limitation can vary a lot from project to project.
Also, when using color variables, create them for colors and then create a different variable with a name that specify its use, the following way.
$color-blue: #2aaafe;
$color-pink: #de4163;
$color-coolgray: #c4ced9;
$color-action: $color-blue;
$color-error: $color-pink;
When using shades of gray on your project, set the values using the HSL format (e.g.: hsl(0, 0%, 30%)
).
HSL stands for hue, saturation, and lightness - and represents a cylindrical-coordinate representation of colors.
An HSL color value is specified with: hsl(hue, saturation, lightness)
. This format makes it easy to see how dark or light the shade of gray is, and easy to change it by only changing the lightness
value.
Right
color: hsl(0, 0%, 25%); // dark gray
color: hsl(0, 0%, 40%); // medium gray
color: hsl(0, 0%, 80%); // light gray
Wrong
color: #404040; // dark gray
color: $color-gray-40; // medium gray
color: #ccc; // light gray
It is also important to avoid many different shades of gray in the project. It is not common for a project to have more than 5 different grays, and in such case the project should be optimized to use only the grays necessary in order to improve the overall UX.
In order to a better management of the amount of grays and the use of them, we suggest you start the css file for colors with the color palette documented right away.
// light gray for background: hsl(0, 0%, 90%)
// light gray for font: hsl(0, 0%, 70%)
// dark gray for background: hsl(0, 0%, 40%)
// dark gray for font: hsl(0, 0%, 30%)
It should never be used in a project. Use it only for fast prototyping and testing specific components.
color: red;
color: gold;
color: fuchsia;
Don't use CSS classes to query DOM elements on JavaScript.
Set role
attribute to your components and leave CSS classes for styling purposes only because it isn't obvious which class names are for styles and which have JS behaviors bound to them.
Need to include a study about perfomance when querying through role attribute.
Need to include a reference on not using classes to query DOM elements.
Wrong
<div class='user-info'>...</div>
$('.user-info').on('hover', function() { ... });
Right
<div class='user-info' role='avatar-popup'>...</div>
$('[role~="avatar-popup"]').on('hover', function() { ... });
Use role
attributes only to query elements. If your Javascript needs to change a style add/remove classes. Don't set/unset other element attributes.
Responsive web design is not only the adaptation of the user experience to mobile devices, but designing and developing for every possible resolution and ratio of screen.
Therefore, one very useful strategy to responsive design and development is use a fluid structure to your layout. Container dimensions with values set in percentage or relative units (vw
, vh
) are often useful to make sure the sizes will adapt according to available space.
However, breakpoints are useful for adapting specificities and they should be used to better adapt the layout in specific circumstances. Also, break points should be set in variables so it is easier to change them when needed.
The example below is based on Foundation, adapted to better suit our needs.
Right
// Defining values
$small-range: (0em, 40em); /* 0, 640px */
$medium-range: (40.063em, 64em); /* 641px, 1024px */
$large-range: (64.063em, 90em); /* 1025px, 1440px */
$xlarge-range: (90.063em, 120em); /* 1441px, 1920px */
$xxlarge-range: (120.063em); /* 1921px */
// Defining media queries
$small-only: "only screen and (max-width: #{upper-bound($small-range)})" !default;
$medium-up: "only screen and (min-width:#{lower-bound($medium-range)})" !default;
$medium-only: "only screen and (min-width:#{lower-bound($medium-range)}) and (max-width:#{upper-bound($medium-range)})" !default;
$large-up: "only screen and (min-width:#{lower-bound($large-range)})" !default;
$large-only: "only screen and (min-width:#{lower-bound($large-range)}) and (max-width:#{upper-bound($large-range)})" !default;
$xlarge-up: "only screen and (min-width:#{lower-bound($xlarge-range)})" !default;
$xlarge-only: "only screen and (min-width:#{lower-bound($xlarge-range)}) and (max-width:#{upper-bound($xlarge-range)})" !default;
$xxlarge-up: "only screen and (min-width:#{lower-bound($xxlarge-range)})" !default;
// Usage
@media #{$small-only} { … }
@media #{$medium-up} { … }
@media #{$medium-only} { … }
@media #{$large-up} { … }
@media #{$large-only} { … }
@media #{$xlarge-up} { … }
@media #{$xlarge-only} { … }
@media #{$xxlarge-up} { … }
Also, when setting dimensions for containers and main structural components, try using both fixed values and relative values so you can control how much it will vary according to the viewport, for example:
Avoid
.container {
width: calc(50% + 20em);
max-width: calc(100% - 2em);
}
Prefer
.container {
width: 960px;
max-width: 90%;
}
Why columns? When to use?
We believe in consistency, but also in a content-fluid decision. So that's why we built our own column grid system which should be used only in cases that you have to organize elements in a pre-defined horizontal system.
Not all layouts will have a column system, threfore the use of columns are not mandatory.
Our Solution
We aimed to create a framework that would work inside any container, regardless of its width. We just wanted to customize 3 variables: the width of the element container columns, the number of columns ($nOfColumns
) and the gutter between columns ($gutter
). Then the columns width would be adapted accordingly.
Our solution is a Sass based system that could be set in any layout/component.
It also includes custom settings for other sizes of screen you might be working with. It works by setting up a minimum screen size for tablet or mobile and later working out the columns, gutter variables for those screens.
How to use?
First things first: get to know your variables.
The code is really simple. And it will start as two variables only: number of columns and gutter.
$nOfColumns: 12;
$gutter: 1rem;
$columnWidth: ( 100%/#{$nOfColumns} - #{$gutter} );
You will notice that same variables applies for tablets and mobile.
$nOfColumnsTablet: 8;
$gutterTablet: .5rem;
$columnWidthTablet: ( 100%/#{$nOfColumnsTablet} - #{$gutterTablet} );
$nOfColumnsMobile: 4;
$gutterMobile: .5rem;
$columnWidthMobile: ( 100%/#{$nOfColumnsMobile} - #{$gutterMobile} );
And then, set up your maximum tablet and mobile width.
$tabletWidth: 640px;
$mobileWidth: 480px;
Second stuff comes right after: get to know the system.
There you'll see a bunch of mixins for mobile and tablet. They will be setting up the grid the way it should be based on the variables you have set before. Apply it using @include grid(number)
.
And also, there will be set some offset mixins in case you might wanna get off the grid for some reason. Apply it using @include offSetRight(number)
or @includeOffSetLeft(number)
.
Not all set, yet! Set up your container width too.
Mind that the grid works without setting a width, but it might be compromise the overall look of the application and it's certainly not the best approach for a multiple screen application.
.container {
width: calc(60% + 240px);
max-width: 100%;
margin-right: auto;
margin-left: auto;
overflow: auto;
}
Need to talk about offest
All set! You're good to go! Codepen
In design, vertical rhythm is the structure that guides a reader's eye through the content. Consistent vertical rhythm makes a layout more balanced and a more readable content.
In order to ensure a consistent vertical rhythm, we use a vertical grid module which is half the size of the html
's font-size
, set in em
. This means every* vertical distance should be a multiple of 0.5em
.
It is important to understand that not all layouts will have a consistent vertical rhythm. You could opt to not have this consistency for project-specific reasons and, in this case, this don't have to be taken into consideration.
Project WITH consistent vertical rhythm
html { font-size: 16px; }
p { line-height: 1rem; }
.foo { height: 2.5rem; }
Project WITHOUT consistent vertical rhythm
p { line-height: 17px; }
.foo { height: 2.4rem; }
Furhter reading on this subject 4 Simple Steps to Vertical Rhythm Aesthetic Sass 3: Typography and Vertical Rhythm CSS Baseline: The Good, The Bad And The Ugly Using Sass & Compass Vertical Rhythm to set up typography defaults in a project.
It's important for some pages to have a specific layout for print, especially pages with lots of text.
We recommend that print properties be declared in the same component file. But instead of component's properties to be in a separated media query:
Right
.example-component{
width: 100%;
}
@media print {
.example-component {
width: 80%;
}
}
Wrong
.example-component{
width: 100%;
@media print {
width: 80%;
}
}
Just like we follow RSCSS on CSS organization and class naming, we based our instructions in this post below.
We strongly recommend reading it: I totally forgot about print style sheets
Codeign created the Ambience-Base-Components Style Structure with the collaboration of the members, and after some iterations we come in a version that we believe is the best version to be in this styleguide
We recommend the use of SCSS syntax when using Sass.
SCSS (Sassy CSS)
h1 {font-size: $font-size-h1}
h2 {font-size: $font-size-h2}
.item {
box-shadow: 0 2px 0px #dcffa6,
0 2px 5px #000;
}
Sass
h1
font-size: $font-size-h1
h2
font-size: $font-size-h2
.item
box-shadow: 0 2px 0px #dcffa6, 0 2px 5px #000
Comparing both syntaxes, SCSS allows you to write selectors and attributes on the same line so you can simplify your code. At the same time you need to be careful not to code in a way it's hard to read and understand.
You can write plain CSS in SCSS (or reuse CSS code from elsewhere). Also, for the same reason, the learning curve is smoother for people who already know CSS.
In a project, the rules for typography should be pre-defined and systematic. By doing this it makes sure the overall look and feel is consistent and also prevents problems in the user experience.
A project can have as many font families as you like, but each one of them must have a "reason to be". There should not be an extra font family for no reason, if there is no specific need or advantage for using it.
These font families should always be assigned to variables so they can be re-used in the project. The names of the variables should have a direct link with its "reason to be", as presented below.
Right
$font-family-body: "Tiempos Text", Georgia, Times, serif;
$font-family-headings: "Gotham", "Montserrat", Helvetica, serif;
$font-family-button: "FF DIN", "Roboto", sans-serif;
Wrong
$font-family-serif: "Tiempos Text", Georgia, Times, serif;
$font-family-gotham: "Gotham", "Montserrat", Helvetica, serif;
$button-font-family: "FF DIN", "Roboto", sans-serif;
Then, when assigning font families to selectors, you should assign it as few times as possible so the css code is easier to maintain.
Right
p {
font-family: "Tiempos Text", Georgia, Times, serif;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Gotham", "Montserrat", Helvetica, serif;
}
.action-button {
font-family: "FF DIN", "Roboto", sans-serif;
}
Wrong
.sidebar-component {
> .description {
font-family: "Tiempos Text", Georgia, Times, serif;
}
}
For font size and line height values, the variables should be set using the metric prefixes. The variable names should refer to its size in relation to the "base size" (the size used in most of the body text in the project). This way we understand how big or small the text is directly from reading the variable name.
Harry Roberts suggests the greek alphabet notation (
alpha
,beta
,gama
, etc) for setting font sizes names, but we prefere a way that makes more explicit the size in relation to each other (also, his example differs from our due to our use of mixins). You can find more interesting content on font sizing in CSS reading his article "Pragmatic, practical font sizing in CSS".
Right
$font-size-mega: 4em;
$font-size-kilo: 3em;
$font-size-hecto: 2em;
$font-size-deca: 1.5em;
$font-size-base: 1em;
$font-size-deci: 0.75em;
$font-size-centi: 0.625em;
$line-height-mega: 3.5rem;
$line-height-kilo: 3rem;
$line-height-hecto: 2.5rem;
$line-height-deca: 2rem;
$line-height-base: 1.5rem;
$line-height-deci: 1rem;
$line-height-centi: .5rem;
Wrong
$font-size-extralarge: 4em;
$font-size-large: 3em;
$font-size-medium: 2em;
$font-size-small: 1em;
$font-size-extrasmall: 0.5em;
Also Wrong
$font-size-colossal: 4.5em;
$font-size-giant: 4em;
$font-size-huge: 3em;
$font-size-large: 2em;
$font-size-medium: 1em;
$font-size-small: 1em;
$font-size-tiny: .8em;
$font-size-minuscule: 0.6em;
Use the font size attribute on html tag selectors (h1, h2, p, etc) so you won't have to do so on specific situations unless it is an exception.
Right
p {
font-size: $font-size-base;
line-height: $line-height-base;
}
h1 {
font-size: $font-size-mega;
line-height: $line-height-mega;
}
h2 {
font-size: $font-size-kilo;
line-height: $line-height-kilo;
}
h3 {
font-size: $font-size-hecto;
line-height: $line-height-hecto;
}
h4 {
font-size: $font-size-deca;
line-height: $line-height-deca;
}
h5 {
font-size: $font-size-centi;
line-height: $line-height-centi;
}
h6 {
font-size: $font-size-mili;
line-height: $line-height-mili;
}
.news-item{
> .caption {
font-size: $font-size-centi;
line-height: $line-height-centi;
}
}
Avoid the need for repeating a font size variable in your code. Preferably, it should be set only twice: in the html tag selectors (e.g.: h2
) or in a mixin (e.g.: @mixin caption
).
Right
@mixin btn {
font-size: $font-size-centi;
// You would also want to add other "btn" styles here…
}
.action-button {
@mixin btn;
}
.confirmation-popup {
> .button {
@mixin btn;
}
}
Wrong
.action-button {
font-size: $font-size-centi;
}
.confirmation-popup {
> .button {
font-size: $font-size-centi;
}
}
The font weight value should be declared in numbers (e.g.: 100
), the only valid number ins CSS are multiples of 100, from 100 to 900.
Right
.title {
font-weight: 700;
}
Each weight (hairline, thin, light, regular, medium, semibold, bold, heavy, black) should have its own number value (from 100 to 900). If there are any weights not mention before (such as “extra light”, “demibold” or “ultra black”) this framework can be adatped to fit them as log as thin is always 300, regular is always 400 and bold is always 700.
Right
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-.wotf');
font-weight: 100;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-.wotf');
font-weight: 200;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Light.wotf');
font-weight: 300;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Regular.wotf');
font-weight: 400;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Medium.wotf');
font-weight: 500;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Semibold.wotf');
font-weight: 600;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Bold.wotf');
font-weight: 700;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Heavy.wotf');
font-weight: 800;
}
@font-face {
font-family: 'Greta Sans';
src: url('fonts/GretaSans-Black.wotf');
font-weight: 900;
}
The font-size
property should also have a value in either em
or rem
. The default unit in this case should be em
, but if there's need to used the font size in rem
it is fine :)
Right
h1 {font-size: 2em;}
h2 {font-size: 1.6em;}
p {font-size: 1em;}
Wrong
h1 {font-size: 32px;}
h2 {font-size: 10vw;}
p {font-size: 16pt;}
If the project have a consistent vertical rhythm, the line-height
property should have a value multiple of the vertical grid module of the vertical grid module in the project.
More information on Vertical grid.
Project WITH consistent vertical rhythm
html {font-size: 16px;}
h1 {line-height: 2rem;}
h2 {line-height: 1.5rem;}
p {line-height: 1rem;}
Project WITHOUT consistent vertical rhythm
h1 {line-height: 34px;}
h2 {line-height: 26px;}
p {line-height: 16px;}
In general, vertical margins and paddings (top and bottom) are used as typographical tools to organize, group and arrange text. In order to maintain the vertical rhythm they should follow the same rules of the Units for line-height
.
This means those values should always be multiple of 0.5rem
(if the project have a consistent vertical rhythm).
Project WITH consistent vertical rhythm
h3 {margin-bottom: 2rem;}
Project WITHOUT consistent vertical rhythm
h3 {margin-bottom: 32px;}
.sidebar > h3 {margin-bottom: 30px;}
.article > h3 {margin-bottom: 34px;}