Skip to content

Instantly share code, notes, and snippets.

@janogarcia
Last active February 13, 2022 03:13
Show Gist options
  • Save janogarcia/8c140ff9294a285830a0 to your computer and use it in GitHub Desktop.
Save janogarcia/8c140ff9294a285830a0 to your computer and use it in GitHub Desktop.
CSS Coding Guidelines

CSS Coding Guidelines

Table of contents

Browser Support

As a general rule, browsers which global usage share is below 1% are not supported. Other browsers which may show a higher share are not supported for technical reasons (Internet Explorer 9-, Opera Mini, UC Browser...).

Global usage share statistics can be obtained from http://caniuse.com/usage_table.php.

Desktop

Browser Version
Internet Explorer 10+ (sites and apps)
Chrome latest
Firefox latest
Safari latest

Not included:

  • Below 1%: Opera.

Mobile

Browser Version
Android Browser 4.3+
IE Mobile latest
Chrome Android latest
Safari iOS latest

Not included:

  • Above 1%: Opera Mini, UC Browser for Android.
  • Below 1%: Opera Mobile, Blackberry Browser, Firefox for Android.

Browser Testing

Use BrowserStack.
http://www.browserstack.com/

If you need to test Internet Explorer locally, get the official virtual machines from modern.IE.

Preprocessor

Use LESS.
http://lesscss.org/

Postprocessor

Adding and maintaining browser vendor prefixes mixins is both time consuming and error prone. This process can be totally automated via Autoprefixer, wich adds just the necessary vendor prefixes according to the up-to-date caniuse.com database.

Use Autoprefixer.
https://github.com/postcss/autoprefixer

Length Units

By not setting the base font size and breakpoints to an absolute value we let the Browser and OS combination define the native optimal base font size, wich helps in readability, as well as users set their preferred base font size, which helps in accesibility.

Whenever the base font size is changed our UI will scale accordingly, as if the user had used the browser's zoom. As a quick example, here's a Foundation's Zurb Docs page when doubling the base font-size from 16px to 32px via browser settings and when zooming the page to 200%. Almost identical results.

Read more on the subject in Cloud Four's Proportional Media Queries FTW! and this W3Conversions post.

Set html base font-size to 100%, 1em, or inherit. This is equivalent to 16px for many browser and OS combinations, and that's the proportion that our UI will be based on.

Refer to this table to decide which unit to use in each case.

Unit Use for
em media query breakpoints
rem font-size, dimensions (margin, padding, height)
% width (mainly for grid units)
px small details (borders, corners, shadows, images)
unitless line-height

PX to REM conversion

Design comps and deliverables will typically be set in px. To convert px to a proportional rem value, divide by 16:

px rem
16px 1rem
10px 0.625rem
... ...

Alternatively, if you prefer not having to calculate the rem values manually, use this simple px to rem conversion technique http://codepen.io/janogarcia/pen/bNrKEP/.

px rem
16px 16/@px
10px 10/@px
... ...

PX to EM conversion for breakpoints

To convert px to a proportional breakpoint em value, divide by 16:

px breakpoint em
640px 40em
1025px 64.0625em
... ...

Breakpoints

Define 3 to 5 global breakpoints, those that will affect the overall site layout and design. It is key that those breakpoints are agreed and shared with the designer.

As a starting point, you can refer to how breakpoints are configured on popular Front-end frameworks.

Add other minor breakpoints as necessary, those that affect only a few components (a special navigation bar, a media gallery...) for which you need a more fine breakpoint control, or otherwise would break their layout.

A major limitation of media query breakpoints is that they are based on the browser viewport. If you need ultimate component responsiveness, you'll need element queries, that is, media queries that are based on the component root, not on the browser viewport. This allows a component to be aware of the available width of the context where is being placed (think moving a horizontal menu from the header to a sidebar).

Techniques for Responsive Typography (section "Using Element Queries")
Responsive Elements

Mobile First

For best rendering performance, start with mobile styles first and then override those styles as necessary as the viewport gets larger.

Code Formatting and Style

Editor

Set your editor to the following settings to avoid common code inconsistencies and dirty diffs:

  • Use soft-tabs set to four spaces.
  • Trim trailing white space on save.
  • Set encoding to UTF-8.
  • Add new line at end of files.

HTML

Follow HTML style from http://codeguide.co/#html, with the following additions and exceptions:

  • Use soft tabs with 4 spaces instead of 2.
  • Write one discrete, block-level element per line.
  • Always use lowercase tag and attribute names.

HTML attributes should be listed in alphabetical order.

<a class="{{class}}" data-name="{{name}}" href="{{url}}" id="{{id}}">{{text}}</a>

Elements with multiple attributes can have attributes arranged across multiple lines in an effort to improve readability and produce more useful diffs.

<a class="[value]"
 data-action="[value]"
 data-id="[value]"
 href="[url]">
    <span>[text]</span>
</a>

Alphabetize classes, ordered by component, helper, state, and context. Applying modifier classes after each individual component.

<div class="g collection collection--1of3 bch mtl tci is-active has-dropdown"></div>

Always escape the following characters:

Character HTML entity
< &lt;
> &gt;
& &amp;

Additionally, escape single and double quotes for attribute values:

Character HTML entity
" &quot;
' &#39;

CSS

  • Use one discrete selector per line in multi-selector rulesets.
  • The opening brace { on the same line as the last selector, preceded by a space.
  • The first declaration on a new line after the opening brace {.
  • Each declaration indented by four spaces.
  • Each declaration on its own new line, with a trailing ;.
  • Properties and values on the same line.
  • Add a space after :.
  • The closing brace } on its own new line, and in the same column as the first character of the ruleset
  • Add a space after // comments.
// A basic example showing the most basic CSS formatting rules.

.selector-a,
.selector-b {
    color: #aaa;
}
  • Order properties alphabetically.
  • Add a space after commas in values, e.g., rgba(0, 0, 0, 0.5)
  • Use lowercase and shorthand hex values, e.g., color: #aaa;. Lowercase letters are much easier to discern when scanning a document as they tend to have more unique shapes.
  • Use double quotes consistently, e.g., content: ""
  • Double quote attribute values in selectors, e.g., input[type="checkbox"]
  • Where allowed, avoid specifying units for zero-values, e.g., margin-top: 0; instead of margin-top: 0px;.
  • Strive to limit use of shorthand declarations to instances where you must explicitly set all the available values. Common overused shorthand properties include: padding, margin, font, background, border, border-radius. Excessive use of shorthand properties often leads to unnecessary overrides and unintended side effects.
  • Separate each ruleset by a blank line.
// A complete example on CSS formatting.

.selector-a,
.selector-b {
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
    color: #aaa;
    font-style: italic;
    padding-bottom: @b-space * 3;
}

input[type="checkbox"] {
    content: "";
    margin-top: 0;
}

Exceptions and slight deviations

Large blocks of single declarations can use a slightly different, single-line format. In this case, a space should be included after the opening brace and before the closing brace.

.icon {
    background-image: url(/img/sprite.svg);
    display: inline-block;
    height: 16px;
    width:  16px;
}

.icon--home     { background-position:   0     0  ; }
.icon--person   { background-position: -16px   0  ; }
.icon--files    { background-position:   0   -16px; }
.icon--settings { background-position: -16px -16px; }

Long, comma-separated property values - such as collections of gradients or shadows - can be arranged across multiple lines in an effort to improve readability and produce more useful diffs. There are various formats that could be used; one example is shown below.

.selector {
    background-image:
        linear-gradient(#fff, #ccc),
        linear-gradient(#f3c, #4ec);
    box-shadow:
        1px 1px 1px #000,
        2px 2px 1px 1px #ccc inset;
}

Specificity

  • Stick with classes instead of IDs for styling.
  • Avoid unnecessary nesting, limit it as much as possible. This prevents overly-specific CSS selectors.

LESS

  • Extends and Mixins should be placed before standard properties.
  • Always place :extend statements on the first lines of a declaration block.
  • Group .mixin statements at the top of a declaration block, after any :extend statements, ordered alphabetically.
  • Where possible, write numbers at the end of mathematic operations, e.g., @b-space * 0.5.
// A basic example on LESS formatting.

.selector {
    &:extend(.clearfix all);
    .flex(1 1 50%);
    .transition(opacity 0.2s ease);
    padding-bottom: @b-space * 0.5;
}

A complete example on LESS and CSS formatting

You don't need to memorize the LESS and CSS formatting rules, just refer to the following snippet, which includes all the listed rules.

// A complete example on LESS and CSS formatting.

.selector-a,
.selector-b {
    &:extend(.clearfix all);
    .flex(1 1 50%);
    .transition(opacity 0.2s ease);
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
    color: #aaa;
    font-style: italic;
    padding-bottom: @b-space * 3;
}

input[type="checkbox"] {
    content: "";
    margin-top: 0;
}

Naming Conventions

Naming conventions in CSS are hugely useful in making the code more strict, more transparent, and more informative.

A good naming convention will better communicate what type of thing a class does, where can be used, and the relationships between classes.

This has several benefits when reading and writing HTML and CSS:

  • It helps to distinguish between the classes for the root of the component, descendent elements, and modifications.
  • It keeps the specificity of selectors low.
  • It helps to decouple presentation semantics from document semantics.

The following naming convention, based on Medium's and SUIT CSS style guides, relies on structured class names and meaningful hyphens (i.e., not using hyphens merely to separate words).

.component {}
.component.is-animating {}
.component--modifier {}

.component-part {}
.component-anotherPart {}

A couple of examples sites that follow strict naming conventions:

JavaScript Hooks

syntax: js-<targetName>

JavaScript-specific classes reduce the risk that changing the structure or theme of components will inadvertently affect any required JavaScript behaviour and complex functionality. It is not neccesary to use them in every case, just think of them as a tool in your utility belt. If you are creating a class, which you dont intend to use for styling, but instead only as a selector in JavaScript, you should probably be adding the js- prefix. In practice this looks like this:

<a href="/login" class="btn btn-primary js-login"></a>

JavaScript-specific classes should not, under any circumstances, be styled.

Utilities

Utility classes are low-level structural and positional traits. Utilities can be applied directly to any element; multiple utilities can be used together; and utilities can be used alongside component classes.

They exist because certain CSS properties and patterns are used frequently. For example: floats, containing floats, vertical alignment, text truncation. Relying on utilities can help to reduce repetition and provide consistent implementations.

Because utilities are so focused, they will generally use !important to ensure their styles are always applied.

<div class="u-clearfix">
    <p class="u-textTruncate">{$text}</p>
    <img class="u-pullLeft" src="{$src}" alt="">
    <img class="u-pullLeft" src="{$src}" alt="">
    <img class="u-pullLeft" src="{$src}" alt="">
</div>

Syntax: u-<utilityName>

Utilities must use a camel case name, prefixed with a u namespace. What follows is an example of how various utilities can be used to create a simple structure within a component.

<div class="u-clearfix">
    <a class="u-pullLeft" href="{$url}">
        <img class="u-block" src="{$src}" alt="">
    </a>
    <p class="u-sizeFill u-textBreak"></p>
</div>

Responsive utilities

Certain utilities can have responsive variants using the patterns: u-sm-<name>, u-md-<name>, and u-lg-<name> for small, medium, and large media query breakpoints.

Components

Syntax: <componentName>[--modifierName|-descendantName]

Component driven development offers several benefits when reading and writing HTML and CSS:

  • It helps to distinguish between the classes for the root of the component, descendant elements, and modifications.
  • It keeps the specificity of selectors low.
  • It helps to decouple presentation semantics from document semantics.

You can think of components as custom elements that enclose specific semantics, styling, and behaviour.

ComponentName

The component's name must be written in camel case.

.myComponent { /* … */ }
<article class="myComponent"></article>

componentName--modifierName

A component modifier is a class that modifies the presentation of the base component in some form. Modifier names must be written in camel case and be separated from the component name by two hyphens. The class should be included in the HTML in addition to the base component class.

/* Core button */
.btn { /* … */ }
/* Default button style */
.btn--default { /* … */ }
<button class="btn btn--primary"></button>

componentName-descendantName

A component descendant is a class that is attached to a descendant node of a component. It's responsible for applying presentation directly to the descendant on behalf of a particular component. Descendant names must be written in camel case.

<article class="tweet">
    <header class="tweet-header">
        <img class="tweet-avatar" src="{$src}" alt="{$alt}"></header>
    <div class="tweet-body"></div>
</article>

componentName.is-stateOfComponent

Use is-stateName for state-based modifications of components. The state name must be Camel case. Never style these classes directly; they should always be used as an adjoining class.

JS can add/remove these classes. This means that the same state names can be used in multiple contexts, but every component must define its own styles for the state (as they are scoped to the component).

.tweet { /* … */ }
.tweet.is-expanded { /* … */ }
<article class="tweet is-expanded"></article>

Text Direction

In order to support both LTR (most languages) and RTL (Arabic, Hebrew, Persian...) languages our styles should be text direction agnostic.

We'll serve two separate stylesheets for LTR and RTL languages. We won't be using RTL overrides on top of a LTR stylesheet: usually implemented as a second stylesheet containing the same selectors as the LTR one, or in the same stylesheet but preceding the RTL selectors with [dir=ltr] or .dir-rtl.

First, set the global text direction variables:

// Global: text direction

// For LTR set @base-text-direction: ltr;
// For RTL set @base-text-direction: rtl;
@base-text-direction: ltr;

// For LTR set @start: left; @end: right;
// For RTL set @start: right; @end: left;
@start: left;
@end: right;

Then, add proper lang and dir attributes to the <html> tag:

<!-- English -->
<html lang="en" dir="ltr">
<!-- Arabic -->
<html lang="ar" dir="rtl">

Additionaly, when dealing with a RTL language, add direction: rtl; and unicode-bidi: embed; styles to the body element:

body {
    & when (@base-text-direction = rtl) {
        direction: rtl;
        unicode-bidi: embed;
    }
}

Once you have properly set up the html and body elements language and text direction properties, make sure to turn every hardcoded horizontal property and value in your stylesheet, that is not centered or symmetrical, into a dynamic -text direction agnostic- horizontal value.

Use start and end, instead of the harcoded left and right positional names throughout your code (variable names, class names, properties, values...):

// Technique inspired on Yahoo! Atomic CSS
// https://www.haikudeck.com/atomic-css-science-and-technology-presentation-dJ0xlFjhBQ#slide-71

@blockquote-padding-start: 15/@px;

// left for LTR, right for RTL
.float-start { float: @start; }

// right for LRT, left for RTL
.float-end { float: @end; }

.position-bottom-start {
    bottom: 0;
    @{start}: 0;
}

.position-bottom-end {
    bottom: 0;
    @{end}: 0;
}

Watch out for the following properties, which may contain non-centered horizontal positions:

  • text-align
  • float
  • clear
  • margin
  • padding
  • border
  • border-radius
  • background-position
  • right
  • left
  • direction

Keep an eye, too, on transforms that affect any directional UI element (arrows, triangles, angle brackets...): rotate(90deg) in LTR becomes rotate(-90deg) for RTL.

Also, transform functions order matters. For example, let's say we're applying a translateX(10px) transform in LTR to an arrow icon pointing to the right, the same transform in RTL plus inverting the icon direction will be either scaleX(-1) translateX(10px) or translateX(-10px) scaleX(-1) for RTL, but not scaleX(-1) translateX(-10px) or translateX(10px) scaleX(-1).

Avoid using shorthand values for those properties that deal with horizontal dimensions and both left and right dimensions are not equal or symmetrical: border-width:, border-color:, border-style:, border-radius:, margin:, padding:...

If you really need to use shorthand values, wrap them inside a LESS CSS Guard or Mixin:

.border-rounded-start {
    & when (@base-text-direction = ltr) {
        border-radius: @component-base-border-rounded 0 0 @component-base-border-rounded;
    }
    & when (@base-text-direction = rtl) {
        border-radius: 0 @component-base-border-rounded @component-base-border-rounded 0;
    }
}

Flip horizontally every relevant or directional image. Typical examples of directional images include icons and icon sprites. As for relevant pictures and illustrations, you could create two versions, image.png and image-rtl.png, or less ideally make use of CSS transforms.

If you're cropping an image with a container element, you'll probably want to normalize the container direction to direction: ltr so that the cropped area of the image is always the same no matter the agent's language direction.

Use a suitable font that supports the target RTL languages, or use basic Helvetica, Arial, sans-serif fonts and no italics.

Use LTR overrides direction: ltr; for that data that is always LTR: an <input> for an URL, numbers, telephone numbers...

The direction property in CSS sets the direction of content flow within a block-level element. This applies to text, inline, and inline-block elements. It also sets the default alignment of text and the direction that table cells flow within a table row.

It tells the browser the direction in which the displayed text is intended to be read, but it does not change the writing direction of text. The following example indicates that the direction of the Hebrew text is right to left:

<p>The bulk of the content is in English and flows left to right,
until this phrase in Hebrew makes an appearance,
<span lang="he" dir="rtl"> שָׁלוֹם</span> (meaning hello), which
needs to be set to read right-to-left.</p>`.

There is a specific HTML tag that can be used for changing the direction of text: <bdo> the bidirectional override element. This exists so there is a semantics-free element to use just for this purpose:

<!-- This text will go left to right. -->
<p>This text will go left to right.</p>
<!-- .tfel ot thgir og lliw txet sihT -->
<p><bdo dir="rtl">This text will go right to left.</bdo></p>

Alternatively, you can use these helpers for local overrides:

[dir="ltr"],
.dir-ltr {
    direction: ltr;
    unicode-bidi: embed;
}

[dir="rtl"],
.dir-rtl {
    direction: rtl;
    unicode-bidi: embed;
}

bdo[dir="ltr"],
.dir-ltr-bdo {
    direction: ltr;
    unicode-bidi: bidi-override;
}

bdo[dir="rtl"],
.dir-rtl-bdo {
    direction: rtl;
    unicode-bidi: bidi-override;
}

For text of unknown directionality, for instance user generated content, we can add dir="auto" to a wrapping element to let the user agent decide.

Characters and Punctuation

Make sure to always use the proper chracters and punctuation symbols for the text language in any context (HTML, CSS or JavaScript).

For example, in Chinese use the full-width comma (single character) instead of the English comma and space combination , (two characters). Same for full stops , colons and question marks , to name a few.

Feel free to check the Punctuation articles on Wikipedia .

Inspiration and Reference

Style guides, patterns, and frameworks

Area Tools
Communication
  • Brand assets guidelines
  • Content style guide
Design
  • UX pattern library
  • Design language
Development
  • Front-end coding guidelines
  • Front-end framework
  • Live style guide

CSS - Coding standards

Preprocessor-based

CSS - Polls

http://css-tricks.com/polls/

CSS - Modular architectures

They are in essence conventions for selector naming and their combinators:

  • Namespacing
  • Meaningful dashes and underscores
  • Module semantics (parent, children, extensions...)

Plus some ideas that revolve around OO design principles:

  • Separation of concerns (CSS and HTML decoupling)
  • SOLID principles

CSS - Front-end boilerplates, patterns, and frameworks

Popular

Niche

More tools

Responsive patterns

Quick Reference

HTML

CSS

Chars

Multilingual

Multilingual: Text direction

Validation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment