Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

CSS best practices

Contributors Lindsay Lim [X]

Contents Andrew Sheppard

Status DEFINITION

1. Pre-processor Last 08 Mar 2018


2. Organising/Structure reviewed

3. Variables
4. Mobile-first approach
5. Naming conventions
6. Rules
7. Formatting / ordering

Pre-processor
We're using SCSS as our CSS extension language.

Some older projects may use LESS, but going forward SCSS is preferred.

http://sass-lang.com/guide

Organising/structuring
Have a logical method to organising files
Each file should have a specific purpose
ITCSS is one method to solving this

Variables

Rules
Use conventions.
Good rule is to prefix the variable with the name of the component
Prefix global variables with global
Use semantic naming
Name based on function or purpose, not how it looks
Make use of variable maps
Use this tool to name your colours. Read more
Manage z-index's in a single variable map
Separate into multiple files
Keep lowercase, hyphen-separated

Further reading
https://davidwalsh.name/sass-color-variables-dont-suck
http://thesassway.com/beginner/variable-naming
https://webdesign.tutsplus.com/articles/quick-tip-name-your-sass-variables-modularly--
webdesign-13364

Naming examples

// colors.scss
$color-flush-orange: #ff8200;
$color-valhalla: #211551;
$color-deep-koamaru: #171c8f;
$color-persian-blue: #232ad8;

// fonts.scss
$font-family-brand: "Gotham Rounded SSm A", sans-serif;
$font-family-content: "Open Sans", sans-serif;

// variables.scss
// Example of using a variable map
$global-container-padding: (
"mobile": 1.6rem,
"tablet": 2.4rem,
"desktop": 3.2rem
);
$global-border-radius: .3rem;

// Examples of BAD variable names


$padding: 10px; // ambiguous
$globalMaxSiteWidth: 800px; // camelCase
$red: ff0000; // describing style

z-index management

// z-index layers. All z-index values have to be declared here, and use
// the get-z() function
$zindex-layers: (
"modal" : 1000,
"dimmer" : 800,
"header" : 500,
"footerSticky" : 400,
"default" : 1,
"zero" : 0,
"below" : -1
);

// Retrieve z-index value


@function get-z($layer: "default") {
@if map-has-key($zindex-layers, $layer) {
@return map-get($zindex-layers, $layer);
} @else {
@debug "#{$layer} does not exist in the $zindex-layers map.
Property omitted.";
}
}

.header {
...
z-index: get-z("header");
}
Mobile-first approach

What is it?
Is a tenet of progressive enhancement.
Writing mobile styles first, then using min-width media queries to introduce larger-display styles
as they're needed.
The opposite would mean you write desktop-styles first then having to reset properties on small
devices, which can result in more code.

Why?
Code for larger screens is often more complex than mobile.
If your site is good on mobile, that will likely translate well up to larger devices

MQ Mixin
MQ mixin is a third-party mixin that makes media queries easy and intuitive to use. Read more
Highly recommended to use this in your project

Example
Two ways of styling the same component. Desktop-first approach having to use more lines of code.

Note: Rather than using @media (min-width: 768px) I'm using the MQ mixin, which generates the
same thing.

Mobile-first approach

.foo {
padding: 10px;
font-size: 14px;

@include mq($from: "tablet") {


font-size: 16px;
}

@include mq($from: "desktop") {


font-size: 18px;
padding: 20px;
margin: 20px;
border: 2px solid black;
position: relative;
}
}
Desktop-first approach

// 1. Have to repeat these properties again, to remove them from small


devices
.foo {
font-size: 18px;
padding: 20px;
margin: 20px;
border: 2px solid black;
position: relative;

@include mq($until: "tablet") {


font-size: 16px;
padding: 0; // [1]
margin: 0; // [1]
border: 0; // [1]
position: static; // [1]
}

@include mq($until: "mobile") {


padding: 10px;
font-size: 14px;
}
}

Naming conventions
What we want is to be able to write code that is as transparent and self-documenting as possible.
Transparency means that it is clear and obvious (to others) in its intent; self-documenting means that we
don’t have to lose time to writing and reading lengthy, supplementary documentation.

One naming method we've been using is BEM.

What is BEM?

“BEM (Block, Element, Modifier) is a component-based approach to web


development. The idea behind it is to divide the user interface into independent
blocks. This makes interface development easy and fast even with a complex UI,
and it allows reuse of existing code without copying and pasting.”

A block is an independent entity, a “building block” of an application. A block can be either


simple or compound (containing other blocks).
An element is a part of a block that performs a certain function. Elements are context-
dependent: they only make sense in the context of the block they belong to. Is separated from
block name with double-underscore (__).
A modifier is a property of a block or an element that alters its look or behaviour. Is separated
from block or element with double-dash (–).
BEM naming structure

[block]
[block]--[modifier]
[block]__[element]
[block]__[element]--[modifier]

// Avoid this
[block]__[element]__[element]

Why BEM?
BEM naming provides three specific benefits:

1. It communicates purpose or function


2. It communicates component structure
3. It sets a consistent low-level of specificity for styling selectors

Extending BEM
Javascript prefix. Signify that this piece of the DOM has some behaviour acting upon it, and
that JavaScript binds onto it to provide that behaviour. These will not have any CSS associated
with them. e.g. .js-component-name
If using ITCSS as your CSS architecture, we recommend extending BEM to BEMIT. Full
explanation here

Examples
We highly recommend reading this article for detailed examples:

https://seesparkbox.com/foundry/bem_by_example

Further reading
http://getbem.com/introduction/
https://css-tricks.com/bem-101/

Rules
Code smells in CSS - Part 1
Avoid undoing styles
Avoid magic numbers (a value used because 'it just works')
Avoid qualified selectors (styles attached to element)
Use unitless values for line-heights
Avoid brute-force CSS
Careful of wide-reaching selectors
Use !important proactively, not reactively
Don't style ID's
Avoid ambiguous class names
Code smells in CSS - Part 2
Careful with use of @extends
Avoid string concatenation for classes (useing & to concat strings in your classes)
Use full background properties instead of shorthand
Key selector appearing more than once
Key selector shouldn't appear in another file
Use flexbox
Use autoprefixer in your build process
Provide fallback support for non-supporting browsers (IE9)
Classnames should be lowercase, hyphenated, and specific
Try avoid nesting where possible (adds specificity), and limit to 3 levels deep where you cannot.
One selector per line, one rule per line
Use plenty of comments
Media queries should be declared inside a selector. Do not wrap multiple selectors inside a
media query.
This ensures each selector is only written once. Makes it easier when performing a
search as there's one source of truth for a class.
Use rem's for your units. Set it up so 1rem = 10px (example below)
Where allowed, avoid specifying units for zero-values, e.g. margin: 0;
Omit the zero from any values below one. e.g. .5rem instead of 0.5rem
Favour double-quotes over single-quotes
Calculations should be in brackets. e.g. padding: ($global__padding * 2) 3rem;
Use Stylelint and editorconfig to maintain standards
Example Stylelint setup on AustralianSuper Employer Portal
Example editorconfig setup on AustralianSuper Employer Portal

Naming, one selector and property per line

// Good
.credit-card-panel,
.my-component-name {
width: 10rem;
height: 10rem;
margin: 0;
}

// Bad
.red, .myComponentName, .my_component_name {
width: 10rem; height: 10rem;
margin: 0px;
}

Nesting

// Good
.credit-card {}

.credit-card__image {}

.credit-card__title {
.icon {}
}

// Bad
.credit-card {
.credit-card__image {}

.credit-card__title {
.icon {}
}
}
Set up 1rem = 10px

// 1. Standardize 1rem to 10px, to make using rems easier


// Resource: http://snook.ca/archives/html_and_css/font-size-with-rem
html {
font-size: 62.5%; // [2]
}

body {
font-size: 1.6rem;
}
Media queries

// Good
.foo {
...

@include mq($from: "tablet") {


...
}

@include mq($from: "desktop") {


...
}
}

.bar {
...

@include mq($from: "tablet") {


...
}

@include mq($from: "desktop") {


...
}
}

// Bad
.foo {
...
}

.bar {
...
}

@include mq($from: "tablet") {


.foo {
...
}

.bar {
...
}
}

@include mq($from: "desktop") {


.foo {
...
}

.bar {
...
}
}
Commenting

// Add comments for things that may not be immediately obvious


// from the class name or from the property value.
//
// Especially important for properties that provide quirky
// fixes and/or may have a big impact if removed.

// GOOD:

// The <li> element


// 1. Fixes weird IE9 display issue
// 2. Remove default appearance, particularly on mobile OS' such as iOS
.accordion__item {
display: flex;
margin: 0; // [1]
color: red;
appearance: none; // [2]
}

// BAD:

.accordion__item {
display: flex;
margin: 0; // Fixes weird IE9 display issue
color: red;
appearance: none; // Remove default appearance, particularly on mobile
OS' such as iOS

Formatting / ordering

Property Ordering
Ordering properties in a consistent manner can speed up debugging and reduce errors.

Here's one way of grouping by type. Within the groups, it's not imperative that it follows this exact order, e.
g. height could come before width and vice versa
Property ordering

// Group properties by type. positioning first, then display & box model,
then other
// Ordering within the group isn't as important, just be consistent.
// https://css-tricks.com/poll-results-how-do-you-order-your-css-
properties/

.selector {
// Positioning
position: absolute;
z-index: 10;
top: 0;
right: 0;
bottom: 0;
left: 0;

// Display & Box Model


display: inline-block;
overflow: hidden;
box-sizing: border-box;
width: 10rem;
height: 10rem;
padding: .8rem;
border: 1rem solid $color__something;
margin: 1rem;

// Other
background-color: $color__black;
color: $color__white;
font-family: sans-serif;
font-size: 1.6rem;
text-align: right;
}

Selector ordering
Having a consistent pattern for ordering inside a selector can help

Selector ordering

.selector {
// @extends
// @includes
// regular properties
// @media queries
// --modifiers
// parents - modified behaviour when in given parent context
// :pseudo selectors
// child selectors (ideally these wouldn't be nested inside a selector)
}

// Example
// 1. Here is an example comment
// 2. Necessary for weird Firefox bug
// 3. Necessary to override default styling of <small>
.selector {
@extend %subtle-link-transition;
@include fancyTransition("fade", 4);
position: relative;
display: inline-block; // [1]
@include mq($from: "tablet") {
padding: 0;
background-color: red;
}

@include mq($from: "desktop") {


background-color: blue;
overflow: hidden; // [2]
}

&:hover {
background-color: purple;
}

// Shows circle next to our title


&::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
}

&.selector--modifier {
background-color: black;
}

&.selector--is-state {
background-color: black;
}

// Different display when .selector is within marketing component


.c-marketing & {
color: red;
}

// Child element
.small {
font-size: 1.2rem; // [3]
}
}

// Example of BAD code

// This is hard to follow because nothing is grouped.


// Properties are spread between modifiers, have to scan the entire
selector
// to find one property.
// Easy to miss position being declared twice, or that .selector has an
@extend and @include
.selector {
position: relative;
display: inline-block;

// Child element
.small {
font-size: 1.2rem;
}

@include mq($from: "tablet") {


padding: 0;
background-color: red;
}

// Shows circle next to our title


&::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
}

@include mq($from: "desktop") {


background-color: blue;
overflow: hidden;
}

position: absolute;

&.selector--modifier {
background-color: black;
}

&.selector--is-state {
background-color: black;
}

&:hover {
background-color: purple;
}
color: red;
@extend %subtle-link-transition;
@include fancyTransition("fade", 4);
}

You might also like