CSS Style Guide - BEVM Approach

| Comments

I’m trying to maintain a living CSS style guide which helps as a self guide for me & to get insights from others. Feel free to drop on comments section about what you use and what you feel can be changed here.

Table of Contents

BEVM

I use a BEM-like syntax picked mostly from Spatie CSS Guidelines & The “variation” is a concept picked up from Chainable BEM modifiers,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.component                      /* Component */
.component__element             /* Child */
.component__element__element    /* Grandchild */

.items                          /* Use plurals if possible */
.item

.-modifier                      /* Single property modifier, can be chained */

.component--variation           /* Standalone variation of a component */
.component__element--variation  /* Standalone variation of an element */

.helper-property                /* Generic helper grouped by type (eg. `align-right`, `margin-top-s`) */

.js-hook                        /* Script hook, not used for styling */

.component and .component__element

1
<div class="news"></div>
  • A single reusable component or pattern
  • Children are separated with __
  • All lowercase, can contain - in name
  • Avoid more than 3 levels deep
1
2
3
4
5
<div class="news">
    <div class="news__item">
        <div class="news__item__publish-date"></div>
    </div>
</div>

Be descriptive with component elements. Consider class="team__member" instead of class="team__item"

1
2
3
<div class="team">
    <div class="team__member"></div>
</div>

You can use plurals & singulars for readability. Consider class="member" instead of class="members__member"

1
2
3
<div class="members">
    <div class="member"></div>
</div>

.-modifier

1
<div class="button -rounded -active"></div>
1
2
3
4
5
6
7
8
.button {
    &.-rounded {

    }

    &.-active {
    }
}
  • A modifier changes only simple properties of a component, or adds a property
  • Modifiers are always tied to a component, don’t work on their own (make sure you never write “global” modifier selectors)
  • Multiple modifiers are possible. Each modifier is responsible for a property: class="alert -success -rounded -large". If you keep using these modifiers together, consider a variation (see below)
  • Since modifiers have a single responsibility, the order in HTML or CSS shouldn’t matter

.component–variation

1
<div class="button--delete"></div>
1
2
3
4
5
6
7
8
9
.button--delete {
    /* Base button classes */
    @apply .button;

    /* Variations */
    background-color: red;
    color: white;
    text-transform: uppercase;
}
  • A variation adds more than one properties at once to a class, and acts as a shorthand for multiple modifiers often used together
  • It’s used stand-alone without the need to use the base class button
  • It’s a logical case to use @apply here, so the variation can inherit the original modifiers (under consideration)
  • Even variations should be generic and reusable if possible: class="team--large" is better than class="team--management"

.helper-property

1
2
3
4
<div class="align-right"></div>
<div class="visibility-hidden"></div>
<div class="text-ellipsis"></div>
<div class="margin-top-s"></div>
  • Reusable utility classes throughout the entire project
  • Prefixed by type (= the property that will be effected)
  • Each helper class is responsible for a well-defined set of properties. It should be clear that these are not components

.js-hook

1
2
3
4
5
<div class="js-map"
     data-map-icon="url.png"
     data-map-lat="4.56"
     data-map-lon="1.23">
</div>
  • Use js-hook to initiate handlers like document.getElementsByClassName("js-hook")
  • Use data-attributes only for data storage or configuration storage
  • Has no effect on styling whatsoever

DOM structure

  • All styling is done by classes (except for HTML that is out of our control)
  • Avoid #id’s for styling
  • Make elements easily reusable, moveable in a project, or between projects
  • Avoid multiple components on 1 DOM-element. Break them up.
1
2
3
4
5
6
7
<!-- Try to avoid, news padding or margin could break the grid-->
<div class="grid__col news"></div>

<!-- More flexible, readable & moveable -->
<div class="grid__col">
    <article class="news"></article>
</div>

Tags are interchangeable since styling is done by class.

1
2
3
4
<!-- All the same -->
<div class="article"></div>
<section class="article"></div>
<article class="article"></div>

Class Visual Grouping

1
<div class="js-hook component__element -modifier helper"></div>

Visual class grouping can be done with … | …

1
<div class="js-news-click | news__item -blue -small -active | padding-top-s align-right"></div>

Code Style

We use stylelint to lint our stylesheets. Configuration is done a custom .stylelintrc which extends stylelint-config-standard.

1
2
3
4
5
6
7
8
9
10
{
  "extends": "stylelint-config-standard",
  "ignoreFiles": "resources/assets/css/vendor/*",
  "rules": {
      "indentation": [2],
      "at-rule-empty-line-before": null,
      "number-leading-zero": null,
      "selector-pseudo-element-colon-notation": "single",
    }
}

Installation

1
2
yarn add stylelint
yarn add stylelint-config-standard

Usage

Most projects have a lint script (with the –fix flag) available in their package.json.

stylelint resources/assets/css/**/**.css --fix -r

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* Comment */
.component {                      /* Indent 4 spaces, space before bracket */
    @at-rule ;                   /*  @at-rules first */

    a-property: value;            /* Props sorted */
    b-property: value;
    c-property: .45em;            /* No leading zero's */

    &:hover {                     /* Pseudo class */

    }

    &:before,                     /* Pseudo-elements */
    &:after {                     /* Each on a line */

    }

    &.-modifier {

    }

    &.-modifier2 {

    }

    /* Try to avoid */
    @apply ;                     /* Use only for variations */

    &_subclass {                  /* Unreadable and not searchable */

    }

    h1 {                          /* Avoid unless you have no control over the HTML inside the `.component` */

    }

}
                                  /* Line between classes */
.component--variation {           /* A component with few extra modifications often used together */
    @apply .component;            /* Only good use for @apply */

}

.component__element {             /* Separate class for readability, searchability instead of `&__element`*/

}

Folder/File structure

I typically use 5 folders and a main app.css file:

1
2
3
4
5
6
|-- base       : basic html elements
|-- components : single components
|-- helpers    : helper classes
|-- settings   : variables
|-- vendor     : custom files from 3rd party components like fancybox, select2 etc.
|-- app.css    : main file

app.css

Import in proper order

1
2
3
4
5
@import "settings/**/*";
@import "base/**/*";
@import "components/**/*";
@import "helpers/**/*";
@import "vendor/**/*";

Base Folder

Contains resets and sensible defaults for basic html elements.

1
2
3
4
5
6
7
|-- universal.css
|-- html.css
|-- a.css
|-- p.css
|-- heading.css (h1, h2, h3)
|-- list.css (ul, ol, dl)
|-- 

Components Folder

Stand-alone reusable components with their modifiers and variations.

1
2
3
|-- alert.css
|-- avatar.css
|-- 

Helper Folder

Stand-alone helper classes for small layout issues.

1
2
3
4
|-- align.css
|-- margin.css
|-- padding.css
|-- 

Settings Folder

Settings for colors, breakpoints, typography, etc. You can start small with one settings.css and split them up in different files if your variables grow.

1
2
3
4
|-- breakpoint.css
|-- color.css
|-- grid.css
|-- 

Vendor Folders

Imported and customized CSS from 3rd party components (this is the syntactical Wild West, you probably don’t want to lint this).

1
2
3
|-- select2.css
|-- jquery-ui.css
|-- 

This guide is inspired from various sources, thanks to those who shared their views which help me build this self guide.

Tools I Use

| Comments

“We become what we behold. We shape our tools, and thereafter our tools shape us.”
― Marshall McLuhan

Tools are very important for all professions, this article covers the main tools I use for my everyday work (I work as a frontend developer & user experience designer, so if you are in the same profession you may find this useful)

I will try to keep this post updated. (Last Updated: 18-Aug-2017)

Terminal

  • iTerm2 - my default terminal. I love those spilt pane in iterm2, try hitting Command (⌘) + d to split vertically & Command (⌘) + Shift + d split horizontally
  • oh-my-zsh - gives super power to my shell. Many useful Plugins & Themes.
  • z - remembers your frequently used directory and helps quickly cd into it, check the wiki for more details.

Code Editor / IDE

  • Visual Studio Code - recently became a fan of this open source editor from Microsoft. Love those git integrations and fast performance of the editor.
  • Atom - second favorite and minimal editor.
  • Sublime Text - super fast, Some of my favorite plugins

Browsers

Browser Extensions

  • Tab Suspender - chrome sucks a lot of memory and slows your machine, Use this extension which will kill unused open tabs and increase your RAM space
  • Tab Switcher - fast switch between open tabs. a) Press Command (⌘) + Shift + k use up & down arrow to navigate b) Click return to focus on the tab.

Will add a separate blog post about all of my extensions

Design

  • Sketch - pretty sleak and ease to use app for designing mobile, web apps.
  • Photoshop - all time favorite tool for designing mobile/web apps, graphics, branding, etc.,
  • Omnigraffile - for Functionality map preparation, User Interface Wireframing.

Check my design profile on Dribble, Behance

Productivity

  • Alfred - powerful mac spotlight alternative
  • Evernote - for quick notes taking
  • Skitch - for annotation on screenshots
  • LICEcap - quick record your screen and generate it as GIF
  • Quicktime - for screen casting, audio recording
  • Moom - quickly make any window full screen, snape to grip, move, etc.,
  • Fanstical2 - calander app with great user experience and nice look & feel

Others

  • Rocket - emoji autosuggestion on any app like slack.
  • Muzzle - automatically silence notifications while screensharing (PS. Read those funny notification messages on muzzle website)

Thanks for reading, let me know in the comments about what you use!

Remove Javascript console.log Automatically

| Comments

When writing a large JavaScript application, it is quite often that console.log and other debugging statements are left here and there. Obviously, at one point those statements need to be removed for the production version or even when the code needs to be checked in into the source repository. There are many different ways to do this, there exists a new tool called groundskeeper which can do this removal for you.

Written for Node.js, groundskeeper (GitHub: github.com/Couto/groundskeeper) is created by Luís Couto to handle logging removal by understanding the syntax tree of the code and deleting the relevant parts. It is not based on regular expression at all. Groundskeeper parses the code (via Esprima) and modify the syntax nodes (via falafel) associated with any logging. Beside a command-line tool, groundskeeper is also a library ready to be used in any other tools and build systems.

Using groundskeeper is pretty simple (as its documentation explained). Let’s assume we have the following filter-debug.js:

1
2
3
4
5
6
7
8
9
10
function filter(list, age) {
  var result = [];
  list.forEach(function (person) {
    if (person.name && person.age > age) {
      console.log('including', person.name);
      result.push(person);
    }
  });
  return result;
}

After its package is installed, running something like:

groundskeeper < filter-debug.js > filter.js

will give the following filter.js:

1
2
3
4
5
6
7
8
9
10
function filter(list, age) {
  var result = [];
  list.forEach(function (person) {
    if (person.name && person.age > age) {

      result.push(person);
    }
  });
  return result;
}

The line containing console.log is now a blank line. Because groundskeeper doesn’t delete the line, the processed output still has the same amount of lines of code as the original one.

How about debugger? We know that this statement may cause some problems with old web browsers. Fortunately groundskeeper can also get rid of any debugger statements in the code. In fact, you can even nuke any other custom application logger you might have. Here is a code fragment:

1
2
3
debugger
var list = filter(customers, 25);
RentalApp.Logger.print(JSON.stringify(list, null, 2));

which can be processed via:

groundskeeper -n RentalApp.Logger < rental.js

and give you the remaining output: var list = filter(customers, 25);

The biggest benefit of automatic removal is when it is combined with Git pre-commit hook so that no more manual step is necessary. This is pretty similar to the use of syntax validation trick. Here is an example of such a hook:

1
2
3
4
files=$(git diff-index --name-only --diff-filter=ACM HEAD | grep -P '\.js$')
for file in $files; do
  groundskeeper < $file > $file.tmp && mv $file.tmp $file
done

All touched *.js files will be processed via groundskeeper before they are checked in. This way, none of them will have stray console.log, debugger statements, or any other custom logger calls anymore. Thus, your repository is guaranteed to be free from debugging leftovers!

Avoiding Unwanted Scopes Creation in AngularJS

| Comments

One of the most appreciated features of AngularJs is scopes, at the same time scopes will give you nightmare if don’t avoid unwanted scopes from creating.

Preventing ng-model data scope is one of the main challenge you get quite often. While working with ng-model data, new unwanted scope can be created by ng-repeat or ng-if procedures. Let’s look at the below example

See the Pen Angular unwanted scope creation by Logesh Paul (@logeshpaul) on CodePen.

In the above example, the scope is created separately for innerScope and outerScope, so it does not share it’s data. We can handle this with single scope - One way to do this is we can use “Controller As” approach instead of using scope as a container for all data and functions.

One catchy solution is to keep everything in objects as shown is below example

See the Pen Angular avoiding unwanted scope creation by Logesh Paul (@logeshpaul) on CodePen.

Credits: http://www.jstips.co/en/angular/preventing-unwanted-scopes-creation-in-angularjs/

Let me what methods/best practices you follow to avoid unwanted scopes from creating in AngularJS.

Handling Browser Window Resize in ReactJS

| Comments

If you want to handle some use cases like “I want to update a div container width on browser window resize in react application” then you are in the right place.

In jQuery it’s fairly simple like below.

1
2
3
4
5
6
$( window ).resize(function() {
  var reelContainerWidth = $(window).width() - 400;

  // sets the width of reel-container to (window size - 400px)
  $("#reel-container").width(reelContainerWidth);
});

In the React world we have do it in slightly different way.

  1. Set initial width in default state
  2. Calculate viewport width each time the browser windows change size
  3. Re-render our component by passing the updated width through state

React way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var React = require("react"),
ReactDOM = require("react-dom");

var ReelContainer = React.createClass({

  getInitialState: function() {
    return {
      reelContainerWidth: 100
    };
  },

  /**
   * Calculate & Update state of new dimensions
   */
  updateDimensions() {
    const reelContainerWidth = window.innerWidth - 400;

    // sets the width of reel-container state to (window size - 400px)
    this.setState({ reelContainerWidth: reelContainerWidth });
  }

  /**
   * Add event listener
   */
  componentDidMount() {
    this.updateDimensions();
    window.addEventListener("resize", this.updateDimensions);
  }

  /**
   * Remove event listener
   */
  componentWillUnmount() {
    window.removeEventListener("resize", this.updateDimensions);
  }

  render(): React.Element {
    const reelContainerWidth = { width: this.state.reelContainerWidth };

    return(
      <div id="reel-container" style={reelContainerWidth}>
         Reel Content
      </div>
    );
  }
}

module.exports = ReelContainer;