Skip to content

Instantly share code, notes, and snippets.

@akrawchyk
Forked from jednano/writing-css.md
Created February 20, 2017 19:35
Show Gist options
  • Save akrawchyk/e66955928906517e759b72038a31fdf6 to your computer and use it in GitHub Desktop.
Save akrawchyk/e66955928906517e759b72038a31fdf6 to your computer and use it in GitHub Desktop.
Writing CSS

Writing CSS

This project uses cssnext, which allows us to use emerging CSS features at design time and transpile into currently-supported, browser-compatible CSS at runtime.

Styles can be found in app/styles. Inside this folder, you should familiarize yourself with the following structure:

styles
├── config
│   ├── fonts.json
│   ├── postcss-processors.js
│   └── vars.json
├── blocks
│   ├── block1.css
│   ├── block2.css
│   ...
├── mixins
│   ├── foo.js
│   ├── foo.spec.js
│   ...
├── sheets
│   ├── global.css
│   ├── page1.css
│   ├── page2.css
│   ...
└── images
    ├── global
    │   ├── logo.png
    │   ...
    ...

Variables

Global variables are defined in vars.json. They can be referenced like so:

.foo {
    width: var(--page-width);
}

This would output the following:

.foo {
    width: 960px;
}

Refer to the postcss-custom-properties plugin for more documentation.

Local Variables

These are defined in individual files, like so:

.foo {
    $gutter: 15px;

    &__column {
        margin-right: $gutter;
    }
}

Transpiles into:

.foo__column {
    margin-right: 15px;
}

Refer to the postcss-simple-vars plugin for more documentation.

Property Lookups

You can also reference property values without a variable:

.foo {
    color: red;
    border: 1px solid @color;
}

Transpiles into:

.foo {
    color: red;
    border: 1px solid red;
}

Refer to the postcss-property-lookup plugin for more documentation.

Sheets

Each sheet will have one or more imports that reference blocks associated with individual pages. The global sheet imports blocks that appear site-wide (e.g., header, footer) as well as normalize, reset and fonts. The sheet files define the actual CSS files that will be deployed to the site.

@import block1;
@import block2;

See the postcss-import plugin for more documentation.

Once a sheet is created you should import the global sheet along with it at the very top of your page's browser-main file, like so:

import '../styles/sheets/global';
import '../styles/sheets/page';

This enables the webpack dev server to hot-load CSS, but you still have to reference the CSS file in the head of the HTML document in order for it to load in staging/production.

<link rel="stylesheet" href="/public/css/global.css" />
<link rel="stylesheet" href="/public/css/page.css" />

You're all set!

Blocks

Blocks refer to BEM blocks and will be imported into sheets. For more information on BEM, please see MindBEMding – getting your head 'round BEM syntax.

BEM Blocks

A block represents the root element of a component. The naming convention is dash-case.

.block {}
.foo-bar {}
.foo-bar-baz {}

Each .block will have its own CSS file where only styles associated with that block are to be written. To create a block, create a new file named <block-name>.css and place it in the blocks folder with the following at the top:

/** @define <block-name> */

This marks your CSS file as a BEM block for the postcss-bem-linter plugin, which validates that the selectors used in this file adhere to the BEM naming convention for this project.

For the purposes of this document, we will use .foo-nav as an example block name. Here's how we would write the foo-nav.css file:

/** @define foo-nav */

.foo-nav {
    background-color: white;
}

BEM Elements

An element represents a descendent of a block that helps form the block as a whole. The naming convention follows this pattern:

.block__element {}

Let's add an item element to the .foo-nav block:

.foo-nav {
    &__item {
        color: black;
    }
}

BEM Modifiers

A modifier represents a different state or version of the block (or element). The naming convention follows this pattern:

.block--modifier {}
.block__element--modifier {}

The reason for double rather than single hyphens is so that your block itself can be hyphen delimited, for example:

.foo-nav {} /* Block */
.foo-nav--dark {} /* Modifier */

Let's add a dark modifier to the .foo-nav block to change, not only the background color, but the text color of each nav item:

.foo-nav {
    &--dark {
        background-color: black;
    }

    &__item {
        color: black;

        .foo-nav--dark & {
            color: white;
        }
    }
}

This will generate the following CSS:

.foo-nav--dark {
    background-color: black;
}

.foo-nav__item {
    color: black;
}

.foo-nav--dark .foo-nav__item {
    color: white;
}

Notice that the .foo-nav--dark & selector within .foo-nav__item generated .foo-nav--dark .foo-nav__item. This is the convention we use to target elements within a block modifier. See the postcss-nested plugin for more information (this is the only feature of the plugin we are using at this time).

BEM Validation

The postcss-bem-linter plugin is responsible for validating BEM syntax. If your block name is .person, it will only validate selectors like the following:

.person {}
.person__hand {}
.person--female {}
.person--female .person__hand {}
.person__hand--left {}

Drop Zones

At a high level, you may begin defining broad content areas of a page. These are what we call drop zones, defined as BEM elements. Inside these drops zones, you may place one or more BEM blocks.

Page-level Drop Zones

Drop zones define a position and, usually, container dimensions. If there were a UI tool that followed this paradign, the drop zones would be the boxes into which you may drag/drop more BEM blocks.

Drop zones are not limited to page-level BEM blocks. They can exist inside any BEM block, nested or otherwise.

The figure below illustrates the same .foo__column BEM element being used to define two separate drop zones.

Drop Zone Columns

Rules

  1. Drop zones shall not define inherited properties, as they would leak styles into BEM blocks within.
  2. BEM blocks shall not define a position. They may define their own display modes, but in order to be portable between drop zones, they should be completely unaware of their context and position.

Animations

Animations and keyframes for a specific block belong in their respective CSS block file.

Nested Properties

As a convenience, the postcss-nested-props plugin enables the following syntax:

.block {
    margin: {
        top: 5px;
        left: 10px;
    }
}

Which will transpile into the following:

.block {
    margin-top: 5px;
    margin-left: 10px;
}

Fonts

Supported fonts are defined in fonts.json. You can reference them with the font shorthand property like so:

.block {
    font: semibold 16px/calc(18/16) source-sans-pro;
}

Which transpiles into:

.block {
    font: 600 16px/1.125 'Source Sans Pro', Arial, sans-serif;
}

See postcss-font-pack for more information.

Link colors

You can add color, :visited, :focus, :hover and :active colors all in one shot like so:

.block__element {
    @link-colors all red;
}

See postcss-all-link-colors for more documentation.

Sprites

Sprite generation is automatic. All you have to do is reference the image you want and postcss-sprites will take care of the rest.

.block {
    background-image: url(../images/global/foo.png);
}

This will update image references and add the position, width and height declarations.

If this block were imported into the global.css sheet then foo.png would be added to the global.png sprite sheet.

.block {
    background-image: url(../images/sprites/global.png);
    background-position: -86px 0;
    width: 28px;
    height: 20px;
}

Centering elements

.block__centered-element {
    top: center;
    left: center;
}

See postcss-center for more documentation.

Circles

.block__circle {
    circle: 100px red;
}

See postcss-circle for more documentation.

Triangles

.block__triangle {
    triangle: pointing-down;
    width: 10px;
    height: 8px;
    background-color: red;
}

See postcss-triangle for more documentation.

Clearfix

.block__element {
    clear: fix;
}

See postcss-clearfix for more documentation.

Mixins

Global CSS mixins are represented as JavaScript functions, defined in the mixins folder. These mixins have proven themselves to be the most useful. There are only a handful of them, which means they should be easy to remember. Learn them. Use them. Try your best not to introduce another one unless the team has decided it's worthwhile. If you do write a mixin, be sure to add documentation for it in this document.

Refer to the postcss-mixins plugin for documentation.

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